Index: Lib/email/test/test_email_renamed.py =================================================================== --- Lib/email/test/test_email_renamed.py (revision 78277) +++ Lib/email/test/test_email_renamed.py (working copy) @@ -2151,12 +2151,12 @@ def test_parsedate_no_dayofweek(self): eq = self.assertEqual eq(utils.parsedate_tz('25 Feb 2003 13:47:26 -0800'), - (2003, 2, 25, 13, 47, 26, 0, 1, -1, -28800)) + (2003, 2, 25, 13, 47, 26, 1, 1, -1, -28800)) def test_parsedate_compact_no_dayofweek(self): eq = self.assertEqual eq(utils.parsedate_tz('5 Feb 2003 13:47:26 -0800'), - (2003, 2, 5, 13, 47, 26, 0, 1, -1, -28800)) + (2003, 2, 5, 13, 47, 26, 2, 1, -1, -28800)) def test_parsedate_acceptable_to_time_functions(self): eq = self.assertEqual Index: Lib/email/test/test_email.py =================================================================== --- Lib/email/test/test_email.py (revision 78277) +++ Lib/email/test/test_email.py (working copy) @@ -2165,15 +2165,22 @@ self.assertEqual(Utils.parsedate('Wed,3 Apr 2002 14:58:26 +0800'), Utils.parsedate('Wed, 3 Apr 2002 14:58:26 +0800')) + def test_parsedata_y2k(self): + """Test for parsing a date with a two-digit year.""" + self.assertEqual(Utils.parsedate_tz('25 Feb 03 13:47:26 -0800'), + Utils.parsedate_tz('25 Feb 2003 13:47:26 -0800')) + self.assertEqual(Utils.parsedate_tz('25 Feb 71 13:47:26 -0800'), + Utils.parsedate_tz('25 Feb 1971 13:47:26 -0800')) + def test_parsedate_no_dayofweek(self): eq = self.assertEqual eq(Utils.parsedate_tz('25 Feb 2003 13:47:26 -0800'), - (2003, 2, 25, 13, 47, 26, 0, 1, -1, -28800)) + (2003, 2, 25, 13, 47, 26, 1, 1, -1, -28800)) def test_parsedate_compact_no_dayofweek(self): eq = self.assertEqual eq(Utils.parsedate_tz('5 Feb 2003 13:47:26 -0800'), - (2003, 2, 5, 13, 47, 26, 0, 1, -1, -28800)) + (2003, 2, 5, 13, 47, 26, 2, 1, -1, -28800)) def test_parsedate_acceptable_to_time_functions(self): eq = self.assertEqual Index: Lib/email/_parseaddr.py =================================================================== --- Lib/email/_parseaddr.py (revision 78277) +++ Lib/email/_parseaddr.py (working copy) @@ -1,4 +1,4 @@ -# Copyright (C) 2002-2007 Python Software Foundation +# Copyright (C) 2002-2007, 2010 Python Software Foundation # Contact: email-sig@python.org """Email address parsing code. @@ -14,11 +14,19 @@ ] import time +import calendar SPACE = ' ' EMPTYSTRING = '' COMMASPACE = ', ' +TZ_UNKNOWN = -1 +DOY_UNKNOWN = 1 + +MINUTES_PER_HOUR = 60 +SECONDS_PER_MINUTE = 60 +SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR + # Parse a date field _monthnames = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec', @@ -48,14 +56,16 @@ Accounts for military timezones. """ data = data.split() + weekday = None # The FWS after the comma after the day-of-week is optional, so search and # adjust for this. if data[0].endswith(',') or data[0].lower() in _daynames: - # There's a dayname here. Skip it - del data[0] + # There's a dayname here. Get the day of the week. + weekday = _daynames.index(data.pop(0).strip(',').lower()) else: i = data[0].rfind(',') if i >= 0: + weekday = _daynames.index(data[0][:i].lower()) data[0] = data[0][i+1:] if len(data) == 3: # RFC 850 date, deprecated stuff = data[0].split('-') @@ -70,8 +80,7 @@ data.append('') # Dummy tz if len(data) < 5: return None - data = data[:5] - [dd, mm, yy, tm, tz] = data + dd, mm, yy, tm, tz = data[:5] mm = mm.lower() if mm not in _monthnames: dd, mm = mm, dd.lower() @@ -80,23 +89,20 @@ mm = _monthnames.index(mm) + 1 if mm > 12: mm -= 12 - if dd[-1] == ',': - dd = dd[:-1] + dd = dd.rstrip(',') i = yy.find(':') if i > 0: yy, tm = tm, yy - if yy[-1] == ',': - yy = yy[:-1] + yy.rstrip(',') if not yy[0].isdigit(): yy, tz = tz, yy - if tm[-1] == ',': - tm = tm[:-1] + tm = tm.rstrip(',') tm = tm.split(':') if len(tm) == 2: - [thh, tmm] = tm - tss = '0' + thh, tmm = tm + tss = 0 elif len(tm) == 3: - [thh, tmm, tss] = tm + thh, tmm, tss = tm else: return None try: @@ -107,6 +113,16 @@ tss = int(tss) except ValueError: return None + # Check for a yy specified in two-digit format, then convert it to the + # appropriate four-digit format, according to the POSIX standard. RFC 822 + # calls for a two-digit yy, but RFC 2822 (which obsoletes RFC 822) + # mandates a 4-digit yy. For more information, see the documentation for + # the time module. + if yy < 100: + if yy > 68: + yy += 1900 + else: + yy += 2000 tzoffset = None tz = tz.upper() if tz in _timezones: @@ -116,16 +132,20 @@ tzoffset = int(tz) except ValueError: pass - # Convert a timezone offset into seconds ; -0500 -> -18000 - if tzoffset: + # Convert a timezone offset into seconds. For example, -0500 to -18000. + if tzoffset is not None: if tzoffset < 0: tzsign = -1 tzoffset = -tzoffset else: tzsign = 1 - tzoffset = tzsign * ( (tzoffset//100)*3600 + (tzoffset % 100)*60) - # Daylight Saving Time flag is set to -1, since DST is unknown. - return yy, mm, dd, thh, tmm, tss, 0, 1, -1, tzoffset + tzoffset = tzsign * ((tzoffset // 100) * SECONDS_PER_HOUR + \ + (tzoffset % 100) * SECONDS_PER_MINUTE) + # Daylight Saving Time and day of year are unknown. + if weekday is None: + weekday = calendar.weekday(yy, mm, dd) + return yy, mm, dd, thh, tmm, tss, \ + weekday, DOY_UNKNOWN, TZ_UNKNOWN, tzoffset def parsedate(data):