By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
434,849 Members | 2,361 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 434,849 IT Pros & Developers. It's quick & easy.

datetime strftime methods require year >= 1900

P: n/a
from datetime import date
dt = date(1005,1,1)
print dt.strftime('%Y')

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
Jul 18 '05 #1
Share this Question
Share on Google+
4 Replies


P: n/a
John Hunter wrote:
from datetime import date
dt = date(1005,1,1)
print dt.strftime('%Y')
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?
print dt.year

1005

HTH
Anna
Jul 18 '05 #2

P: n/a
>>>>> "Anna" == Anna Martelli Ravenscroft <an**@aleax.it> writes:
Anna> Why would you need to use strftime, if you've already got a
Anna> year?
print dt.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
Jul 18 '05 #3

P: n/a
John Hunter wrote:
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.
time.accept2dyear 1 time.strftime("%Y", datetime.date(20, 1, 1).timetuple()) '2020' time.accept2dyear = 0
time.strftime("%Y", datetime.date(20, 1, 1).timetuple()) Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: year >= 1900 required
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[i]] = 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()

strftime(datetime.date(1850, 8, 2), "%Y/%M/%d was a %A") '1850/00/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

Andrew
da***@dalkescientific.com
Jul 18 '05 #4

P: n/a
Andrew Dalke wrote:
>>> strftime(datetime.date(1850, 8, 2), "%Y/%M/%d was a %A")
'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
da***@dalkescientific.com

Jul 18 '05 #5

This discussion thread is closed

Replies have been disabled for this discussion.