SF patch 670012: Compatibility changes for _strptime.py.

Patch from Brett Cannon:

    First, the 'y' directive now handles [00, 68] as a suffix for the
    21st century while [69, 99] is treated as the suffix for the 20th
    century (this is for Open Group compatibility).

    strptime now returns default values that make it a valid date ...

    the ability to pass in a regex object to use instead of a format
    string (and the inverse ability to have strptime return a regex object)
    has been removed. This is in preparation for a future patch that will
    add some caching internally to get a speed boost.
This commit is contained in:
Tim Peters 2003-01-18 03:53:49 +00:00
parent e58962af4d
commit 08e54270f2
2 changed files with 127 additions and 138 deletions

View File

@ -399,101 +399,99 @@ class TimeRE(dict):
def strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
"""Return a time struct based on the input data and the format string.
The format argument may either be a regular expression object compiled by
strptime(), or a format string. If False is passed in for data_string
then the re object calculated for format will be returned. The re object
must be used with the same locale as was used to compile the re object.
"""
"""Return a time struct based on the input data and the format string."""
locale_time = LocaleTime()
if isinstance(format, RegexpType):
if format.pattern.find(locale_time.lang) == -1:
raise TypeError("re object not created with same language as "
"LocaleTime instance")
else:
compiled_re = format
else:
compiled_re = TimeRE(locale_time).compile(format)
if data_string is False:
return compiled_re
else:
found = compiled_re.match(data_string)
if not found:
raise ValueError("time data did not match format")
year = month = day = hour = minute = second = weekday = julian = tz =-1
found_dict = found.groupdict()
for group_key in found_dict.iterkeys():
if group_key == 'y':
year = int("%s%s" %
(time.strftime("%Y")[:-2], found_dict['y']))
elif group_key == 'Y':
year = int(found_dict['Y'])
elif group_key == 'm':
month = int(found_dict['m'])
elif group_key == 'B':
month = _insensitiveindex(locale_time.f_month, found_dict['B'])
elif group_key == 'b':
month = _insensitiveindex(locale_time.a_month, found_dict['b'])
elif group_key == 'd':
day = int(found_dict['d'])
elif group_key is 'H':
hour = int(found_dict['H'])
elif group_key == 'I':
hour = int(found_dict['I'])
ampm = found_dict.get('p', '').lower()
# If there was no AM/PM indicator, we'll treat this like AM
if ampm in ('', locale_time.am_pm[0].lower()):
# We're in AM so the hour is correct unless we're
# looking at 12 midnight.
# 12 midnight == 12 AM == hour 0
if hour == 12:
hour = 0
elif ampm == locale_time.am_pm[1].lower():
# We're in PM so we need to add 12 to the hour unless
# we're looking at 12 noon.
# 12 noon == 12 PM == hour 12
if hour != 12:
hour += 12
elif group_key == 'M':
minute = int(found_dict['M'])
elif group_key == 'S':
second = int(found_dict['S'])
elif group_key == 'A':
weekday = _insensitiveindex(locale_time.f_weekday,
found_dict['A'])
elif group_key == 'a':
weekday = _insensitiveindex(locale_time.a_weekday,
found_dict['a'])
elif group_key == 'w':
weekday = int(found_dict['w'])
if weekday == 0:
weekday = 6
else:
weekday -= 1
elif group_key == 'j':
julian = int(found_dict['j'])
elif group_key == 'Z':
found_zone = found_dict['Z'].lower()
if locale_time.timezone[0] == locale_time.timezone[1]:
pass #Deals with bad locale setup where timezone info is
# the same; first found on FreeBSD 4.4.
elif locale_time.timezone[0].lower() == found_zone:
tz = 0
elif locale_time.timezone[1].lower() == found_zone:
tz = 1
elif locale_time.timezone[2].lower() == found_zone:
tz = 0
#XXX <bc>: If calculating fxns are never exposed to the general
# populous then just inline calculations.
if julian == -1 and year != -1 and month != -1 and day != -1:
compiled_re = TimeRE(locale_time).compile(format)
found = compiled_re.match(data_string)
if not found:
raise ValueError("time data did not match format")
year = 1900
month = day = 1
hour = minute = second = 0
tz = -1
# Defaulted to -1 so as to signal using functions to calc values
weekday = julian = -1
found_dict = found.groupdict()
for group_key in found_dict.iterkeys():
if group_key == 'y':
year = int(found_dict['y'])
# Open Group specification for strptime() states that a %y
#value in the range of [00, 68] is in the century 2000, while
#[69,99] is in the century 1900
if year <= 68:
year += 2000
else:
year += 1900
elif group_key == 'Y':
year = int(found_dict['Y'])
elif group_key == 'm':
month = int(found_dict['m'])
elif group_key == 'B':
month = _insensitiveindex(locale_time.f_month, found_dict['B'])
elif group_key == 'b':
month = _insensitiveindex(locale_time.a_month, found_dict['b'])
elif group_key == 'd':
day = int(found_dict['d'])
elif group_key is 'H':
hour = int(found_dict['H'])
elif group_key == 'I':
hour = int(found_dict['I'])
ampm = found_dict.get('p', '').lower()
# If there was no AM/PM indicator, we'll treat this like AM
if ampm in ('', locale_time.am_pm[0].lower()):
# We're in AM so the hour is correct unless we're
# looking at 12 midnight.
# 12 midnight == 12 AM == hour 0
if hour == 12:
hour = 0
elif ampm == locale_time.am_pm[1].lower():
# We're in PM so we need to add 12 to the hour unless
# we're looking at 12 noon.
# 12 noon == 12 PM == hour 12
if hour != 12:
hour += 12
elif group_key == 'M':
minute = int(found_dict['M'])
elif group_key == 'S':
second = int(found_dict['S'])
elif group_key == 'A':
weekday = _insensitiveindex(locale_time.f_weekday,
found_dict['A'])
elif group_key == 'a':
weekday = _insensitiveindex(locale_time.a_weekday,
found_dict['a'])
elif group_key == 'w':
weekday = int(found_dict['w'])
if weekday == 0:
weekday = 6
else:
weekday -= 1
elif group_key == 'j':
julian = int(found_dict['j'])
elif group_key == 'Z':
found_zone = found_dict['Z'].lower()
if locale_time.timezone[0] == locale_time.timezone[1]:
pass #Deals with bad locale setup where timezone info is
# the same; first found on FreeBSD 4.4.
elif locale_time.timezone[0].lower() == found_zone:
tz = 0
elif locale_time.timezone[1].lower() == found_zone:
tz = 1
elif locale_time.timezone[2].lower() == found_zone:
tz = -1
#XXX <bc>: If calculating fxns are never exposed to the general
#populous then just inline calculations. Also might be able to use
#``datetime`` and the methods it provides.
if julian == -1:
julian = julianday(year, month, day)
if (month == -1 or day == -1) and julian != -1 and year != -1:
else: # Assuming that if they bothered to include Julian day it will
#be accurate
year, month, day = gregorian(julian, year)
if weekday == -1 and year != -1 and month != -1 and day != -1:
if weekday == -1:
weekday = dayofweek(year, month, day)
return time.struct_time(
(year,month,day,hour,minute,second,weekday, julian,tz))
return time.struct_time((year, month, day,
hour, minute, second,
weekday, julian, tz))
def _insensitiveindex(lst, findme):
# Perform a case-insensitive index search.

View File

@ -124,6 +124,14 @@ class LocaleTime_Tests(unittest.TestCase):
self.assertRaises(TypeError, _strptime.LocaleTime, timezone=range(1))
self.assertRaises(TypeError, _strptime.LocaleTime, timezone=range(3))
def test_unknowntimezone(self):
# Handle timezone set to ('','') properly.
# Fixes bug #661354
locale_time = _strptime.LocaleTime(timezone=('',''))
self.failUnless("%Z" not in locale_time.LC_date,
"when timezone == ('',''), string.replace('','%Z') is "
"occuring")
class TimeRETests(unittest.TestCase):
"""Tests for TimeRE."""
@ -180,12 +188,19 @@ class TimeRETests(unittest.TestCase):
found.group('b')))
for directive in ('a','A','b','B','c','d','H','I','j','m','M','p','S',
'U','w','W','x','X','y','Y','Z','%'):
compiled = self.time_re.compile("%%%s"% directive)
found = compiled.match(time.strftime("%%%s" % directive))
compiled = self.time_re.compile("%" + directive)
found = compiled.match(time.strftime("%" + directive))
self.failUnless(found, "Matching failed on '%s' using '%s' regex" %
(time.strftime("%%%s" % directive),
(time.strftime("%" + directive),
compiled.pattern))
def test_blankpattern(self):
# Make sure when tuple or something has no values no regex is generated.
# Fixes bug #661354
test_locale = _strptime.LocaleTime(timezone=('',''))
self.failUnless(_strptime.TimeRE(test_locale).pattern("%Z") == '',
"with timezone == ('',''), TimeRE().pattern('%Z') != ''")
class StrptimeTests(unittest.TestCase):
"""Tests for _strptime.strptime."""
@ -198,21 +213,10 @@ class StrptimeTests(unittest.TestCase):
self.assertRaises(ValueError, _strptime.strptime, data_string="%d",
format="%A")
def test_returning_RE(self):
# Make sure that an re can be returned
strp_output = _strptime.strptime(False, "%Y")
self.failUnless(isinstance(strp_output, type(re.compile(''))),
"re object not returned correctly")
self.failUnless(_strptime.strptime("1999", strp_output),
"Use of re object failed")
bad_locale_time = _strptime.LocaleTime(lang="gibberish")
self.assertRaises(TypeError, _strptime.strptime, data_string='1999',
format=strp_output, locale_time=bad_locale_time)
def helper(self, directive, position):
"""Helper fxn in testing."""
strf_output = time.strftime("%%%s" % directive, self.time_tuple)
strp_output = _strptime.strptime(strf_output, "%%%s" % directive)
strf_output = time.strftime("%" + directive, self.time_tuple)
strp_output = _strptime.strptime(strf_output, "%" + directive)
self.failUnless(strp_output[position] == self.time_tuple[position],
"testing of '%s' directive failed; '%s' -> %s != %s" %
(directive, strf_output, strp_output[position],
@ -222,6 +226,14 @@ class StrptimeTests(unittest.TestCase):
# Test that the year is handled properly
for directive in ('y', 'Y'):
self.helper(directive, 0)
# Must also make sure %y values are correct for bounds set by Open Group
for century, bounds in ((1900, ('69', '99')), (2000, ('00', '68'))):
for bound in bounds:
strp_output = _strptime.strptime(bound, '%y')
expected_result = century + int(bound)
self.failUnless(strp_output[0] == expected_result,
"'y' test failed; passed in '%s' "
"and returned '%s'" % (bound, strp_output[0]))
def test_month(self):
# Test for month directives
@ -262,7 +274,7 @@ class StrptimeTests(unittest.TestCase):
# Test timezone directives.
# When gmtime() is used with %Z, entire result of strftime() is empty.
# Check for equal timezone names deals with bad locale info when this
# occurs; first found in FreeBSD 4.4 -current
# occurs; first found in FreeBSD 4.4.
time_tuple = time.localtime()
strf_output = time.strftime("%Z") #UTC does not have a timezone
strp_output = _strptime.strptime(strf_output, "%Z")
@ -274,7 +286,7 @@ class StrptimeTests(unittest.TestCase):
else:
self.failUnless(strp_output[8] == -1,
"LocaleTime().timezone has duplicate values but "
"timzone value not set to -1")
"timzone value not set to 0")
def test_date_time(self):
# Test %c directive
@ -309,6 +321,14 @@ class StrptimeTests(unittest.TestCase):
self.failUnless(_strptime.strptime(strf_output.capitalize(), "%B"),
"strptime does not handle capword names properly")
def test_defaults(self):
# Default return value should be (1900, 1, 1, 0, 0, 0, 0, 1, 0)
defaults = (1900, 1, 1, 0, 0, 0, 0, 1, -1)
strp_output = _strptime.strptime('1', '%m')
self.failUnless(strp_output == defaults,
"Default values for strptime() are incorrect;"
" %s != %s" % (strp_output, defaults))
class FxnTests(unittest.TestCase):
"""Test functions that fill in info by validating result and are triggered
properly."""
@ -325,14 +345,6 @@ class FxnTests(unittest.TestCase):
"julianday failed; %s != %s" %
(result, self.time_tuple[7]))
def test_julianday_trigger(self):
# Make sure julianday is called
strf_output = time.strftime("%Y-%m-%d", self.time_tuple)
strp_output = _strptime.strptime(strf_output, "%Y-%m-%d")
self.failUnless(strp_output[7] == self.time_tuple[7],
"strptime did not trigger julianday(); %s != %s" %
(strp_output[7], self.time_tuple[7]))
def test_gregorian_result(self):
# Test gregorian
result = _strptime.gregorian(self.time_tuple[7], self.time_tuple[0])
@ -340,17 +352,6 @@ class FxnTests(unittest.TestCase):
self.failUnless(result == comparison,
"gregorian() failed; %s != %s" % (result, comparison))
def test_gregorian_trigger(self):
# Test that gregorian() is triggered
strf_output = time.strftime("%j %Y", self.time_tuple)
strp_output = _strptime.strptime(strf_output, "%j %Y")
self.failUnless(strp_output[1] == self.time_tuple[1] and
strp_output[2] == self.time_tuple[2],
"gregorian() not triggered; month -- %s != %s, "
"day -- %s != %s" %
(strp_output[1], self.time_tuple[1], strp_output[2],
self.time_tuple[2]))
def test_dayofweek_result(self):
# Test dayofweek
result = _strptime.dayofweek(self.time_tuple[0], self.time_tuple[1],
@ -359,15 +360,6 @@ class FxnTests(unittest.TestCase):
self.failUnless(result == comparison,
"dayofweek() failed; %s != %s" % (result, comparison))
def test_dayofweek_trigger(self):
# Make sure dayofweek() gets triggered
strf_output = time.strftime("%Y-%m-%d", self.time_tuple)
strp_output = _strptime.strptime(strf_output, "%Y-%m-%d")
self.failUnless(strp_output[6] == self.time_tuple[6],
"triggering of dayofweek() failed; %s != %s" %
(strp_output[6], self.time_tuple[6]))
class Strptime12AMPMTests(unittest.TestCase):
"""Test a _strptime regression in '%I %p' at 12 noon (12 PM)"""
@ -384,7 +376,6 @@ class JulianTests(unittest.TestCase):
def test_all_julian_days(self):
eq = self.assertEqual
# XXX: should 0 be accepted?
for i in range(1, 367):
# use 2004, since it is a leap year, we have 366 days
eq(_strptime.strptime('%d 2004' % i, '%j %Y')[7], i)