From 8abcc5d5339d8d8e056138d1217fa07fdcc342c9 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Mon, 18 Oct 2004 01:37:57 +0000 Subject: Add support for %U and %W to contribute to calculating the date when the year and day of the week are specified. Closes bug #1045381. --- Doc/lib/libtime.tex | 7 +++++-- Lib/_strptime.py | 44 +++++++++++++++++++++++++++++++++++++++++++- Lib/test/test_strptime.py | 22 ++++++++++++++++++++++ Misc/NEWS | 3 ++- 4 files changed, 72 insertions(+), 4 deletions(-) diff --git a/Doc/lib/libtime.tex b/Doc/lib/libtime.tex index 5bca5bc..3b4ec7e 100644 --- a/Doc/lib/libtime.tex +++ b/Doc/lib/libtime.tex @@ -247,11 +247,11 @@ specification, and are replaced by the indicated characters in the \lineiii{\%S}{Second as a decimal number [00,61].}{(2)} \lineiii{\%U}{Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. All days in a new year - preceding the first Sunday are considered to be in week 0.}{} + preceding the first Sunday are considered to be in week 0.}{(3)} \lineiii{\%w}{Weekday as a decimal number [0(Sunday),6].}{} \lineiii{\%W}{Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. All days in a new year - preceding the first Monday are considered to be in week 0.}{} + preceding the first Monday are considered to be in week 0.}{(3)} \lineiii{\%x}{Locale's appropriate date representation.}{} \lineiii{\%X}{Locale's appropriate time representation.}{} \lineiii{\%y}{Year without century as a decimal number [00,99].}{} @@ -271,6 +271,9 @@ Notes: \item[(2)] The range really is \code{0} to \code{61}; this accounts for leap seconds and the (very rare) double leap seconds. + \item[(3)] + \code{\%U} and \code{\%W} are only used in calculations when the day of the + week and the year are specified. \end{description} Here is an example, a format for dates compatible with that specified diff --git a/Lib/_strptime.py b/Lib/_strptime.py index 17460ce..e2657d7 100644 --- a/Lib/_strptime.py +++ b/Lib/_strptime.py @@ -212,7 +212,7 @@ class TimeRE(dict): for tz in tz_names), 'Z'), '%': '%'}) - base.__setitem__('W', base.__getitem__('U')) + base.__setitem__('W', base.__getitem__('U').replace('U', 'W')) base.__setitem__('c', self.pattern(self.locale_time.LC_date_time)) base.__setitem__('x', self.pattern(self.locale_time.LC_date)) base.__setitem__('X', self.pattern(self.locale_time.LC_time)) @@ -298,10 +298,17 @@ def strptime(data_string, format="%a %b %d %H:%M:%S %Y"): month = day = 1 hour = minute = second = 0 tz = -1 + week_of_year = -1 + week_of_year_start = -1 # 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(): + # Directives not explicitly handled below: + # c, x, X + # handled by making out of other directives + # U, W + # worthless without day of the week if group_key == 'y': year = int(found_dict['y']) # Open Group specification for strptime() states that a %y @@ -355,6 +362,14 @@ def strptime(data_string, format="%a %b %d %H:%M:%S %Y"): weekday -= 1 elif group_key == 'j': julian = int(found_dict['j']) + elif group_key in ('U', 'W'): + week_of_year = int(found_dict[group_key]) + if group_key == 'U': + # U starts week on Sunday + week_of_year_start = 6 + else: + # W starts week on Monday + week_of_year_start = 0 elif group_key == 'Z': # Since -1 is default value only need to worry about setting tz if # it can be something other than -1. @@ -370,6 +385,33 @@ def strptime(data_string, format="%a %b %d %H:%M:%S %Y"): else: tz = value break + # If we know the week of the year and what day of that week, we can figure + # out the Julian day of the year + # Calculations below assume 0 is a Monday + # XXX only works for W + if julian == -1 and week_of_year != -1 and weekday != -1 and year != -1: + # Adjust for U directive so that calculations are not dependent on + # directive used to figure out week of year + if weekday == 6 and week_of_year_start == 6: + week_of_year -= 1 + # For some reason when Dec 31 falls on a Monday the week of the year is + # off by a week; verified on both OS X and Solaris. + elif weekday == 0 and week_of_year_start == 6 and week_of_year >= 52: + week_of_year += 1 + # Calculate how many days in week 0 + first_weekday = datetime_date(year, 1, 1).weekday() + preceeding_days = 7 - first_weekday + if preceeding_days == 7: + preceeding_days = 0 + # If in week 0, then just figure out how many days from Jan 1 to day of + # week specified, else calculate by multiplying week of year by 7, + # adding in days in week 0, and the number of days from Monday to the + # day of the week + if not week_of_year: + julian = 1 + weekday - first_weekday + else: + days_to_week = preceeding_days + (7 * (week_of_year - 1)) + julian = 1 + days_to_week + weekday # Cannot pre-calculate datetime_date() since can change in Julian #calculation and thus could have different value for the day of the week #calculation diff --git a/Lib/test/test_strptime.py b/Lib/test/test_strptime.py index 17f339b..bc68851 100644 --- a/Lib/test/test_strptime.py +++ b/Lib/test/test_strptime.py @@ -6,6 +6,7 @@ import locale import re import sys from test import test_support +from datetime import date as datetime_date import _strptime @@ -417,6 +418,27 @@ class CalculationTests(unittest.TestCase): "Calculation of day of the week failed;" "%s != %s" % (result.tm_wday, self.time_tuple.tm_wday)) + def test_week_of_year_and_day_of_week_calculation(self): + # Should be able to infer date if given year, week of year (%U or %W) + # and day of the week + def test_helper(ymd_tuple, test_reason): + for directive in ('W', 'U'): + format_string = "%%Y %%%s %%w" % directive + strp_input = datetime_date(*ymd_tuple).strftime(format_string) + strp_output = _strptime.strptime(strp_input, format_string) + self.failUnless(strp_output[:3] == ymd_tuple, + "%s(%s) test failed w/ '%s': %s != %s" % + (test_reason, directive, strp_input, + strp_output[:3], ymd_tuple[:3])) + test_helper((1901, 1, 3), "week 0") + test_helper((1901, 1, 8), "common case") + test_helper((1901, 1, 13), "day on Sunday") + test_helper((1901, 1, 14), "day on Monday") + test_helper((1905, 1, 1), "Jan 1 on Sunday") + test_helper((1906, 1, 1), "Jan 1 on Monday") + test_helper((1905, 12, 31), "Dec 31 on Sunday") + test_helper((1906, 12, 31), "Dec 31 on Monday") + class CacheTests(unittest.TestCase): """Test that caching works properly.""" diff --git a/Misc/NEWS b/Misc/NEWS index 7831f84..ebd0ba5 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -22,7 +22,8 @@ Extension Modules Library ------- -... +- Bug #1045381: time.strptime() can now infer the date using %U or %W (week of + the year) when the day of the week and year are also specified. Build ----- -- cgit v0.12