This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author gregory.p.smith
Recipients gregory.p.smith
Date 2008-03-03.21:41:24
SpamBayes Score 0.00048898684
Marked as misclassified No
Message-id <1204580486.17.0.196786227347.issue2227@psf.upfronthosting.co.za>
In-reply-to
Content
Some common python utilities had problems on Feb 29 this year when
parsing dates using format strings that did not include a year in them.

>>> time.strptime('Feb 29', '%b %d')
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "/usr/lib/python2.4/_strptime.py", line 425, in strptime
    julian = datetime_date(year, month, day).toordinal() - \
ValueError: day is out of range for month

This is apparently because python assumes the year is 1900 unless it
explicitly parses another year out of the string.

Applications can work around this by always adding a year and a %Y to
the string they are parsing.

But not all date manipulating applications care about years.  In this
case the application was fail2ban, bug report and patches to it here:

 http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=468382

Should the year default to 1900 (the equivalent of what the much more
forgiving C API does by leaving struct tm tm_year = 0) or should this
error be raised?  If the answer is yes, works as is this is easy and
just turns into us adding a note in the documentation to mention the
behavior.

I do believe this was a valid bug in fail2ban as assuming the current
year for date parsing is a bad idea and will do the wrong thing when
parsing across a year change.

Python's strptime is much more strict than C strptime (glibc's C
strptime is happy to return tm_mon 2 tm_mday 31.  Its range checking is
minimal.

here's a C test case to play with its behavior:

#include <assert.h>
#include <stdio.h>
#include <time.h>
int main(int argc, char *argv[]) {
  unsigned long ret, parsed;
  assert(argc == 2);
  struct tm tm = { 0 };
  ret = strptime(argv[1], "%b %d", &tm);
  parsed = ret - (unsigned long)(argv[1]);
  printf("ret 0x%x  parsed %d  tm_mon %d  tm_mday %d  tm_year %d\n",
         ret, parsed,
         tm.tm_mon, tm.tm_mday, tm.tm_year);
}

% ./foo 'Feb 28'
ret 0xffffda8a  parsed 6  tm_mon 1  tm_mday 28  tm_year 0
% ./foo 'Feb 29'
ret 0xffffda8a  parsed 6  tm_mon 1  tm_mday 29  tm_year 0
% ./foo 'Feb 31'
ret 0xffffda8a  parsed 6  tm_mon 1  tm_mday 31  tm_year 0
% ./foo 'Feb 32'
ret 0x0  parsed 9596  tm_mon 1  tm_mday 0  tm_year 0
History
Date User Action Args
2008-03-03 21:41:26gregory.p.smithsetspambayes_score: 0.000488987 -> 0.00048898684
recipients: + gregory.p.smith
2008-03-03 21:41:26gregory.p.smithsetspambayes_score: 0.000488987 -> 0.000488987
messageid: <1204580486.17.0.196786227347.issue2227@psf.upfronthosting.co.za>
2008-03-03 21:41:25gregory.p.smithlinkissue2227 messages
2008-03-03 21:41:24gregory.p.smithcreate