Is there any way to use a strftime-like function for dates before 1900 in Python?
Asked Answered
B

5

20

I didn't realize this, but apparently Python's strftime function doesn't support dates before 1900:

>>> from datetime import datetime
>>> d = datetime(1899, 1, 1)
>>> d.strftime('%Y-%m-%d')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: year=1899 is before 1900; the datetime strftime() methods require year >= 1900

I'm sure I could hack together something myself to do this, but I figure the strftime function is there for a reason (and there also is a reason why it can't support pre-1900 dates). I need to be able to support dates before 1900. I'd just use str, but there's too much variation. In other words, it may or may not have microseconds or it may or may not have a timezone. Is there any solution to this?

If it makes a difference, I'm doing this so that I can write the data to a text file and load it into a database using Oracle SQL*Loader.

I essentially ended up doing Alex Martelli's answer. Here's a more complete implementation:

>>> from datetime import datetime
>>> d = datetime.now()
>>> d = d.replace(microsecond=0, tzinfo=None)
>>> str(d)
'2009-10-29 11:27:27'

The only difference is that str(d) is equivalent to d.isoformat(' ').

Biarritz answered 29/10, 2009 at 14:3 Comment(0)
Z
15

isoformat works on datetime instances w/o limitation of range:

>>> import datetime
>>> x=datetime.datetime(1865, 7, 2, 9, 30, 21)
>>> x.isoformat()
'1865-07-02T09:30:21'

If you need a different-format string it's not too hard to slice, dice and remix pieces of the string you get from isoformat, which is very consistent (YYYY-MM-DDTHH:MM:SS.mmmmmm, with the dot and following microseconds omitted if microseconds are zero).

Zurn answered 29/10, 2009 at 15:7 Comment(1)
This is basically what I ended up doing.Biarritz
H
11

The documentation seems pretty clear about this:

The exact range of years for which strftime() works also varies across platforms. Regardless of platform, years before 1900 cannot be used.

So there isn't going to be a solution that uses strftime(). Luckily, it's pretty straightforward to do this "by hand":

>>> "%02d-%02d-%02d %02d:%02d" % (d.year,d.month,d.day,d.hour,d.minute)
'1899-01-01 00:00'
Haywood answered 29/10, 2009 at 14:12 Comment(1)
Have posted so sample code showing why you don't really need strftime().Haywood
C
4

mxDateTime can handle arbitrary dates. Python's time and datetime modules use UNIX timestamps internally, that's why they have limited range.

In [5]: mx.DateTime.DateTime(1899)
Out[5]: <mx.DateTime.DateTime object for '1899-01-01 00:00:00.00' at 154a960>

In [6]: DateTime.DateTime(1899).Format('%Y-%m-%d')
Out[6]: 1899-01-01
Cenis answered 29/10, 2009 at 14:12 Comment(1)
Urgh... Just when I thought I was able to remove the mx* things as a dependency when we're not using mxODBC. :-(Biarritz
T
3

This is from the matplotlib source. Could provide a good starting point for rolling your own.

def strftime(self, dt, fmt):
    fmt = self.illegal_s.sub(r"\1", fmt)
    fmt = fmt.replace("%s", "s")
    if dt.year > 1900:
        return cbook.unicode_safe(dt.strftime(fmt))

    year = dt.year
    # For every non-leap year century, advance by
    # 6 years to get into the 28-year repeat cycle
    delta = 2000 - year
    off = 6*(delta // 100 + delta // 400)
    year = year + off

    # Move to around the year 2000
    year = year + ((2000 - year)//28)*28
    timetuple = dt.timetuple()
    s1 = time.strftime(fmt, (year,) + timetuple[1:])
    sites1 = self._findall(s1, str(year))

    s2 = time.strftime(fmt, (year+28,) + timetuple[1:])
    sites2 = self._findall(s2, str(year+28))

    sites = []
    for site in sites1:
        if site in sites2:
        sites.append(site)

    s = s1
    syear = "%4d" % (dt.year,)
    for site in sites:
        s = s[:site] + syear + s[site+4:]

    return cbook.unicode_safe(s)
Tedi answered 29/10, 2009 at 14:25 Comment(2)
Ahh,derived from my contribution to matplotlib. sniff :)Westleigh
@dalke, that's awesome. Quite a small world here on Stack Overflow.Tedi
V
2

This is the "feature" of the ctime library (UTF). Also You may have problem above 2038.

Vaudevillian answered 29/10, 2009 at 14:10 Comment(2)
I see. So the issue is that the date can't be outside plus or minus 32 bits?Biarritz
Exactly, no straight forward solution.Vaudevillian

© 2022 - 2024 — McMap. All rights reserved.