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 @@ -2,9 +2,12 @@ import time import unittest import locale +import os import sysconfig import sys import platform +from calendar import timegm +from collections import namedtuple try: import threading except ImportError: @@ -471,6 +474,88 @@ self.assertRaises(ValueError, time.get_clock_info, 'xxx') +class TestTimezoneInfoIsUpdated(unittest.TestCase): + + TzInfo = namedtuple('TzInfo', 'tzname daylight timezone altzone') + + def _verify_timezone_info(self, expected_tzinfo): + # verify that time module attributes correspond to the expected tzinfo + skipped = [] + max_size = bool(expected_tzinfo.daylight) + 1 + for attr in ['tzname', 'daylight', 'timezone', 'altzone']: + if hasattr(time, attr): + # don't use altzone if no DST + if attr != 'altzone' or expected_tzinfo.daylight: + with self.subTest(attr=attr): + got = getattr(time, attr) + expected = getattr(expected_tzinfo, attr) + if attr == 'tzname': + # don't use tzname[1] if no DST + got = got[:max_size] + expected = expected[:max_size] + self.assertEqual(got, expected) + else: + skipped.append(attr) + + if skipped: + self.skipTest("time.{%s} required" % ','.join(skipped)) + + # test with Olson's timezone + # (the tz database availability check should work in a common case) + @unittest.skipIf(sys.platform.startswith('win'), + "Windows CRT does not use Olson's TZ database") + @unittest.skipUnless(os.path.exists('/usr/share/zoneinfo') or + os.path.exists('/usr/lib/zoneinfo'), + "Can't find the Olson's TZ database") + @support.run_with_tz('Europe/Moscow') + def test_timezone_with_multiple_tzinfos(self): + # test whether time functions update time module attributes + # such as tzname, timezone, daylight for the given year + + # ctime, localtime, mktime, strftime -- should update time attributes + # asctime, gmtime -- do not update time attribute + # tzset -- updates time attributes according to the current (year) time + should_update = 'ctime', 'localtime', 'mktime', 'strftime' + tt = lambda year: (year, 7, 1, 0, 0, 0, -1, 0, -1) + time_func = { # func_name -> year -> call + 'ctime': lambda year: time.ctime(timegm(tt(year))), + 'localtime': lambda year: time.localtime(timegm(tt(year))), + 'mktime': lambda year: time.mktime(tt(year)), + 'strftime': lambda year: time.strftime('%Z', tt(year)), + 'asctime': lambda year: time.asctime(tt(year)), + 'gmtime': lambda year: time.gmtime(timegm(tt(year))), + } + tzname = 'MSK', 'MSD' + timezone = -10800 # +0300 + altzone = timezone - 3600 # +0400 + expected = { + # Year January July December + # 2010 MSK+0300 MSD+0400 MSK+0300 + 2010: self.TzInfo(tzname, 1, timezone, altzone), + # 2011 MSK+0300 MSK+0400 MSK+0400 + 2011: self.TzInfo(tzname[:1], 0, timezone, None), #XXX offset? + # 2013 MSK+0400 MSK+0400 MSK+0400 + 2013: self.TzInfo(tzname[:1], 0, altzone, altzone), + # 2014 MSK+0400 MSK+0400 MSK+0300 + 2014: self.TzInfo(tzname[:1], 0, altzone, None), #XXX offset? + # 2015 MSK+0300 MSK+0300 MSK+0300 + 2015: self.TzInfo(tzname[:1], 0, timezone, timezone), + } + + for year in sorted(expected): + for name in should_update + ('asctime', 'gmtime'): + with self.subTest(year=year, name=name): + time.tzset() # reset tzinfo + tzinfo_before = self.TzInfo(time.tzname, + *[getattr(time, attr, None) for attr in [ + 'daylight', 'timezone', 'altzone']]) + self._verify_timezone_info(tzinfo_before) + time_func[name](year) + if name in should_update: + self._verify_timezone_info(expected[year]) + else: + self._verify_timezone_info(tzinfo_before) + class TestLocale(unittest.TestCase): def setUp(self): self.oldloc = locale.setlocale(locale.LC_ALL)