From 1fdb6335304551b79838523811525d3c59d901ae Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Sun, 9 Mar 2003 07:44:42 +0000 Subject: SF patch #691928: Use datetime in _strptime Contributed by Brett Cannon. To prevent code duplication, I patched _strptime to use datetime's date object to do Julian day, Gregorian, and day of the week calculations. Patch also includes new regression tests to test results and the calculation gets triggered. Very minor comment changes and the contact email are also changed. --- Lib/_strptime.py | 74 ++++++++++++----------------------------------- Lib/test/test_strptime.py | 70 ++++++++++++++++++++++++-------------------- 2 files changed, 56 insertions(+), 88 deletions(-) diff --git a/Lib/_strptime.py b/Lib/_strptime.py index 4afc8fc..55391c1 100644 --- a/Lib/_strptime.py +++ b/Lib/_strptime.py @@ -6,32 +6,25 @@ CLASSES: time information as is returned by time.strftime() FUNCTIONS: - firstjulian -- Calculates the Julian date up to the first of the specified - year - gregorian -- Calculates the Gregorian date based on the Julian day and - year - julianday -- Calculates the Julian day since the first of the year based - on the Gregorian date - dayofweek -- Calculates the day of the week from the Gregorian date. + _getlang -- Figure out what language is being used for the locale strptime -- Calculates the time struct represented by the passed-in string -Requires Python 2.2.1 or higher. +Requires Python 2.2.1 or higher (mainly because of the use of property()). Can be used in Python 2.2 if the following line is added: - >>> True = 1; False = 0 + True = 1; False = 0 """ import time import locale import calendar from re import compile as re_compile from re import IGNORECASE +from datetime import date as datetime_date __author__ = "Brett Cannon" -__email__ = "drifty@bigfoot.com" +__email__ = "brett@python.org" __all__ = ['strptime'] -RegexpType = type(re_compile('')) - def _getlang(): # Figure out what the current language is set to. current_lang = locale.getlocale(locale.LC_TIME)[0] @@ -425,7 +418,7 @@ def strptime(data_string, format="%a %b %d %H:%M:%S %Y"): month = day = 1 hour = minute = second = 0 tz = -1 - # Defaulted to -1 so as to signal using functions to calc values + # weekday and julian defaulted to -1 so as to signal need to calculate values weekday = julian = -1 found_dict = found.groupdict() for group_key in found_dict.iterkeys(): @@ -495,16 +488,21 @@ def strptime(data_string, format="%a %b %d %H:%M:%S %Y"): tz = 1 elif locale_time.timezone[2].lower() == found_zone: tz = -1 - #XXX : 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. + # Cannot pre-calculate datetime_date() since can change in Julian + #calculation and thus could have different value for the day of the week + #calculation if julian == -1: - julian = julianday(year, month, day) - else: # Assuming that if they bothered to include Julian day it will + # Need to add 1 to result since first day of the year is 1, not 0. + julian = datetime_date(year, month, day).toordinal() - \ + datetime_date(year, 1, 1).toordinal() + 1 + else: # Assume that if they bothered to include Julian day it will #be accurate - year, month, day = gregorian(julian, year) + datetime_result = datetime_date.fromordinal((julian - 1) + datetime_date(year, 1, 1).toordinal()) + year = datetime_result.year + month = datetime_result.month + day = datetime_result.day if weekday == -1: - weekday = dayofweek(year, month, day) + weekday = datetime_date(year, month, day).weekday() return time.struct_time((year, month, day, hour, minute, second, weekday, julian, tz)) @@ -522,39 +520,3 @@ def _insensitiveindex(lst, findme): else: raise ValueError("value not in list") -def firstjulian(year): - """Calculate the Julian date up until the first of the year.""" - return ((146097 * (year + 4799)) // 400) - 31738 - -def julianday(year, month, day): - """Calculate the Julian day since the beginning of the year. - Calculated from the Gregorian date. - """ - a = (14 - month) // 12 - return (day - 32045 - + (((153 * (month + (12 * a) - 3)) + 2) // 5) - + ((146097 * (year + 4800 - a)) // 400)) - firstjulian(year) + 1 - -def gregorian(julian, year): - """Return 3-item list containing Gregorian date based on the Julian day.""" - a = 32043 + julian + firstjulian(year) - b = ((4 * a) + 3) // 146097 - c = a - ((146097 * b) // 4) - d = ((4 * c) + 3) // 1461 - e = c - ((1461 * d) // 4) - m = ((5 * e) + 2) // 153 - day = 1 + e - (((153 * m) + 2) // 5) - month = m + 3 - (12 * (m // 10)) - year = (100 * b) + d - 4800 + (m // 10) - return [year, month, day] - -def dayofweek(year, month, day): - """Calculate the day of the week (Monday is 0).""" - a = (14 - month) // 12 - y = year - a - weekday = (day + y + ((97 * y) // 400) - + ((31 * (month + (12 * a) -2 )) // 12)) % 7 - if weekday == 0: - return 6 - else: - return weekday-1 diff --git a/Lib/test/test_strptime.py b/Lib/test/test_strptime.py index 2510a90..e708f4c 100644 --- a/Lib/test/test_strptime.py +++ b/Lib/test/test_strptime.py @@ -329,37 +329,6 @@ class StrptimeTests(unittest.TestCase): "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.""" - - def setUp(self): - """Create an initial time tuple.""" - self.time_tuple = time.gmtime() - - def test_julianday_result(self): - # Test julianday - result = _strptime.julianday(self.time_tuple[0], self.time_tuple[1], - self.time_tuple[2]) - self.failUnless(result == self.time_tuple[7], - "julianday failed; %s != %s" % - (result, self.time_tuple[7])) - - def test_gregorian_result(self): - # Test gregorian - result = _strptime.gregorian(self.time_tuple[7], self.time_tuple[0]) - comparison = [self.time_tuple[0], self.time_tuple[1], self.time_tuple[2]] - self.failUnless(result == comparison, - "gregorian() failed; %s != %s" % (result, comparison)) - - def test_dayofweek_result(self): - # Test dayofweek - result = _strptime.dayofweek(self.time_tuple[0], self.time_tuple[1], - self.time_tuple[2]) - comparison = self.time_tuple[6] - self.failUnless(result == comparison, - "dayofweek() failed; %s != %s" % (result, comparison)) - class Strptime12AMPMTests(unittest.TestCase): """Test a _strptime regression in '%I %p' at 12 noon (12 PM)""" @@ -380,15 +349,52 @@ class JulianTests(unittest.TestCase): # use 2004, since it is a leap year, we have 366 days eq(_strptime.strptime('%d 2004' % i, '%j %Y')[7], i) +class CalculationTests(unittest.TestCase): + """Test that strptime() fills in missing info correctly""" + + def setUp(self): + self.time_tuple = time.gmtime() + + def test_julian_calculation(self): + # Make sure that when Julian is missing that it is calculated + format_string = "%Y %m %d %H %M %S %w %Z" + result = _strptime.strptime(time.strftime(format_string, self.time_tuple), + format_string) + self.failUnless(result.tm_yday == self.time_tuple.tm_yday, + "Calculation of tm_yday failed; %s != %s" % + (result.tm_yday, self.time_tuple.tm_yday)) + + def test_gregorian_calculation(self): + # Test that Gregorian date can be calculated from Julian day + format_string = "%Y %H %M %S %w %j %Z" + result = _strptime.strptime(time.strftime(format_string, self.time_tuple), + format_string) + self.failUnless(result.tm_year == self.time_tuple.tm_year and + result.tm_mon == self.time_tuple.tm_mon and + result.tm_mday == self.time_tuple.tm_mday, + "Calculation of Gregorian date failed;" + "%s-%s-%s != %s-%s-%s" % + (result.tm_year, result.tm_mon, result.tm_mday, + self.time_tuple.tm_year, self.time_tuple.tm_mon, + self.time_tuple.tm_mday)) + + def test_day_of_week_calculation(self): + # Test that the day of the week is calculated as needed + format_string = "%Y %m %d %H %S %j %Z" + result = _strptime.strptime(time.strftime(format_string, self.time_tuple), + format_string) + self.failUnless(result.tm_wday == self.time_tuple.tm_wday, + "Calculation of day of the week failed;" + "%s != %s" % (result.tm_wday, self.time_tuple.tm_wday)) def test_main(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(LocaleTime_Tests)) suite.addTest(unittest.makeSuite(TimeRETests)) suite.addTest(unittest.makeSuite(StrptimeTests)) - suite.addTest(unittest.makeSuite(FxnTests)) suite.addTest(unittest.makeSuite(Strptime12AMPMTests)) suite.addTest(unittest.makeSuite(JulianTests)) + suite.addTest(unittest.makeSuite(CalculationTests)) test_support.run_suite(suite) -- cgit v0.12