35

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".

Chris Wesseling
  • 6,226
  • 2
  • 36
  • 72
Roger
  • 4,737
  • 4
  • 43
  • 68
  • 11
    The vague nature of your dates might merit rolling your own datetime class. – Joel Cornett Apr 07 '13 at 01:28
  • @JoelCornett Yea, but uncertainties in real values are just real values themselves. So any [module or data structure](http://stackoverflow.com/a/19869864/623735) that can do datetime's well (handles BC and timedeltas) should be used for Roger's data. If Roger's vagueness can only be defined in natural language terms, since he needs to quantify that vagueness somewhere in his app (otherwise he'd be recording his quantities as strings), then he would need sentiment analysis (natural language processing). – hobs Nov 08 '13 at 22:51

4 Answers4

12

Astronomers and aerospace engineers have to deal with BC dates and a continuous timeline, so that's the context words you want to put into your duck.com/you.com/neeva/searX web 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.

hobs
  • 18,473
  • 10
  • 83
  • 106
  • Thanks. Interesting library. However, the precision of the dates in my app are not determined by the way the code handles or calculates dates but by the historic accuracy of the date. For example the accuracy of "about 10.000BC" cannot be made more accurate by the underlying algorithm but by an (inherently) subjective interpretation of the word "about". Another example is what I would call "referring dates" like "during the rise of the Roman Empire", a dating that refers to a more or less known time frame. It's a difficult subject. – Roger Nov 09 '13 at 14:14
  • 1
    Yea, your natural language processing problem can be separated from your data structure problem. NLP can quantify the word "about" or "during", or just define it in your custom language (like google advanced search does, with phrases like "2 days ago", I think). But your data structure precision needs to be as good as the *best* input data precision as well as encompass the range you need (BC and AD dates). `dateutil.parser` will help with your NLP problem, if you monkey-patched it to deal with your custom vocabulary ('about', 'during', 'BC') and use `astropy.Time` instead of `datetime`. – hobs Nov 10 '13 at 20:33
5

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
spicey.furnsh("path/to/leap/second/kernel/naif0012.tls")

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:]
else: 
    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

vlz
  • 911
  • 1
  • 10
  • 18
creeser
  • 363
  • 1
  • 4
  • 11
4

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:

http://code.activestate.com/lists/python-list/623672/

  • 2
    I accepted your answer as there's very little available on this topic except for the link you provided and the library I already found. I'm gearing up to roll my own implementation... – Roger Apr 15 '13 at 11:40
  • 1
    Can you please re-validate the link? It seems to not exist anymore. – Kube Kubow Mar 16 '20 at 14:51
2

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

fd.as_datetime()
>>> 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 ;-)

Peter Hanley
  • 1,254
  • 1
  • 11
  • 19