Home | History | Annotate | Download | only in test
      1 from test import support
      2 import decimal
      3 import enum
      4 import locale
      5 import math
      6 import platform
      7 import sys
      8 import sysconfig
      9 import time
     10 import unittest
     11 try:
     12     import threading
     13 except ImportError:
     14     threading = None
     15 try:
     16     import _testcapi
     17 except ImportError:
     18     _testcapi = None
     19 
     20 
     21 # Max year is only limited by the size of C int.
     22 SIZEOF_INT = sysconfig.get_config_var('SIZEOF_INT') or 4
     23 TIME_MAXYEAR = (1 << 8 * SIZEOF_INT - 1) - 1
     24 TIME_MINYEAR = -TIME_MAXYEAR - 1
     25 
     26 SEC_TO_US = 10 ** 6
     27 US_TO_NS = 10 ** 3
     28 MS_TO_NS = 10 ** 6
     29 SEC_TO_NS = 10 ** 9
     30 NS_TO_SEC = 10 ** 9
     31 
     32 class _PyTime(enum.IntEnum):
     33     # Round towards minus infinity (-inf)
     34     ROUND_FLOOR = 0
     35     # Round towards infinity (+inf)
     36     ROUND_CEILING = 1
     37     # Round to nearest with ties going to nearest even integer
     38     ROUND_HALF_EVEN = 2
     39 
     40 # Rounding modes supported by PyTime
     41 ROUNDING_MODES = (
     42     # (PyTime rounding method, decimal rounding method)
     43     (_PyTime.ROUND_FLOOR, decimal.ROUND_FLOOR),
     44     (_PyTime.ROUND_CEILING, decimal.ROUND_CEILING),
     45     (_PyTime.ROUND_HALF_EVEN, decimal.ROUND_HALF_EVEN),
     46 )
     47 
     48 
     49 class TimeTestCase(unittest.TestCase):
     50 
     51     def setUp(self):
     52         self.t = time.time()
     53 
     54     def test_data_attributes(self):
     55         time.altzone
     56         time.daylight
     57         time.timezone
     58         time.tzname
     59 
     60     def test_time(self):
     61         time.time()
     62         info = time.get_clock_info('time')
     63         self.assertFalse(info.monotonic)
     64         self.assertTrue(info.adjustable)
     65 
     66     def test_clock(self):
     67         time.clock()
     68 
     69         info = time.get_clock_info('clock')
     70         self.assertTrue(info.monotonic)
     71         self.assertFalse(info.adjustable)
     72 
     73     @unittest.skipUnless(hasattr(time, 'clock_gettime'),
     74                          'need time.clock_gettime()')
     75     def test_clock_realtime(self):
     76         time.clock_gettime(time.CLOCK_REALTIME)
     77 
     78     @unittest.skipUnless(hasattr(time, 'clock_gettime'),
     79                          'need time.clock_gettime()')
     80     @unittest.skipUnless(hasattr(time, 'CLOCK_MONOTONIC'),
     81                          'need time.CLOCK_MONOTONIC')
     82     def test_clock_monotonic(self):
     83         a = time.clock_gettime(time.CLOCK_MONOTONIC)
     84         b = time.clock_gettime(time.CLOCK_MONOTONIC)
     85         self.assertLessEqual(a, b)
     86 
     87     @unittest.skipUnless(hasattr(time, 'clock_getres'),
     88                          'need time.clock_getres()')
     89     def test_clock_getres(self):
     90         res = time.clock_getres(time.CLOCK_REALTIME)
     91         self.assertGreater(res, 0.0)
     92         self.assertLessEqual(res, 1.0)
     93 
     94     @unittest.skipUnless(hasattr(time, 'clock_settime'),
     95                          'need time.clock_settime()')
     96     def test_clock_settime(self):
     97         t = time.clock_gettime(time.CLOCK_REALTIME)
     98         try:
     99             time.clock_settime(time.CLOCK_REALTIME, t)
    100         except PermissionError:
    101             pass
    102 
    103         if hasattr(time, 'CLOCK_MONOTONIC'):
    104             self.assertRaises(OSError,
    105                               time.clock_settime, time.CLOCK_MONOTONIC, 0)
    106 
    107     def test_conversions(self):
    108         self.assertEqual(time.ctime(self.t),
    109                          time.asctime(time.localtime(self.t)))
    110         self.assertEqual(int(time.mktime(time.localtime(self.t))),
    111                          int(self.t))
    112 
    113     def test_sleep(self):
    114         self.assertRaises(ValueError, time.sleep, -2)
    115         self.assertRaises(ValueError, time.sleep, -1)
    116         time.sleep(1.2)
    117 
    118     def test_strftime(self):
    119         tt = time.gmtime(self.t)
    120         for directive in ('a', 'A', 'b', 'B', 'c', 'd', 'H', 'I',
    121                           'j', 'm', 'M', 'p', 'S',
    122                           'U', 'w', 'W', 'x', 'X', 'y', 'Y', 'Z', '%'):
    123             format = ' %' + directive
    124             try:
    125                 time.strftime(format, tt)
    126             except ValueError:
    127                 self.fail('conversion specifier: %r failed.' % format)
    128 
    129     def _bounds_checking(self, func):
    130         # Make sure that strftime() checks the bounds of the various parts
    131         # of the time tuple (0 is valid for *all* values).
    132 
    133         # The year field is tested by other test cases above
    134 
    135         # Check month [1, 12] + zero support
    136         func((1900, 0, 1, 0, 0, 0, 0, 1, -1))
    137         func((1900, 12, 1, 0, 0, 0, 0, 1, -1))
    138         self.assertRaises(ValueError, func,
    139                             (1900, -1, 1, 0, 0, 0, 0, 1, -1))
    140         self.assertRaises(ValueError, func,
    141                             (1900, 13, 1, 0, 0, 0, 0, 1, -1))
    142         # Check day of month [1, 31] + zero support
    143         func((1900, 1, 0, 0, 0, 0, 0, 1, -1))
    144         func((1900, 1, 31, 0, 0, 0, 0, 1, -1))
    145         self.assertRaises(ValueError, func,
    146                             (1900, 1, -1, 0, 0, 0, 0, 1, -1))
    147         self.assertRaises(ValueError, func,
    148                             (1900, 1, 32, 0, 0, 0, 0, 1, -1))
    149         # Check hour [0, 23]
    150         func((1900, 1, 1, 23, 0, 0, 0, 1, -1))
    151         self.assertRaises(ValueError, func,
    152                             (1900, 1, 1, -1, 0, 0, 0, 1, -1))
    153         self.assertRaises(ValueError, func,
    154                             (1900, 1, 1, 24, 0, 0, 0, 1, -1))
    155         # Check minute [0, 59]
    156         func((1900, 1, 1, 0, 59, 0, 0, 1, -1))
    157         self.assertRaises(ValueError, func,
    158                             (1900, 1, 1, 0, -1, 0, 0, 1, -1))
    159         self.assertRaises(ValueError, func,
    160                             (1900, 1, 1, 0, 60, 0, 0, 1, -1))
    161         # Check second [0, 61]
    162         self.assertRaises(ValueError, func,
    163                             (1900, 1, 1, 0, 0, -1, 0, 1, -1))
    164         # C99 only requires allowing for one leap second, but Python's docs say
    165         # allow two leap seconds (0..61)
    166         func((1900, 1, 1, 0, 0, 60, 0, 1, -1))
    167         func((1900, 1, 1, 0, 0, 61, 0, 1, -1))
    168         self.assertRaises(ValueError, func,
    169                             (1900, 1, 1, 0, 0, 62, 0, 1, -1))
    170         # No check for upper-bound day of week;
    171         #  value forced into range by a ``% 7`` calculation.
    172         # Start check at -2 since gettmarg() increments value before taking
    173         #  modulo.
    174         self.assertEqual(func((1900, 1, 1, 0, 0, 0, -1, 1, -1)),
    175                          func((1900, 1, 1, 0, 0, 0, +6, 1, -1)))
    176         self.assertRaises(ValueError, func,
    177                             (1900, 1, 1, 0, 0, 0, -2, 1, -1))
    178         # Check day of the year [1, 366] + zero support
    179         func((1900, 1, 1, 0, 0, 0, 0, 0, -1))
    180         func((1900, 1, 1, 0, 0, 0, 0, 366, -1))
    181         self.assertRaises(ValueError, func,
    182                             (1900, 1, 1, 0, 0, 0, 0, -1, -1))
    183         self.assertRaises(ValueError, func,
    184                             (1900, 1, 1, 0, 0, 0, 0, 367, -1))
    185 
    186     def test_strftime_bounding_check(self):
    187         self._bounds_checking(lambda tup: time.strftime('', tup))
    188 
    189     def test_strftime_format_check(self):
    190         # Test that strftime does not crash on invalid format strings
    191         # that may trigger a buffer overread. When not triggered,
    192         # strftime may succeed or raise ValueError depending on
    193         # the platform.
    194         for x in [ '', 'A', '%A', '%AA' ]:
    195             for y in range(0x0, 0x10):
    196                 for z in [ '%', 'A%', 'AA%', '%A%', 'A%A%', '%#' ]:
    197                     try:
    198                         time.strftime(x * y + z)
    199                     except ValueError:
    200                         pass
    201 
    202     def test_default_values_for_zero(self):
    203         # Make sure that using all zeros uses the proper default
    204         # values.  No test for daylight savings since strftime() does
    205         # not change output based on its value and no test for year
    206         # because systems vary in their support for year 0.
    207         expected = "2000 01 01 00 00 00 1 001"
    208         with support.check_warnings():
    209             result = time.strftime("%Y %m %d %H %M %S %w %j", (2000,)+(0,)*8)
    210         self.assertEqual(expected, result)
    211 
    212     def test_strptime(self):
    213         # Should be able to go round-trip from strftime to strptime without
    214         # raising an exception.
    215         tt = time.gmtime(self.t)
    216         for directive in ('a', 'A', 'b', 'B', 'c', 'd', 'H', 'I',
    217                           'j', 'm', 'M', 'p', 'S',
    218                           'U', 'w', 'W', 'x', 'X', 'y', 'Y', 'Z', '%'):
    219             format = '%' + directive
    220             strf_output = time.strftime(format, tt)
    221             try:
    222                 time.strptime(strf_output, format)
    223             except ValueError:
    224                 self.fail("conversion specifier %r failed with '%s' input." %
    225                           (format, strf_output))
    226 
    227     def test_strptime_bytes(self):
    228         # Make sure only strings are accepted as arguments to strptime.
    229         self.assertRaises(TypeError, time.strptime, b'2009', "%Y")
    230         self.assertRaises(TypeError, time.strptime, '2009', b'%Y')
    231 
    232     def test_strptime_exception_context(self):
    233         # check that this doesn't chain exceptions needlessly (see #17572)
    234         with self.assertRaises(ValueError) as e:
    235             time.strptime('', '%D')
    236         self.assertIs(e.exception.__suppress_context__, True)
    237         # additional check for IndexError branch (issue #19545)
    238         with self.assertRaises(ValueError) as e:
    239             time.strptime('19', '%Y %')
    240         self.assertIs(e.exception.__suppress_context__, True)
    241 
    242     def test_asctime(self):
    243         time.asctime(time.gmtime(self.t))
    244 
    245         # Max year is only limited by the size of C int.
    246         for bigyear in TIME_MAXYEAR, TIME_MINYEAR:
    247             asc = time.asctime((bigyear, 6, 1) + (0,) * 6)
    248             self.assertEqual(asc[-len(str(bigyear)):], str(bigyear))
    249         self.assertRaises(OverflowError, time.asctime,
    250                           (TIME_MAXYEAR + 1,) + (0,) * 8)
    251         self.assertRaises(OverflowError, time.asctime,
    252                           (TIME_MINYEAR - 1,) + (0,) * 8)
    253         self.assertRaises(TypeError, time.asctime, 0)
    254         self.assertRaises(TypeError, time.asctime, ())
    255         self.assertRaises(TypeError, time.asctime, (0,) * 10)
    256 
    257     def test_asctime_bounding_check(self):
    258         self._bounds_checking(time.asctime)
    259 
    260     def test_ctime(self):
    261         t = time.mktime((1973, 9, 16, 1, 3, 52, 0, 0, -1))
    262         self.assertEqual(time.ctime(t), 'Sun Sep 16 01:03:52 1973')
    263         t = time.mktime((2000, 1, 1, 0, 0, 0, 0, 0, -1))
    264         self.assertEqual(time.ctime(t), 'Sat Jan  1 00:00:00 2000')
    265         for year in [-100, 100, 1000, 2000, 2050, 10000]:
    266             try:
    267                 testval = time.mktime((year, 1, 10) + (0,)*6)
    268             except (ValueError, OverflowError):
    269                 # If mktime fails, ctime will fail too.  This may happen
    270                 # on some platforms.
    271                 pass
    272             else:
    273                 self.assertEqual(time.ctime(testval)[20:], str(year))
    274 
    275     @unittest.skipUnless(hasattr(time, "tzset"),
    276                          "time module has no attribute tzset")
    277     def test_tzset(self):
    278 
    279         from os import environ
    280 
    281         # Epoch time of midnight Dec 25th 2002. Never DST in northern
    282         # hemisphere.
    283         xmas2002 = 1040774400.0
    284 
    285         # These formats are correct for 2002, and possibly future years
    286         # This format is the 'standard' as documented at:
    287         # http://www.opengroup.org/onlinepubs/007904975/basedefs/xbd_chap08.html
    288         # They are also documented in the tzset(3) man page on most Unix
    289         # systems.
    290         eastern = 'EST+05EDT,M4.1.0,M10.5.0'
    291         victoria = 'AEST-10AEDT-11,M10.5.0,M3.5.0'
    292         utc='UTC+0'
    293 
    294         org_TZ = environ.get('TZ',None)
    295         try:
    296             # Make sure we can switch to UTC time and results are correct
    297             # Note that unknown timezones default to UTC.
    298             # Note that altzone is undefined in UTC, as there is no DST
    299             environ['TZ'] = eastern
    300             time.tzset()
    301             environ['TZ'] = utc
    302             time.tzset()
    303             self.assertEqual(
    304                 time.gmtime(xmas2002), time.localtime(xmas2002)
    305                 )
    306             self.assertEqual(time.daylight, 0)
    307             self.assertEqual(time.timezone, 0)
    308             self.assertEqual(time.localtime(xmas2002).tm_isdst, 0)
    309 
    310             # Make sure we can switch to US/Eastern
    311             environ['TZ'] = eastern
    312             time.tzset()
    313             self.assertNotEqual(time.gmtime(xmas2002), time.localtime(xmas2002))
    314             self.assertEqual(time.tzname, ('EST', 'EDT'))
    315             self.assertEqual(len(time.tzname), 2)
    316             self.assertEqual(time.daylight, 1)
    317             self.assertEqual(time.timezone, 18000)
    318             self.assertEqual(time.altzone, 14400)
    319             self.assertEqual(time.localtime(xmas2002).tm_isdst, 0)
    320             self.assertEqual(len(time.tzname), 2)
    321 
    322             # Now go to the southern hemisphere.
    323             environ['TZ'] = victoria
    324             time.tzset()
    325             self.assertNotEqual(time.gmtime(xmas2002), time.localtime(xmas2002))
    326 
    327             # Issue #11886: Australian Eastern Standard Time (UTC+10) is called
    328             # "EST" (as Eastern Standard Time, UTC-5) instead of "AEST"
    329             # (non-DST timezone), and "EDT" instead of "AEDT" (DST timezone),
    330             # on some operating systems (e.g. FreeBSD), which is wrong. See for
    331             # example this bug:
    332             # http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=93810
    333             self.assertIn(time.tzname[0], ('AEST' 'EST'), time.tzname[0])
    334             self.assertTrue(time.tzname[1] in ('AEDT', 'EDT'), str(time.tzname[1]))
    335             self.assertEqual(len(time.tzname), 2)
    336             self.assertEqual(time.daylight, 1)
    337             self.assertEqual(time.timezone, -36000)
    338             self.assertEqual(time.altzone, -39600)
    339             self.assertEqual(time.localtime(xmas2002).tm_isdst, 1)
    340 
    341         finally:
    342             # Repair TZ environment variable in case any other tests
    343             # rely on it.
    344             if org_TZ is not None:
    345                 environ['TZ'] = org_TZ
    346             elif 'TZ' in environ:
    347                 del environ['TZ']
    348             time.tzset()
    349 
    350     def test_insane_timestamps(self):
    351         # It's possible that some platform maps time_t to double,
    352         # and that this test will fail there.  This test should
    353         # exempt such platforms (provided they return reasonable
    354         # results!).
    355         for func in time.ctime, time.gmtime, time.localtime:
    356             for unreasonable in -1e200, 1e200:
    357                 self.assertRaises(OverflowError, func, unreasonable)
    358 
    359     def test_ctime_without_arg(self):
    360         # Not sure how to check the values, since the clock could tick
    361         # at any time.  Make sure these are at least accepted and
    362         # don't raise errors.
    363         time.ctime()
    364         time.ctime(None)
    365 
    366     def test_gmtime_without_arg(self):
    367         gt0 = time.gmtime()
    368         gt1 = time.gmtime(None)
    369         t0 = time.mktime(gt0)
    370         t1 = time.mktime(gt1)
    371         self.assertAlmostEqual(t1, t0, delta=0.2)
    372 
    373     def test_localtime_without_arg(self):
    374         lt0 = time.localtime()
    375         lt1 = time.localtime(None)
    376         t0 = time.mktime(lt0)
    377         t1 = time.mktime(lt1)
    378         self.assertAlmostEqual(t1, t0, delta=0.2)
    379 
    380     def test_mktime(self):
    381         # Issue #1726687
    382         for t in (-2, -1, 0, 1):
    383             if sys.platform.startswith('aix') and t == -1:
    384                 # Issue #11188, #19748: mktime() returns -1 on error. On Linux,
    385                 # the tm_wday field is used as a sentinel () to detect if -1 is
    386                 # really an error or a valid timestamp. On AIX, tm_wday is
    387                 # unchanged even on success and so cannot be used as a
    388                 # sentinel.
    389                 continue
    390             try:
    391                 tt = time.localtime(t)
    392             except (OverflowError, OSError):
    393                 pass
    394             else:
    395                 self.assertEqual(time.mktime(tt), t)
    396 
    397     # Issue #13309: passing extreme values to mktime() or localtime()
    398     # borks the glibc's internal timezone data.
    399     @unittest.skipUnless(platform.libc_ver()[0] != 'glibc',
    400                          "disabled because of a bug in glibc. Issue #13309")
    401     def test_mktime_error(self):
    402         # It may not be possible to reliably make mktime return error
    403         # on all platfom.  This will make sure that no other exception
    404         # than OverflowError is raised for an extreme value.
    405         tt = time.gmtime(self.t)
    406         tzname = time.strftime('%Z', tt)
    407         self.assertNotEqual(tzname, 'LMT')
    408         try:
    409             time.mktime((-1, 1, 1, 0, 0, 0, -1, -1, -1))
    410         except OverflowError:
    411             pass
    412         self.assertEqual(time.strftime('%Z', tt), tzname)
    413 
    414     @unittest.skipUnless(hasattr(time, 'monotonic'),
    415                          'need time.monotonic')
    416     def test_monotonic(self):
    417         # monotonic() should not go backward
    418         times = [time.monotonic() for n in range(100)]
    419         t1 = times[0]
    420         for t2 in times[1:]:
    421             self.assertGreaterEqual(t2, t1, "times=%s" % times)
    422             t1 = t2
    423 
    424         # monotonic() includes time elapsed during a sleep
    425         t1 = time.monotonic()
    426         time.sleep(0.5)
    427         t2 = time.monotonic()
    428         dt = t2 - t1
    429         self.assertGreater(t2, t1)
    430         # Issue #20101: On some Windows machines, dt may be slightly low
    431         self.assertTrue(0.45 <= dt <= 1.0, dt)
    432 
    433         # monotonic() is a monotonic but non adjustable clock
    434         info = time.get_clock_info('monotonic')
    435         self.assertTrue(info.monotonic)
    436         self.assertFalse(info.adjustable)
    437 
    438     def test_perf_counter(self):
    439         time.perf_counter()
    440 
    441     def test_process_time(self):
    442         # process_time() should not include time spend during a sleep
    443         start = time.process_time()
    444         time.sleep(0.100)
    445         stop = time.process_time()
    446         # use 20 ms because process_time() has usually a resolution of 15 ms
    447         # on Windows
    448         self.assertLess(stop - start, 0.020)
    449 
    450         info = time.get_clock_info('process_time')
    451         self.assertTrue(info.monotonic)
    452         self.assertFalse(info.adjustable)
    453 
    454     @unittest.skipUnless(hasattr(time, 'monotonic'),
    455                          'need time.monotonic')
    456     @unittest.skipUnless(hasattr(time, 'clock_settime'),
    457                          'need time.clock_settime')
    458     def test_monotonic_settime(self):
    459         t1 = time.monotonic()
    460         realtime = time.clock_gettime(time.CLOCK_REALTIME)
    461         # jump backward with an offset of 1 hour
    462         try:
    463             time.clock_settime(time.CLOCK_REALTIME, realtime - 3600)
    464         except PermissionError as err:
    465             self.skipTest(err)
    466         t2 = time.monotonic()
    467         time.clock_settime(time.CLOCK_REALTIME, realtime)
    468         # monotonic must not be affected by system clock updates
    469         self.assertGreaterEqual(t2, t1)
    470 
    471     def test_localtime_failure(self):
    472         # Issue #13847: check for localtime() failure
    473         invalid_time_t = None
    474         for time_t in (-1, 2**30, 2**33, 2**60):
    475             try:
    476                 time.localtime(time_t)
    477             except OverflowError:
    478                 self.skipTest("need 64-bit time_t")
    479             except OSError:
    480                 invalid_time_t = time_t
    481                 break
    482         if invalid_time_t is None:
    483             self.skipTest("unable to find an invalid time_t value")
    484 
    485         self.assertRaises(OSError, time.localtime, invalid_time_t)
    486         self.assertRaises(OSError, time.ctime, invalid_time_t)
    487 
    488     def test_get_clock_info(self):
    489         clocks = ['clock', 'perf_counter', 'process_time', 'time']
    490         if hasattr(time, 'monotonic'):
    491             clocks.append('monotonic')
    492 
    493         for name in clocks:
    494             info = time.get_clock_info(name)
    495             #self.assertIsInstance(info, dict)
    496             self.assertIsInstance(info.implementation, str)
    497             self.assertNotEqual(info.implementation, '')
    498             self.assertIsInstance(info.monotonic, bool)
    499             self.assertIsInstance(info.resolution, float)
    500             # 0.0 < resolution <= 1.0
    501             self.assertGreater(info.resolution, 0.0)
    502             self.assertLessEqual(info.resolution, 1.0)
    503             self.assertIsInstance(info.adjustable, bool)
    504 
    505         self.assertRaises(ValueError, time.get_clock_info, 'xxx')
    506 
    507 
    508 class TestLocale(unittest.TestCase):
    509     def setUp(self):
    510         self.oldloc = locale.setlocale(locale.LC_ALL)
    511 
    512     def tearDown(self):
    513         locale.setlocale(locale.LC_ALL, self.oldloc)
    514 
    515     def test_bug_3061(self):
    516         try:
    517             tmp = locale.setlocale(locale.LC_ALL, "fr_FR")
    518         except locale.Error:
    519             self.skipTest('could not set locale.LC_ALL to fr_FR')
    520         # This should not cause an exception
    521         time.strftime("%B", (2009,2,1,0,0,0,0,0,0))
    522 
    523 
    524 class _TestAsctimeYear:
    525     _format = '%d'
    526 
    527     def yearstr(self, y):
    528         return time.asctime((y,) + (0,) * 8).split()[-1]
    529 
    530     def test_large_year(self):
    531         # Check that it doesn't crash for year > 9999
    532         self.assertEqual(self.yearstr(12345), '12345')
    533         self.assertEqual(self.yearstr(123456789), '123456789')
    534 
    535 class _TestStrftimeYear:
    536 
    537     # Issue 13305:  For years < 1000, the value is not always
    538     # padded to 4 digits across platforms.  The C standard
    539     # assumes year >= 1900, so it does not specify the number
    540     # of digits.
    541 
    542     if time.strftime('%Y', (1,) + (0,) * 8) == '0001':
    543         _format = '%04d'
    544     else:
    545         _format = '%d'
    546 
    547     def yearstr(self, y):
    548         return time.strftime('%Y', (y,) + (0,) * 8)
    549 
    550     def test_4dyear(self):
    551         # Check that we can return the zero padded value.
    552         if self._format == '%04d':
    553             self.test_year('%04d')
    554         else:
    555             def year4d(y):
    556                 return time.strftime('%4Y', (y,) + (0,) * 8)
    557             self.test_year('%04d', func=year4d)
    558 
    559     def skip_if_not_supported(y):
    560         msg = "strftime() is limited to [1; 9999] with Visual Studio"
    561         # Check that it doesn't crash for year > 9999
    562         try:
    563             time.strftime('%Y', (y,) + (0,) * 8)
    564         except ValueError:
    565             cond = False
    566         else:
    567             cond = True
    568         return unittest.skipUnless(cond, msg)
    569 
    570     @skip_if_not_supported(10000)
    571     def test_large_year(self):
    572         return super().test_large_year()
    573 
    574     @skip_if_not_supported(0)
    575     def test_negative(self):
    576         return super().test_negative()
    577 
    578     del skip_if_not_supported
    579 
    580 
    581 class _Test4dYear:
    582     _format = '%d'
    583 
    584     def test_year(self, fmt=None, func=None):
    585         fmt = fmt or self._format
    586         func = func or self.yearstr
    587         self.assertEqual(func(1),    fmt % 1)
    588         self.assertEqual(func(68),   fmt % 68)
    589         self.assertEqual(func(69),   fmt % 69)
    590         self.assertEqual(func(99),   fmt % 99)
    591         self.assertEqual(func(999),  fmt % 999)
    592         self.assertEqual(func(9999), fmt % 9999)
    593 
    594     def test_large_year(self):
    595         self.assertEqual(self.yearstr(12345), '12345')
    596         self.assertEqual(self.yearstr(123456789), '123456789')
    597         self.assertEqual(self.yearstr(TIME_MAXYEAR), str(TIME_MAXYEAR))
    598         self.assertRaises(OverflowError, self.yearstr, TIME_MAXYEAR + 1)
    599 
    600     def test_negative(self):
    601         self.assertEqual(self.yearstr(-1), self._format % -1)
    602         self.assertEqual(self.yearstr(-1234), '-1234')
    603         self.assertEqual(self.yearstr(-123456), '-123456')
    604         self.assertEqual(self.yearstr(-123456789), str(-123456789))
    605         self.assertEqual(self.yearstr(-1234567890), str(-1234567890))
    606         self.assertEqual(self.yearstr(TIME_MINYEAR + 1900), str(TIME_MINYEAR + 1900))
    607         # Issue #13312: it may return wrong value for year < TIME_MINYEAR + 1900
    608         # Skip the value test, but check that no error is raised
    609         self.yearstr(TIME_MINYEAR)
    610         # self.assertEqual(self.yearstr(TIME_MINYEAR), str(TIME_MINYEAR))
    611         self.assertRaises(OverflowError, self.yearstr, TIME_MINYEAR - 1)
    612 
    613 
    614 class TestAsctime4dyear(_TestAsctimeYear, _Test4dYear, unittest.TestCase):
    615     pass
    616 
    617 class TestStrftime4dyear(_TestStrftimeYear, _Test4dYear, unittest.TestCase):
    618     pass
    619 
    620 
    621 class TestPytime(unittest.TestCase):
    622     @unittest.skipUnless(time._STRUCT_TM_ITEMS == 11, "needs tm_zone support")
    623     def test_localtime_timezone(self):
    624 
    625         # Get the localtime and examine it for the offset and zone.
    626         lt = time.localtime()
    627         self.assertTrue(hasattr(lt, "tm_gmtoff"))
    628         self.assertTrue(hasattr(lt, "tm_zone"))
    629 
    630         # See if the offset and zone are similar to the module
    631         # attributes.
    632         if lt.tm_gmtoff is None:
    633             self.assertTrue(not hasattr(time, "timezone"))
    634         else:
    635             self.assertEqual(lt.tm_gmtoff, -[time.timezone, time.altzone][lt.tm_isdst])
    636         if lt.tm_zone is None:
    637             self.assertTrue(not hasattr(time, "tzname"))
    638         else:
    639             self.assertEqual(lt.tm_zone, time.tzname[lt.tm_isdst])
    640 
    641         # Try and make UNIX times from the localtime and a 9-tuple
    642         # created from the localtime. Test to see that the times are
    643         # the same.
    644         t = time.mktime(lt); t9 = time.mktime(lt[:9])
    645         self.assertEqual(t, t9)
    646 
    647         # Make localtimes from the UNIX times and compare them to
    648         # the original localtime, thus making a round trip.
    649         new_lt = time.localtime(t); new_lt9 = time.localtime(t9)
    650         self.assertEqual(new_lt, lt)
    651         self.assertEqual(new_lt.tm_gmtoff, lt.tm_gmtoff)
    652         self.assertEqual(new_lt.tm_zone, lt.tm_zone)
    653         self.assertEqual(new_lt9, lt)
    654         self.assertEqual(new_lt.tm_gmtoff, lt.tm_gmtoff)
    655         self.assertEqual(new_lt9.tm_zone, lt.tm_zone)
    656 
    657     @unittest.skipUnless(time._STRUCT_TM_ITEMS == 11, "needs tm_zone support")
    658     def test_strptime_timezone(self):
    659         t = time.strptime("UTC", "%Z")
    660         self.assertEqual(t.tm_zone, 'UTC')
    661         t = time.strptime("+0500", "%z")
    662         self.assertEqual(t.tm_gmtoff, 5 * 3600)
    663 
    664     @unittest.skipUnless(time._STRUCT_TM_ITEMS == 11, "needs tm_zone support")
    665     def test_short_times(self):
    666 
    667         import pickle
    668 
    669         # Load a short time structure using pickle.
    670         st = b"ctime\nstruct_time\np0\n((I2007\nI8\nI11\nI1\nI24\nI49\nI5\nI223\nI1\ntp1\n(dp2\ntp3\nRp4\n."
    671         lt = pickle.loads(st)
    672         self.assertIs(lt.tm_gmtoff, None)
    673         self.assertIs(lt.tm_zone, None)
    674 
    675 
    676 @unittest.skipIf(_testcapi is None, 'need the _testcapi module')
    677 class CPyTimeTestCase:
    678     """
    679     Base class to test the C _PyTime_t API.
    680     """
    681     OVERFLOW_SECONDS = None
    682 
    683     def setUp(self):
    684         from _testcapi import SIZEOF_TIME_T
    685         bits = SIZEOF_TIME_T * 8 - 1
    686         self.time_t_min = -2 ** bits
    687         self.time_t_max = 2 ** bits - 1
    688 
    689     def time_t_filter(self, seconds):
    690         return (self.time_t_min <= seconds <= self.time_t_max)
    691 
    692     def _rounding_values(self, use_float):
    693         "Build timestamps used to test rounding."
    694 
    695         units = [1, US_TO_NS, MS_TO_NS, SEC_TO_NS]
    696         if use_float:
    697             # picoseconds are only tested to pytime_converter accepting floats
    698             units.append(1e-3)
    699 
    700         values = (
    701             # small values
    702             1, 2, 5, 7, 123, 456, 1234,
    703             # 10^k - 1
    704             9,
    705             99,
    706             999,
    707             9999,
    708             99999,
    709             999999,
    710             # test half even rounding near 0.5, 1.5, 2.5, 3.5, 4.5
    711             499, 500, 501,
    712             1499, 1500, 1501,
    713             2500,
    714             3500,
    715             4500,
    716         )
    717 
    718         ns_timestamps = [0]
    719         for unit in units:
    720             for value in values:
    721                 ns = value * unit
    722                 ns_timestamps.extend((-ns, ns))
    723         for pow2 in (0, 5, 10, 15, 22, 23, 24, 30, 33):
    724             ns = (2 ** pow2) * SEC_TO_NS
    725             ns_timestamps.extend((
    726                 -ns-1, -ns, -ns+1,
    727                 ns-1, ns, ns+1
    728             ))
    729         for seconds in (_testcapi.INT_MIN, _testcapi.INT_MAX):
    730             ns_timestamps.append(seconds * SEC_TO_NS)
    731         if use_float:
    732             # numbers with an exact representation in IEEE 754 (base 2)
    733             for pow2 in (3, 7, 10, 15):
    734                 ns = 2.0 ** (-pow2)
    735                 ns_timestamps.extend((-ns, ns))
    736 
    737         # seconds close to _PyTime_t type limit
    738         ns = (2 ** 63 // SEC_TO_NS) * SEC_TO_NS
    739         ns_timestamps.extend((-ns, ns))
    740 
    741         return ns_timestamps
    742 
    743     def _check_rounding(self, pytime_converter, expected_func,
    744                         use_float, unit_to_sec, value_filter=None):
    745 
    746         def convert_values(ns_timestamps):
    747             if use_float:
    748                 unit_to_ns = SEC_TO_NS / float(unit_to_sec)
    749                 values = [ns / unit_to_ns for ns in ns_timestamps]
    750             else:
    751                 unit_to_ns = SEC_TO_NS // unit_to_sec
    752                 values = [ns // unit_to_ns for ns in ns_timestamps]
    753 
    754             if value_filter:
    755                 values = filter(value_filter, values)
    756 
    757             # remove duplicates and sort
    758             return sorted(set(values))
    759 
    760         # test rounding
    761         ns_timestamps = self._rounding_values(use_float)
    762         valid_values = convert_values(ns_timestamps)
    763         for time_rnd, decimal_rnd in ROUNDING_MODES :
    764             context = decimal.getcontext()
    765             context.rounding = decimal_rnd
    766 
    767             for value in valid_values:
    768                 debug_info = {'value': value, 'rounding': decimal_rnd}
    769                 try:
    770                     result = pytime_converter(value, time_rnd)
    771                     expected = expected_func(value)
    772                 except Exception as exc:
    773                     self.fail("Error on timestamp conversion: %s" % debug_info)
    774                 self.assertEqual(result,
    775                                  expected,
    776                                  debug_info)
    777 
    778         # test overflow
    779         ns = self.OVERFLOW_SECONDS * SEC_TO_NS
    780         ns_timestamps = (-ns, ns)
    781         overflow_values = convert_values(ns_timestamps)
    782         for time_rnd, _ in ROUNDING_MODES :
    783             for value in overflow_values:
    784                 debug_info = {'value': value, 'rounding': time_rnd}
    785                 with self.assertRaises(OverflowError, msg=debug_info):
    786                     pytime_converter(value, time_rnd)
    787 
    788     def check_int_rounding(self, pytime_converter, expected_func,
    789                            unit_to_sec=1, value_filter=None):
    790         self._check_rounding(pytime_converter, expected_func,
    791                              False, unit_to_sec, value_filter)
    792 
    793     def check_float_rounding(self, pytime_converter, expected_func,
    794                              unit_to_sec=1, value_filter=None):
    795         self._check_rounding(pytime_converter, expected_func,
    796                              True, unit_to_sec, value_filter)
    797 
    798     def decimal_round(self, x):
    799         d = decimal.Decimal(x)
    800         d = d.quantize(1)
    801         return int(d)
    802 
    803 
    804 class TestCPyTime(CPyTimeTestCase, unittest.TestCase):
    805     """
    806     Test the C _PyTime_t API.
    807     """
    808     # _PyTime_t is a 64-bit signed integer
    809     OVERFLOW_SECONDS = math.ceil((2**63 + 1) / SEC_TO_NS)
    810 
    811     def test_FromSeconds(self):
    812         from _testcapi import PyTime_FromSeconds
    813 
    814         # PyTime_FromSeconds() expects a C int, reject values out of range
    815         def c_int_filter(secs):
    816             return (_testcapi.INT_MIN <= secs <= _testcapi.INT_MAX)
    817 
    818         self.check_int_rounding(lambda secs, rnd: PyTime_FromSeconds(secs),
    819                                 lambda secs: secs * SEC_TO_NS,
    820                                 value_filter=c_int_filter)
    821 
    822     def test_FromSecondsObject(self):
    823         from _testcapi import PyTime_FromSecondsObject
    824 
    825         self.check_int_rounding(
    826             PyTime_FromSecondsObject,
    827             lambda secs: secs * SEC_TO_NS)
    828 
    829         self.check_float_rounding(
    830             PyTime_FromSecondsObject,
    831             lambda ns: self.decimal_round(ns * SEC_TO_NS))
    832 
    833     def test_AsSecondsDouble(self):
    834         from _testcapi import PyTime_AsSecondsDouble
    835 
    836         def float_converter(ns):
    837             if abs(ns) % SEC_TO_NS == 0:
    838                 return float(ns // SEC_TO_NS)
    839             else:
    840                 return float(ns) / SEC_TO_NS
    841 
    842         self.check_int_rounding(lambda ns, rnd: PyTime_AsSecondsDouble(ns),
    843                                 float_converter,
    844                                 NS_TO_SEC)
    845 
    846     def create_decimal_converter(self, denominator):
    847         denom = decimal.Decimal(denominator)
    848 
    849         def converter(value):
    850             d = decimal.Decimal(value) / denom
    851             return self.decimal_round(d)
    852 
    853         return converter
    854 
    855     def test_AsTimeval(self):
    856         from _testcapi import PyTime_AsTimeval
    857 
    858         us_converter = self.create_decimal_converter(US_TO_NS)
    859 
    860         def timeval_converter(ns):
    861             us = us_converter(ns)
    862             return divmod(us, SEC_TO_US)
    863 
    864         if sys.platform == 'win32':
    865             from _testcapi import LONG_MIN, LONG_MAX
    866 
    867             # On Windows, timeval.tv_sec type is a C long
    868             def seconds_filter(secs):
    869                 return LONG_MIN <= secs <= LONG_MAX
    870         else:
    871             seconds_filter = self.time_t_filter
    872 
    873         self.check_int_rounding(PyTime_AsTimeval,
    874                                 timeval_converter,
    875                                 NS_TO_SEC,
    876                                 value_filter=seconds_filter)
    877 
    878     @unittest.skipUnless(hasattr(_testcapi, 'PyTime_AsTimespec'),
    879                          'need _testcapi.PyTime_AsTimespec')
    880     def test_AsTimespec(self):
    881         from _testcapi import PyTime_AsTimespec
    882 
    883         def timespec_converter(ns):
    884             return divmod(ns, SEC_TO_NS)
    885 
    886         self.check_int_rounding(lambda ns, rnd: PyTime_AsTimespec(ns),
    887                                 timespec_converter,
    888                                 NS_TO_SEC,
    889                                 value_filter=self.time_t_filter)
    890 
    891     def test_AsMilliseconds(self):
    892         from _testcapi import PyTime_AsMilliseconds
    893 
    894         self.check_int_rounding(PyTime_AsMilliseconds,
    895                                 self.create_decimal_converter(MS_TO_NS),
    896                                 NS_TO_SEC)
    897 
    898     def test_AsMicroseconds(self):
    899         from _testcapi import PyTime_AsMicroseconds
    900 
    901         self.check_int_rounding(PyTime_AsMicroseconds,
    902                                 self.create_decimal_converter(US_TO_NS),
    903                                 NS_TO_SEC)
    904 
    905 
    906 class TestOldPyTime(CPyTimeTestCase, unittest.TestCase):
    907     """
    908     Test the old C _PyTime_t API: _PyTime_ObjectToXXX() functions.
    909     """
    910 
    911     # time_t is a 32-bit or 64-bit signed integer
    912     OVERFLOW_SECONDS = 2 ** 64
    913 
    914     def test_object_to_time_t(self):
    915         from _testcapi import pytime_object_to_time_t
    916 
    917         self.check_int_rounding(pytime_object_to_time_t,
    918                                 lambda secs: secs,
    919                                 value_filter=self.time_t_filter)
    920 
    921         self.check_float_rounding(pytime_object_to_time_t,
    922                                   self.decimal_round,
    923                                   value_filter=self.time_t_filter)
    924 
    925     def create_converter(self, sec_to_unit):
    926         def converter(secs):
    927             floatpart, intpart = math.modf(secs)
    928             intpart = int(intpart)
    929             floatpart *= sec_to_unit
    930             floatpart = self.decimal_round(floatpart)
    931             if floatpart < 0:
    932                 floatpart += sec_to_unit
    933                 intpart -= 1
    934             elif floatpart >= sec_to_unit:
    935                 floatpart -= sec_to_unit
    936                 intpart += 1
    937             return (intpart, floatpart)
    938         return converter
    939 
    940     def test_object_to_timeval(self):
    941         from _testcapi import pytime_object_to_timeval
    942 
    943         self.check_int_rounding(pytime_object_to_timeval,
    944                                 lambda secs: (secs, 0),
    945                                 value_filter=self.time_t_filter)
    946 
    947         self.check_float_rounding(pytime_object_to_timeval,
    948                                   self.create_converter(SEC_TO_US),
    949                                   value_filter=self.time_t_filter)
    950 
    951     def test_object_to_timespec(self):
    952         from _testcapi import pytime_object_to_timespec
    953 
    954         self.check_int_rounding(pytime_object_to_timespec,
    955                                 lambda secs: (secs, 0),
    956                                 value_filter=self.time_t_filter)
    957 
    958         self.check_float_rounding(pytime_object_to_timespec,
    959                                   self.create_converter(SEC_TO_NS),
    960                                   value_filter=self.time_t_filter)
    961 
    962 
    963 if __name__ == "__main__":
    964     unittest.main()
    965