I’m setting out to build an app with Python that will need to handle BC dates extensively (store and retrieve in DB, do calculations). Most dates will be of various uncertainties, like “around 2000BC”.

I know Python’s datetime library only handles dates from 1 AD.

So far I only found FlexiDate. Are there any other options?

EDIT: The best approach would probably be to store them as strings (have String as the basic data type) and -as suggested- have a custom datetime class which can make some numerical sense of it. For the majority it looks like dates will only consist of a year. There are some interesting problems to solve like “early 500BC”, “between 1600BC and 1500BC”, “before 1800BC”.

Astronomers and aerospace engineers have to deal with BC dates and a continuous time line, so that’s the google context for your search.

Astropy‘s Time class will work for you (and even more precisely and completely than you hoped). pip install astropy and you’re on your way.

If you roll your own, you should review some of the formulas in Vallado’s chapter on dates. There are lots of obscure fudge factors required to convert dates from Julian to Gregorian etc.

Its an interesting question, it seems odd that such a class does not exist yet (re @joel Cornett comment) If you only work in years only it would simplify your class to handling integers rather than calendar dates – you could possibly use a dictionary with the text description (10 BC) against and integer value (-10)
EDIT: I googled this:


NASA Spice functions handle BC extremely well with conversions from multiple formats. In these examples begin_date and end_date contain the TDB seconds past the J2000 epoch corresponding to input dates:

import spiceypy as spice

# load a leap second kernel

begin_date = spice.str2et('13201 B.C. 05-06 00:00')
end_date = spice.str2et('17191 A.D. 03-15 00:00')  

Documentation of str2et(),
Input format documentation, as well as
Leapsecond kernel files are available via the NASA Spice homepage.

converting from datetime or other time methods to spice is simple:

if indate.year < 0.0:
    spice_indate = str(indate.year) + ' B.C. ' + sindate[-17:]
    spice_indate = str(spice_indate)[1:]
    spice_indate = str(indate.year) + ' A.D. ' + sindate[-17:]

'2018 B.C. 03-31 19:33:38.44'

Other functions include: TIMOUT, TPARSE both converting to and from J2000 epoch seconds.

These functions are available in python through spiceypy, install e.g. via pip3 install spiceypy

This is an old question, but I had the same one and found this article announcing datautil, which is designed to handle dates like:

  • Dates in distant past and future including BC/BCE dates
  • Dates in a wild variety of formats: Jan 1890, January 1890, 1st Dec 1890, Spring 1890 etc
  • Dates of varying precision: e.g. 1890, 1890-01 (i.e. Jan 1890), 1890-01-02
  • Imprecise dates: c1890, 1890?, fl 1890 etc

Install is just

pip install datautil

I explored it for only a few minutesso far, but have noted that it doesn’t accept str as an argument (only unicode) and it implements its own date class (Flexidate, ‘a slightly extended version of ISO8601’), which is sort of useful maybe.

>>> from datautil.date import parse
>>> parse('Jan 1890')

error: 'str' object has no attribute 'read'

>>> fd = parse(u'Jan 1890')
<class 'datautil.date.FlexiDate'> 1890-01

>>> datetime.datetime(1890, 1, 1, 0, 0)

>>> bc = parse(u'2000BC')
<class 'datautil.date.FlexiDate'> -2000

but alas…

>>> bc.as_datetime()
ValueError: year is out of range

Unfortunately for me, I was looking for something that could handle dates with “circa” (c., ca, ca., circ. or cca.)

>>> ca = parse(u'ca 1900')
<class 'datautil.date.FlexiDate'>  [UNPARSED: ca 1900]

Oh well – I guess I can always send a pull request 😉