Issue #6641: The datetime.strptime method now supports the %z directive.

This commit is contained in:
Alexander Belopolsky 2010-06-17 18:30:34 +00:00
parent f4112e2653
commit ca94f55758
5 changed files with 101 additions and 74 deletions

View file

@ -16,7 +16,10 @@ import calendar
from re import compile as re_compile
from re import IGNORECASE, ASCII
from re import escape as re_escape
from datetime import date as datetime_date
from datetime import (date as datetime_date,
datetime as datetime_datetime,
timedelta as datetime_timedelta,
timezone as datetime_timezone)
try:
from _thread import allocate_lock as _thread_allocate_lock
except:
@ -204,6 +207,7 @@ class TimeRE(dict):
#XXX: Does 'Y' need to worry about having less or more than
# 4 digits?
'Y': r"(?P<Y>\d\d\d\d)",
'z': r"(?P<z>[+-]\d\d[0-5]\d)",
'A': self.__seqToRE(self.locale_time.f_weekday, 'A'),
'a': self.__seqToRE(self.locale_time.a_weekday, 'a'),
'B': self.__seqToRE(self.locale_time.f_month[1:], 'B'),
@ -293,7 +297,9 @@ def _calc_julian_from_U_or_W(year, week_of_year, day_of_week, week_starts_Mon):
def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
"""Return a time struct based on the input string and the format string."""
"""Return a 2-tuple consisting of a time struct and an int containg
the number of microseconds based on the input string and the
format string."""
for index, arg in enumerate([data_string, format]):
if not isinstance(arg, str):
@ -333,10 +339,12 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
if len(data_string) != found.end():
raise ValueError("unconverted data remains: %s" %
data_string[found.end():])
year = 1900
month = day = 1
hour = minute = second = fraction = 0
tz = -1
tzoffset = None
# Default to -1 to signify that values not known; not critical to have,
# though
week_of_year = -1
@ -417,6 +425,11 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
else:
# W starts week on Monday.
week_of_year_start = 0
elif group_key == 'z':
z = found_dict['z']
tzoffset = int(z[1:3]) * 60 + int(z[3:5])
if z.startswith("-"):
tzoffset = -tzoffset
elif group_key == 'Z':
# Since -1 is default value only need to worry about setting tz if
# it can be something other than -1.
@ -453,9 +466,35 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
day = datetime_result.day
if weekday == -1:
weekday = datetime_date(year, month, day).weekday()
return (time.struct_time((year, month, day,
hour, minute, second,
weekday, julian, tz)), fraction)
# Add timezone info
tzname = found_dict.get("Z")
if tzoffset is not None:
gmtoff = tzoffset * 60
else:
gmtoff = None
return (year, month, day,
hour, minute, second,
weekday, julian, tz, gmtoff, tzname), fraction
def _strptime_time(data_string, format="%a %b %d %H:%M:%S %Y"):
return _strptime(data_string, format)[0]
"""Return a time struct based on the input string and the
format string."""
tt = _strptime(data_string, format)[0]
return time.struct_time(tt[:9])
def _strptime_datetime(data_string, format="%a %b %d %H:%M:%S %Y"):
"""Return a datetime instace based on the input string and the
format string."""
tt, fraction = _strptime(data_string, format)
gmtoff, tzname = tt[-2:]
args = tt[:6] + (fraction,)
if gmtoff is not None:
tzdelta = datetime_timedelta(seconds=gmtoff)
if tzname:
tz = datetime_timezone(tzdelta, tzname)
else:
tz = datetime_timezone(tzdelta)
args += (tz,)
return datetime_datetime(*args)

View file

@ -17,6 +17,7 @@ from datetime import tzinfo
from datetime import time
from datetime import timezone
from datetime import date, datetime
import time as _time
pickle_choices = [(pickle, pickle, proto) for proto in range(3)]
assert len(pickle_choices) == 3
@ -1731,11 +1732,41 @@ class TestDateTime(TestDate):
string = '2004-12-01 13:02:47.197'
format = '%Y-%m-%d %H:%M:%S.%f'
result, frac = _strptime._strptime(string, format)
expected = self.theclass(*(result[0:6]+(frac,)))
expected = _strptime._strptime_datetime(string, format)
got = self.theclass.strptime(string, format)
self.assertEqual(expected, got)
strptime = self.theclass.strptime
self.assertEqual(strptime("+0002", "%z").utcoffset(), 2 * MINUTE)
self.assertEqual(strptime("-0002", "%z").utcoffset(), -2 * MINUTE)
# Only local timezone and UTC are supported
for tzseconds, tzname in ((0, 'UTC'), (0, 'GMT'),
(-_time.timezone, _time.tzname[0])):
if tzseconds < 0:
sign = '-'
seconds = -tzseconds
else:
sign ='+'
seconds = tzseconds
hours, minutes = divmod(seconds//60, 60)
dtstr = "{}{:02d}{:02d} {}".format(sign, hours, minutes, tzname)
dt = strptime(dtstr, "%z %Z")
self.assertEqual(dt.utcoffset(), timedelta(seconds=tzseconds))
self.assertEqual(dt.tzname(), tzname)
# Can produce inconsistent datetime
dtstr, fmt = "+1234 UTC", "%z %Z"
dt = strptime(dtstr, fmt)
self.assertEqual(dt.utcoffset(), 12 * HOUR + 34 * MINUTE)
self.assertEqual(dt.tzname(), 'UTC')
# yet will roundtrip
self.assertEqual(dt.strftime(fmt), dtstr)
# Produce naive datetime if no %z is provided
self.assertEqual(strptime("UTC", "%Z").tzinfo, None)
with self.assertRaises(ValueError): strptime("-2400", "%z")
with self.assertRaises(ValueError): strptime("-000", "%z")
def test_more_timetuple(self):
# This tests fields beyond those tested by the TestDate.test_timetuple.
t = self.theclass(2004, 12, 31, 6, 22, 33)
@ -3196,6 +3227,7 @@ def first_sunday_on_or_after(dt):
return dt
ZERO = timedelta(0)
MINUTE = timedelta(minutes=1)
HOUR = timedelta(hours=1)
DAY = timedelta(days=1)
# In the US, DST starts at 2am (standard time) on the first Sunday in April.