get available time zones including posix and right directories from importlib import resources availablezones sortedzoneinfo availabletimezones tzpath zoneinfo tzpath def validkeykey for root in tzpath keyfile os path joinroot key if os path existskeyfile return true components key split packagename jointzdata zoneinfo components 1 resourcename components1 try return resources filespackagename joinpathresourcename isfile except modulenotfounderror return false this relies on the fact that dictionaries maintain insertion order for shrinking purposes it is preferable to start with the standard version then move to the posix version then to the right version outzones availablezones for prefix in posix right prefixout for key in availablezones prefixkey fprefixkey if validkeyprefixkey prefixout appendprefixkey outzonesprefix prefixout output for keys in outzones values output extendkeys return output validkeys validkeys if not validkeys raise unittest skiptestno time zone data available def validkeys return hypothesis strategies sampledfromvalidkeys keyexamples africaabidjan africacasablanca americalosangeles americasantiago asiatokyo australiasydney europedublin europelisbon europelondon pacifickiritimati utc def addkeyexamplesf for key in keyexamples f hypothesis examplekeyf return f class zoneinfotestzoneinfotestbase module pyzoneinfo hypothesis givenkeyvalidkeys addkeyexamples def teststrself key zi self klasskey self assertequalstrzi key hypothesis givenkeyvalidkeys addkeyexamples def testkeyself key zi self klasskey self assertequalzi key key hypothesis given dthypothesis strategies oneof hypothesis strategies datetimes hypothesis strategies times hypothesis exampledtdatetime datetime min hypothesis exampledtdatetime datetime max hypothesis exampledtdatetime datetime1970 1 1 hypothesis exampledtdatetime datetime2039 1 1 hypothesis exampledtdatetime time0 hypothesis exampledtdatetime time12 0 hypothesis exampledtdatetime time23 59 59 999999 def testutcself dt zi self klassutc dtzi dt replacetzinfozi self assertequaldtzi utcoffset zero self assertequaldtzi dst zero self assertequaldtzi tzname utc class czoneinfotestzoneinfotest module czoneinfo class zoneinfopickletestzoneinfotestbase module pyzoneinfo def setupself with contextlib exitstack as stack stack entercontexttestsupport setzoneinfomoduleself module self addcleanupstack popall close super setup hypothesis givenkeyvalidkeys addkeyexamples def testpickleunpicklecacheself key zi self klasskey pklstr pickle dumpszi zirt pickle loadspklstr self assertiszi zirt hypothesis givenkeyvalidkeys addkeyexamples def testpickleunpicklenocacheself key zi self klass nocachekey pklstr pickle dumpszi zirt pickle loadspklstr self assertisnotzi zirt self assertequalstrzi strzirt hypothesis givenkeyvalidkeys addkeyexamples def testpickleunpicklecachemultipleroundsself key test that pickleunpickle is idempotent zicache self klasskey zi0 self klass nocachekey pklstr0 pickle dumpszi0 zi1 pickle loadspklstr0 pklstr1 pickle dumpszi1 zi2 pickle loadspklstr1 pklstr2 pickle dumpszi2 self assertequalpklstr0 pklstr1 self assertequalpklstr1 pklstr2 self assertisnotzi0 zi1 self assertisnotzi0 zi2 self assertisnotzi1 zi2 self assertisnotzi0 zicache self assertisnotzi1 zicache self assertisnotzi2 zicache class czoneinfopickletestzoneinfopickletest module czoneinfo class zoneinfocachetestzoneinfotestbase module pyzoneinfo hypothesis givenkeyvalidkeys addkeyexamples def testcacheself key zi0 self klasskey zi1 self klasskey self assertiszi0 zi1 hypothesis givenkeyvalidkeys addkeyexamples def testnocacheself key zi0 self klass nocachekey zi1 self klass nocachekey self assertisnotzi0 zi1 class czoneinfocachetestzoneinfocachetest klass czoneinfo zoneinfo class pythoncconsistencytestunittest testcase convert to utc this can overflow but we just care about consistency pep 495 says that an interzone comparison between ambiguous datetimes is always false convert from utc overflow ok if it happens in both implementations python c c python get available time zones including posix and right directories this relies on the fact that dictionaries maintain insertion order for shrinking purposes it is preferable to start with the standard version then move to the posix version then to the right version test that pickle unpickle is idempotent test that pickle unpickle is idempotent tests that the c and python versions do the same thing convert to utc this can overflow but we just care about consistency pragma nocover consistently raises the same exception pep 495 says that an inter zone comparison between ambiguous datetimes is always false convert from utc overflow ok if it happens in both implementations pragma nocover consistently raises the same exception python c c python
import contextlib
import datetime
import os
import pickle
import unittest
import zoneinfo

from test.support.hypothesis_helper import hypothesis

import test.test_zoneinfo._support as test_support

ZoneInfoTestBase = test_support.ZoneInfoTestBase

py_zoneinfo, c_zoneinfo = test_support.get_modules()

UTC = datetime.timezone.utc
MIN_UTC = datetime.datetime.min.replace(tzinfo=UTC)
MAX_UTC = datetime.datetime.max.replace(tzinfo=UTC)
ZERO = datetime.timedelta(0)


def _valid_keys():
    
    from importlib import resources

    available_zones = sorted(zoneinfo.available_timezones())
    TZPATH = zoneinfo.TZPATH

    def valid_key(key):
        for root in TZPATH:
            key_file = os.path.join(root, key)
            if os.path.exists(key_file):
                return True

        components = key.split("/")
        package_name = ".".join(["tzdata.zoneinfo"] + components[:-1])
        resource_name = components[-1]

        try:
            return resources.files(package_name).joinpath(resource_name).is_file()
        except ModuleNotFoundError:
            return False

    
    
    
    out_zones = {"": available_zones}
    for prefix in ["posix", "right"]:
        prefix_out = []
        for key in available_zones:
            prefix_key = f"{prefix}/{key}"
            if valid_key(prefix_key):
                prefix_out.append(prefix_key)

        out_zones[prefix] = prefix_out

    output = []
    for keys in out_zones.values():
        output.extend(keys)

    return output


VALID_KEYS = _valid_keys()
if not VALID_KEYS:
    raise unittest.SkipTest("No time zone data available")


def valid_keys():
    return hypothesis.strategies.sampled_from(VALID_KEYS)


KEY_EXAMPLES = [
    "Africa/Abidjan",
    "Africa/Casablanca",
    "America/Los_Angeles",
    "America/Santiago",
    "Asia/Tokyo",
    "Australia/Sydney",
    "Europe/Dublin",
    "Europe/Lisbon",
    "Europe/London",
    "Pacific/Kiritimati",
    "UTC",
]


def add_key_examples(f):
    for key in KEY_EXAMPLES:
        f = hypothesis.example(key)(f)
    return f


class ZoneInfoTest(ZoneInfoTestBase):
    module = py_zoneinfo

    @hypothesis.given(key=valid_keys())
    @add_key_examples
    def test_str(self, key):
        zi = self.klass(key)
        self.assertEqual(str(zi), key)

    @hypothesis.given(key=valid_keys())
    @add_key_examples
    def test_key(self, key):
        zi = self.klass(key)

        self.assertEqual(zi.key, key)

    @hypothesis.given(
        dt=hypothesis.strategies.one_of(
            hypothesis.strategies.datetimes(), hypothesis.strategies.times()
        )
    )
    @hypothesis.example(dt=datetime.datetime.min)
    @hypothesis.example(dt=datetime.datetime.max)
    @hypothesis.example(dt=datetime.datetime(1970, 1, 1))
    @hypothesis.example(dt=datetime.datetime(2039, 1, 1))
    @hypothesis.example(dt=datetime.time(0))
    @hypothesis.example(dt=datetime.time(12, 0))
    @hypothesis.example(dt=datetime.time(23, 59, 59, 999999))
    def test_utc(self, dt):
        zi = self.klass("UTC")
        dt_zi = dt.replace(tzinfo=zi)

        self.assertEqual(dt_zi.utcoffset(), ZERO)
        self.assertEqual(dt_zi.dst(), ZERO)
        self.assertEqual(dt_zi.tzname(), "UTC")


class CZoneInfoTest(ZoneInfoTest):
    module = c_zoneinfo


class ZoneInfoPickleTest(ZoneInfoTestBase):
    module = py_zoneinfo

    def setUp(self):
        with contextlib.ExitStack() as stack:
            stack.enter_context(test_support.set_zoneinfo_module(self.module))
            self.addCleanup(stack.pop_all().close)

        super().setUp()

    @hypothesis.given(key=valid_keys())
    @add_key_examples
    def test_pickle_unpickle_cache(self, key):
        zi = self.klass(key)
        pkl_str = pickle.dumps(zi)
        zi_rt = pickle.loads(pkl_str)

        self.assertIs(zi, zi_rt)

    @hypothesis.given(key=valid_keys())
    @add_key_examples
    def test_pickle_unpickle_no_cache(self, key):
        zi = self.klass.no_cache(key)
        pkl_str = pickle.dumps(zi)
        zi_rt = pickle.loads(pkl_str)

        self.assertIsNot(zi, zi_rt)
        self.assertEqual(str(zi), str(zi_rt))

    @hypothesis.given(key=valid_keys())
    @add_key_examples
    def test_pickle_unpickle_cache_multiple_rounds(self, key):
        
        zi_0 = self.klass(key)
        pkl_str_0 = pickle.dumps(zi_0)
        zi_1 = pickle.loads(pkl_str_0)
        pkl_str_1 = pickle.dumps(zi_1)
        zi_2 = pickle.loads(pkl_str_1)
        pkl_str_2 = pickle.dumps(zi_2)

        self.assertEqual(pkl_str_0, pkl_str_1)
        self.assertEqual(pkl_str_1, pkl_str_2)

        self.assertIs(zi_0, zi_1)
        self.assertIs(zi_0, zi_2)
        self.assertIs(zi_1, zi_2)

    @hypothesis.given(key=valid_keys())
    @add_key_examples
    def test_pickle_unpickle_no_cache_multiple_rounds(self, key):
        
        zi_cache = self.klass(key)

        zi_0 = self.klass.no_cache(key)
        pkl_str_0 = pickle.dumps(zi_0)
        zi_1 = pickle.loads(pkl_str_0)
        pkl_str_1 = pickle.dumps(zi_1)
        zi_2 = pickle.loads(pkl_str_1)
        pkl_str_2 = pickle.dumps(zi_2)

        self.assertEqual(pkl_str_0, pkl_str_1)
        self.assertEqual(pkl_str_1, pkl_str_2)

        self.assertIsNot(zi_0, zi_1)
        self.assertIsNot(zi_0, zi_2)
        self.assertIsNot(zi_1, zi_2)

        self.assertIsNot(zi_0, zi_cache)
        self.assertIsNot(zi_1, zi_cache)
        self.assertIsNot(zi_2, zi_cache)


class CZoneInfoPickleTest(ZoneInfoPickleTest):
    module = c_zoneinfo


class ZoneInfoCacheTest(ZoneInfoTestBase):
    module = py_zoneinfo

    @hypothesis.given(key=valid_keys())
    @add_key_examples
    def test_cache(self, key):
        zi_0 = self.klass(key)
        zi_1 = self.klass(key)

        self.assertIs(zi_0, zi_1)

    @hypothesis.given(key=valid_keys())
    @add_key_examples
    def test_no_cache(self, key):
        zi_0 = self.klass.no_cache(key)
        zi_1 = self.klass.no_cache(key)

        self.assertIsNot(zi_0, zi_1)


class CZoneInfoCacheTest(ZoneInfoCacheTest):
    klass = c_zoneinfo.ZoneInfo


class PythonCConsistencyTest(unittest.TestCase):
    

    def _is_ambiguous(self, dt):
        return dt.replace(fold=not dt.fold).utcoffset() == dt.utcoffset()

    @hypothesis.given(dt=hypothesis.strategies.datetimes(), key=valid_keys())
    @hypothesis.example(dt=datetime.datetime.min, key="America/New_York")
    @hypothesis.example(dt=datetime.datetime.max, key="America/New_York")
    @hypothesis.example(dt=datetime.datetime(1970, 1, 1), key="America/New_York")
    @hypothesis.example(dt=datetime.datetime(2020, 1, 1), key="Europe/Paris")
    @hypothesis.example(dt=datetime.datetime(2020, 6, 1), key="Europe/Paris")
    def test_same_str(self, dt, key):
        py_dt = dt.replace(tzinfo=py_zoneinfo.ZoneInfo(key))
        c_dt = dt.replace(tzinfo=c_zoneinfo.ZoneInfo(key))

        self.assertEqual(str(py_dt), str(c_dt))

    @hypothesis.given(dt=hypothesis.strategies.datetimes(), key=valid_keys())
    @hypothesis.example(dt=datetime.datetime(1970, 1, 1), key="America/New_York")
    @hypothesis.example(dt=datetime.datetime(2020, 2, 5), key="America/New_York")
    @hypothesis.example(dt=datetime.datetime(2020, 8, 12), key="America/New_York")
    @hypothesis.example(dt=datetime.datetime(2040, 1, 1), key="Africa/Casablanca")
    @hypothesis.example(dt=datetime.datetime(1970, 1, 1), key="Europe/Paris")
    @hypothesis.example(dt=datetime.datetime(2040, 1, 1), key="Europe/Paris")
    @hypothesis.example(dt=datetime.datetime.min, key="Asia/Tokyo")
    @hypothesis.example(dt=datetime.datetime.max, key="Asia/Tokyo")
    def test_same_offsets_and_names(self, dt, key):
        py_dt = dt.replace(tzinfo=py_zoneinfo.ZoneInfo(key))
        c_dt = dt.replace(tzinfo=c_zoneinfo.ZoneInfo(key))

        self.assertEqual(py_dt.tzname(), c_dt.tzname())
        self.assertEqual(py_dt.utcoffset(), c_dt.utcoffset())
        self.assertEqual(py_dt.dst(), c_dt.dst())

    @hypothesis.given(
        dt=hypothesis.strategies.datetimes(timezones=hypothesis.strategies.just(UTC)),
        key=valid_keys(),
    )
    @hypothesis.example(dt=MIN_UTC, key="Asia/Tokyo")
    @hypothesis.example(dt=MAX_UTC, key="Asia/Tokyo")
    @hypothesis.example(dt=MIN_UTC, key="America/New_York")
    @hypothesis.example(dt=MAX_UTC, key="America/New_York")
    @hypothesis.example(
        dt=datetime.datetime(2006, 10, 29, 5, 15, tzinfo=UTC),
        key="America/New_York",
    )
    def test_same_from_utc(self, dt, key):
        py_zi = py_zoneinfo.ZoneInfo(key)
        c_zi = c_zoneinfo.ZoneInfo(key)

        
        py_overflow_exc = None
        c_overflow_exc = None
        try:
            py_dt = dt.astimezone(py_zi)
        except OverflowError as e:
            py_overflow_exc = e

        try:
            c_dt = dt.astimezone(c_zi)
        except OverflowError as e:
            c_overflow_exc = e

        if (py_overflow_exc is not None) != (c_overflow_exc is not None):
            raise py_overflow_exc or c_overflow_exc  

        if py_overflow_exc is not None:
            return  

        
        
        if py_dt != c_dt:
            self.assertEqual(
                self._is_ambiguous(py_dt),
                self._is_ambiguous(c_dt),
                (py_dt, c_dt),
            )

        self.assertEqual(py_dt.tzname(), c_dt.tzname())
        self.assertEqual(py_dt.utcoffset(), c_dt.utcoffset())
        self.assertEqual(py_dt.dst(), c_dt.dst())

    @hypothesis.given(dt=hypothesis.strategies.datetimes(), key=valid_keys())
    @hypothesis.example(dt=datetime.datetime.max, key="America/New_York")
    @hypothesis.example(dt=datetime.datetime.min, key="America/New_York")
    @hypothesis.example(dt=datetime.datetime.min, key="Asia/Tokyo")
    @hypothesis.example(dt=datetime.datetime.max, key="Asia/Tokyo")
    def test_same_to_utc(self, dt, key):
        py_dt = dt.replace(tzinfo=py_zoneinfo.ZoneInfo(key))
        c_dt = dt.replace(tzinfo=c_zoneinfo.ZoneInfo(key))

        
        py_overflow_exc = None
        c_overflow_exc = None
        try:
            py_utc = py_dt.astimezone(UTC)
        except OverflowError as e:
            py_overflow_exc = e

        try:
            c_utc = c_dt.astimezone(UTC)
        except OverflowError as e:
            c_overflow_exc = e

        if (py_overflow_exc is not None) != (c_overflow_exc is not None):
            raise py_overflow_exc or c_overflow_exc  

        if py_overflow_exc is not None:
            return  

        self.assertEqual(py_utc, c_utc)

    @hypothesis.given(key=valid_keys())
    @add_key_examples
    def test_cross_module_pickle(self, key):
        py_zi = py_zoneinfo.ZoneInfo(key)
        c_zi = c_zoneinfo.ZoneInfo(key)

        with test_support.set_zoneinfo_module(py_zoneinfo):
            py_pkl = pickle.dumps(py_zi)

        with test_support.set_zoneinfo_module(c_zoneinfo):
            c_pkl = pickle.dumps(c_zi)

        with test_support.set_zoneinfo_module(c_zoneinfo):
            
            py_to_c_zi = pickle.loads(py_pkl)
            self.assertIs(py_to_c_zi, c_zi)

        with test_support.set_zoneinfo_module(py_zoneinfo):
            
            c_to_py_zi = pickle.loads(c_pkl)
            self.assertIs(c_to_py_zi, py_zi)