datetime strftime methods require year >= 1900

J

John Hunter

Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: year=1005 is before 1900; the datetime strftime() methods require year >= 1900

Does anyone know of a datetime string formatter that can handles
strftime format strings over the full range that datetime objects
support?

Thanks,
John Hunter
 
A

Anna Martelli Ravenscroft

John said:
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: year=1005 is before 1900; the datetime strftime() methods require year >= 1900

Does anyone know of a datetime string formatter that can handles
strftime format strings over the full range that datetime objects
support?

Thanks,
John Hunter

Why would you need to use strftime, if you've already got a year?1005

HTH
Anna
 
J

John Hunter

Anna> Why would you need to use strftime, if you've already got a
Anna> year? Anna> 1005

That was just a simple-minded example. I need the ability to handle
general format strings over the range of datetime dates.

JDH
 
A

Andrew Dalke

John said:
Does anyone know of a datetime string formatter that can handles
strftime format strings over the full range that datetime objects
support?

Here's what the Python source says

/* Give up if the year is before 1900.
* Python strftime() plays games with the year, and different
* games depending on whether envar PYTHON2K is set. This makes
* years before 1900 a nightmare, even if the platform strftime
* supports them (and not all do).
* We could get a lot farther here by avoiding Python's strftime
* wrapper and calling the C strftime() directly, but that isn't
* an option in the Python implementation of this module.
*/

The underlying time.strftime module supports special
behaviour for dates < 1900.
Traceback (most recent call last):

One concern about your request is, what a date mean
when you get before 1900? I assume you want the proleptic
Gregorian calendar, that is, to apply it even when and
where it wasn't in use.

One way to fake it is to move the date to a date in the
supported time range which starts on the same day, then
use strftime on that new date.

It's not enough to find the fake year number in the
resulting string and convert it into the real year
number. After all, the format string might be
"1980 %Y" and if the %Y expands to 1980 in your shifted
time frame then you don't know which to change.

To figure that out, move the date forward by 28 years
(which is the repeat cycle except for the non-leap
centuries) and do it again. The parts of the two
strings that differ indicate where to put the change.

I tried to write this function but I wasn't sure
how to handle the non-leap year centuries. It seems
to be that those are the same as 6 years later, so
that Jan. 1900's calendar looks like 1906's.

Here's what I came up with. Seems to work.


# Format a datetime.date using the proleptic Gregorian calendar

import time, datetime

def _findall(text, substr):
# Also finds overlaps
sites = []
i = 0
while 1:
j = text.find(substr, i)
if j == -1:
break
sites.append(j)
i=j+1
return sites

# I hope I did this math right. Every 28 years the
# calendar repeats, except through century leap years
# excepting the 400 year leap years. But only if
# you're using the Gregorian calendar.

def strftime(dt, fmt):
# WARNING: known bug with "%s", which is the number
# of seconds since the epoch. This is too harsh
# of a check. It should allow "%%s".
fmt = fmt.replace("%s", "s")
if dt.year > 1900:
return time.strftime(fmt, dt.timetuple())

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 = _findall(s1, str(year))

s2 = time.strftime(fmt, (year+28,) + timetuple[1:])
sites2 = _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 s

# Make sure that the day names are in order
# from 1/1/1 until August 2000
def test():
s = strftime(datetime.date(1800, 9, 23),
"%Y has the same days as 1980 and 2008")
if s != "1800 has the same days as 1980 and 2008":
raise AssertionError(s)

print "Testing all day names from 0001/01/01 until 2000/08/01"
days = []
for i in range(1, 10):
days.append(datetime.date(2000, 1, i).strftime("%A"))
nextday = {}
for i in range(8):
nextday[days] = days[i+1]

startdate = datetime.date(1, 1, 1)
enddate = datetime.date(2000, 8, 1)
prevday = strftime(startdate, "%A")
one_day = datetime.timedelta(1)

testdate = startdate + one_day
while testdate < enddate:
if (testdate.day == 1 and testdate.month == 1 and
(testdate.year % 100 == 0)):
print testdate.year
day = strftime(testdate, "%A")
if nextday[prevday] != day:
raise AssertionError(str(testdate))
prevday = day
testdate = testdate + one_day

if __name__ == "__main__":
test()


% cal 8 1850
August 1850
S M Tu W Th F S
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31

Andrew
(e-mail address removed)
 
A

Andrew Dalke

Andrew said:
'1850/00/02 was a Friday'

While "%Y/%m/%d was a %A" gives

1850/08/02 was a Friday
% cal 8 1850
August 1850
S M Tu W Th F S
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31

I once played around with an alternate time
format string that allowed

%(2000)/%(12)/%(31) to mean %Y, %m, %d.
%(Jan)/%(January)/%(Mon)/%(Monday) to mean %b/%B/%a/%A

because I forget which name is which.

Andrew
(e-mail address removed)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top