diff --git a/Lib/_strptime.py b/Lib/_strptime.py --- a/Lib/_strptime.py +++ b/Lib/_strptime.py @@ -158,26 +158,31 @@ class LocaleTime(object): self.LC_date_time = date_time[0] self.LC_date = date_time[1] self.LC_time = date_time[2] def __calc_timezone(self): # Set self.timezone by using time.tzname. # Do not worry about possibility of time.tzname[0] == timetzname[1] # and time.daylight; handle that in strptime . + (no_saving, has_saving) = self._get_timezone() + self.timezone = (no_saving, has_saving) + + @staticmethod + def _get_timezone(): try: time.tzset() except AttributeError: pass no_saving = frozenset(["utc", "gmt", time.tzname[0].lower()]) if time.daylight: has_saving = frozenset([time.tzname[1].lower()]) else: has_saving = frozenset() - self.timezone = (no_saving, has_saving) + return (no_saving, has_saving) class TimeRE(dict): """Handle conversion from format directives to regexes.""" def __init__(self, locale_time=None): """Create keys/values. @@ -207,25 +212,28 @@ class TimeRE(dict): # 4 digits? 'Y': r"(?P\d\d\d\d)", 'z': r"(?P[+-]\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'), 'b': self.__seqToRE(self.locale_time.a_month[1:], 'b'), 'p': self.__seqToRE(self.locale_time.am_pm, 'p'), - 'Z': self.__seqToRE((tz for tz_names in self.locale_time.timezone - for tz in tz_names), - 'Z'), + 'Z': self._calculate_timezone(), '%': '%'}) 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)) + def _calculate_timezone(self, locale_time=None): + if locale_time is None: + locale_time = self.locale_time.timezone + return self.__seqToRE((tz for tz_names in locale_time for tz in tz_names), 'Z') + def __seqToRE(self, to_convert, directive): """Convert a list to a regex string for matching a directive. Want possible matching values to be from longest to shortest. This prevents the possibility of a match occuring for a value that also a substring of a larger value that should have matched (e.g., 'abc' matching when 'abcdef' should have been the match). @@ -303,17 +311,21 @@ def _strptime(data_string, format="%a %b for index, arg in enumerate([data_string, format]): if not isinstance(arg, str): msg = "strptime() argument {} must be str, not {}" raise TypeError(msg.format(index, type(arg))) global _TimeRE_cache, _regex_cache with _cache_lock: - if _getlang() != _TimeRE_cache.locale_time.lang: + locale_time = _TimeRE_cache.locale_time + locale_timezone = locale_time._get_timezone() + if locale_time.timezone != locale_timezone: + _TimeRE_cache['Z'] = _TimeRE_cache._calculate_timezone(locale_timezone) + if _getlang() != locale_time.lang: _TimeRE_cache = TimeRE() _regex_cache.clear() if len(_regex_cache) > _CACHE_MAX_SIZE: _regex_cache.clear() locale_time = _TimeRE_cache.locale_time format_regex = _regex_cache.get(format) if not format_regex: try: diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -223,16 +223,41 @@ class TimeTestCase(unittest.TestCase): # If mktime fails, ctime will fail too. This may happen # on some platforms. pass else: self.assertEqual(time.ctime(testval)[20:], str(year)) @unittest.skipUnless(hasattr(time, "tzset"), "time module has no attribute tzset") + def test_tzset_locale_cache(self): + # See issue 6478 + from os import environ + + try: + environ['TZ'] = 'Pacific/Fiji' + time.tzset() + time.strptime('25-06-2008', '%d-%m-%Y') + + environ['TZ'] = 'America/New_York' + time.tzset() + time.strptime('Fri Jul 25 13:26:29 EDT 2008', + '%a %b %d %H:%M:%S %Z %Y') + except ValueError: + self.fail('time.tzset does not reset _strptime\'s locale ' + 'time cache. See issue 6478.') + finally: + # Repair TZ environment variable in case any other tests + # rely on it. + if 'TZ' in environ: + del environ['TZ'] + time.tzset() + + @unittest.skipUnless(hasattr(time, "tzset"), + "time module has no attribute tzset") def test_tzset(self): from os import environ # Epoch time of midnight Dec 25th 2002. Never DST in northern # hemisphere. xmas2002 = 1040774400.0