diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 423bb3ebd80..4bf42e0eb42 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -14,6 +14,7 @@ import gc import glob import importlib import importlib.util +import locale import logging.handlers import nntplib import os @@ -92,7 +93,7 @@ __all__ = [ "bigmemtest", "bigaddrspacetest", "cpython_only", "get_attribute", "requires_IEEE_754", "skip_unless_xattr", "requires_zlib", "anticipate_failure", "load_package_tests", "detect_api_mismatch", - "check__all__", "skip_unless_bind_unix_socket", + "check__all__", "skip_unless_bind_unix_socket", "skip_if_buggy_ucrt_strfptime", "ignore_warnings", # sys "is_jython", "is_android", "check_impl_detail", "unix_shell", @@ -2501,6 +2502,27 @@ def skip_unless_symlink(test): msg = "Requires functional symlink implementation" return test if ok else unittest.skip(msg)(test) +_buggy_ucrt = None +def skip_if_buggy_ucrt_strfptime(test): + """ + Skip decorator for tests that use buggy strptime/strftime + + If the UCRT bugs are present time.localtime().tm_zone will be + an empty string, otherwise we assume the UCRT bugs are fixed + + See bpo-37552 [Windows] strptime/strftime return invalid + results with UCRT version 17763.615 + """ + global _buggy_ucrt + if _buggy_ucrt is None: + if(sys.platform == 'win32' and + locale.getdefaultlocale()[1] == 'cp65001' and + time.localtime().tm_zone == ''): + _buggy_ucrt = True + else: + _buggy_ucrt = False + return unittest.skip("buggy MSVC UCRT strptime/strftime")(test) if _buggy_ucrt else test + class PythonSymlink: """Creates a symlink for the current Python executable""" def __init__(self, link=None): diff --git a/Lib/test/test_strptime.py b/Lib/test/test_strptime.py index 623da401eee..55a0f426731 100644 --- a/Lib/test/test_strptime.py +++ b/Lib/test/test_strptime.py @@ -7,6 +7,7 @@ import re import os import sys from test import support +from test.support import skip_if_buggy_ucrt_strfptime from datetime import date as datetime_date import _strptime @@ -135,6 +136,7 @@ class TimeRETests(unittest.TestCase): "%s does not have re characters escaped properly" % pattern_string) + @skip_if_buggy_ucrt_strfptime def test_compile(self): # Check that compiled regex is correct found = self.time_re.compile(r"%A").match(self.locale_time.f_weekday[6]) @@ -365,6 +367,7 @@ class StrptimeTests(unittest.TestCase): _strptime._strptime("-01:3030", "%z") self.assertEqual("Inconsistent use of : in -01:3030", str(err.exception)) + @skip_if_buggy_ucrt_strfptime def test_timezone(self): # Test timezone directives. # When gmtime() is used with %Z, entire result of strftime() is empty. @@ -489,6 +492,7 @@ class CalculationTests(unittest.TestCase): def setUp(self): self.time_tuple = time.gmtime() + @skip_if_buggy_ucrt_strfptime 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" @@ -498,6 +502,7 @@ class CalculationTests(unittest.TestCase): "Calculation of tm_yday failed; %s != %s" % (result.tm_yday, self.time_tuple.tm_yday)) + @skip_if_buggy_ucrt_strfptime def test_gregorian_calculation(self): # Test that Gregorian date can be calculated from Julian day format_string = "%Y %H %M %S %w %j %Z" @@ -512,6 +517,7 @@ class CalculationTests(unittest.TestCase): self.time_tuple.tm_year, self.time_tuple.tm_mon, self.time_tuple.tm_mday)) + @skip_if_buggy_ucrt_strfptime 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" diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py index f790d43b6f4..8d8d31e7825 100644 --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -14,6 +14,7 @@ try: except ImportError: _testcapi = None +from test.support import skip_if_buggy_ucrt_strfptime # Max year is only limited by the size of C int. SIZEOF_INT = sysconfig.get_config_var('SIZEOF_INT') or 4 @@ -250,6 +251,7 @@ class TimeTestCase(unittest.TestCase): result = time.strftime("%Y %m %d %H %M %S %w %j", (2000,)+(0,)*8) self.assertEqual(expected, result) + @skip_if_buggy_ucrt_strfptime def test_strptime(self): # Should be able to go round-trip from strftime to strptime without # raising an exception. @@ -672,6 +674,7 @@ class TestStrftime4dyear(_TestStrftimeYear, _Test4dYear, unittest.TestCase): class TestPytime(unittest.TestCase): + @skip_if_buggy_ucrt_strfptime @unittest.skipUnless(time._STRUCT_TM_ITEMS == 11, "needs tm_zone support") def test_localtime_timezone(self):