feat: add "complete" timezone support
at least for now, this is all we need i think.. if nothing configured, will fallback to default system local timezone. or can configure the default, or alternate(s) as needed. some day when we drop support for python 3.8, can also remove the python-dateutil dependency..
This commit is contained in:
parent
dac91406c7
commit
0ffc72f766
6 changed files with 396 additions and 87 deletions
|
|
@ -165,49 +165,122 @@ class TestLoadObject(TestCase):
|
|||
self.assertIs(result, TestCase)
|
||||
|
||||
|
||||
class TestGetTimezoneByName(TestCase):
|
||||
|
||||
def test_modern(self):
|
||||
try:
|
||||
import zoneinfo
|
||||
except ImportError:
|
||||
self.assertLess(sys.version_info, (3, 9))
|
||||
pytest.skip("this test is not relevant before python 3.9")
|
||||
|
||||
tz = mod.get_timezone_by_name("America/Chicago")
|
||||
self.assertIsInstance(tz, zoneinfo.ZoneInfo)
|
||||
self.assertIsInstance(tz, datetime.tzinfo)
|
||||
self.assertEqual(tz.key, "America/Chicago")
|
||||
|
||||
def test_legacy(self):
|
||||
import dateutil.tz
|
||||
|
||||
orig_import = __import__
|
||||
|
||||
def mock_import(name, *args, **kwargs):
|
||||
if name == "zoneinfo":
|
||||
raise ImportError
|
||||
return orig_import(name, *args, **kwargs)
|
||||
|
||||
with patch("builtins.__import__", side_effect=mock_import):
|
||||
tz = mod.get_timezone_by_name("America/Chicago")
|
||||
self.assertIsInstance(tz, dateutil.tz.tzfile)
|
||||
self.assertIsInstance(tz, datetime.tzinfo)
|
||||
dt = datetime.datetime.now(tz)
|
||||
self.assertIn(dt.tzname(), ["CDT", "CST"])
|
||||
|
||||
|
||||
class TestLocaltime(TestCase):
|
||||
|
||||
def test_naive_utc(self):
|
||||
# nb. must override local_zone for test consistency
|
||||
tz = datetime.timezone(-datetime.timedelta(hours=5))
|
||||
dt = datetime.datetime(2025, 12, 16, 0, 16) # utc
|
||||
result = mod.localtime(dt, local_zone=tz)
|
||||
self.assertIsInstance(result, datetime.datetime)
|
||||
self.assertIs(result.tzinfo, tz)
|
||||
self.assertEqual(result, datetime.datetime(2025, 12, 15, 19, 16, tzinfo=tz))
|
||||
|
||||
def test_naive_local(self):
|
||||
# nb. must override local_zone for test consistency
|
||||
tz = datetime.timezone(-datetime.timedelta(hours=5))
|
||||
dt = datetime.datetime(2025, 12, 15, 19, 16) # local
|
||||
|
||||
# can test precisely when overriding local_zone
|
||||
result = mod.localtime(dt, local_zone=tz, from_utc=False)
|
||||
self.assertIsInstance(result, datetime.datetime)
|
||||
self.assertIs(result.tzinfo, tz)
|
||||
self.assertEqual(result, datetime.datetime(2025, 12, 15, 19, 16, tzinfo=tz))
|
||||
|
||||
# must test loosely for fallback to system local timezone
|
||||
result = mod.localtime(dt, from_utc=False)
|
||||
self.assertIsInstance(result, datetime.datetime)
|
||||
self.assertIsInstance(result.tzinfo, datetime.tzinfo)
|
||||
self.assertEqual(result.year, 2025)
|
||||
self.assertEqual(result.month, 12)
|
||||
|
||||
def test_aware_utc(self):
|
||||
# nb. must override local_zone for test consistency
|
||||
tz = datetime.timezone(-datetime.timedelta(hours=5))
|
||||
dt = datetime.datetime(2025, 12, 16, 0, 16, tzinfo=datetime.timezone.utc)
|
||||
result = mod.localtime(dt, local_zone=tz)
|
||||
self.assertIsInstance(result, datetime.datetime)
|
||||
self.assertIs(result.tzinfo, tz)
|
||||
self.assertEqual(result, datetime.datetime(2025, 12, 15, 19, 16, tzinfo=tz))
|
||||
|
||||
def test_aware_local(self):
|
||||
# nb. must override local_zone for test consistency
|
||||
tz = datetime.timezone(-datetime.timedelta(hours=5))
|
||||
other = datetime.timezone(-datetime.timedelta(hours=7))
|
||||
dt = datetime.datetime(2025, 12, 15, 17, 16, tzinfo=other)
|
||||
|
||||
# can test precisely when overriding local_zone
|
||||
result = mod.localtime(dt, local_zone=tz)
|
||||
self.assertIsInstance(result, datetime.datetime)
|
||||
self.assertIs(result.tzinfo, tz)
|
||||
self.assertEqual(result, datetime.datetime(2025, 12, 15, 19, 16, tzinfo=tz))
|
||||
|
||||
# must test loosely for fallback to system local timezone
|
||||
result = mod.localtime(dt)
|
||||
self.assertIsInstance(result, datetime.datetime)
|
||||
self.assertIsInstance(result.tzinfo, datetime.tzinfo)
|
||||
self.assertEqual(result.year, 2025)
|
||||
self.assertEqual(result.month, 12)
|
||||
|
||||
def test_current_time(self):
|
||||
tz = datetime.timezone(-datetime.timedelta(hours=5))
|
||||
|
||||
# has tzinfo by default
|
||||
dt = mod.localtime()
|
||||
self.assertIsInstance(dt, datetime.datetime)
|
||||
self.assertIsNotNone(dt.tzinfo)
|
||||
now = datetime.datetime.now()
|
||||
self.assertAlmostEqual(int(dt.timestamp()), int(now.timestamp()))
|
||||
# overriding local_zone
|
||||
result = mod.localtime(local_zone=tz)
|
||||
self.assertIsInstance(result, datetime.datetime)
|
||||
self.assertIs(result.tzinfo, tz)
|
||||
|
||||
# fallback to system local timezone
|
||||
result = mod.localtime()
|
||||
self.assertIsInstance(result, datetime.datetime)
|
||||
self.assertIsInstance(result.tzinfo, datetime.tzinfo)
|
||||
self.assertIsNot(result.tzinfo, tz)
|
||||
|
||||
def test_want_tzinfo(self):
|
||||
|
||||
# wants tzinfo implicitly
|
||||
result = mod.localtime()
|
||||
self.assertIsInstance(result.tzinfo, datetime.tzinfo)
|
||||
|
||||
# wants tzinfo explicitly
|
||||
result = mod.localtime(want_tzinfo=True)
|
||||
self.assertIsInstance(result.tzinfo, datetime.tzinfo)
|
||||
|
||||
# no tzinfo
|
||||
dt = mod.localtime(tzinfo=False)
|
||||
self.assertIsInstance(dt, datetime.datetime)
|
||||
self.assertIsNone(dt.tzinfo)
|
||||
now = datetime.datetime.now()
|
||||
self.assertAlmostEqual(int(dt.timestamp()), int(now.timestamp()))
|
||||
|
||||
def test_convert_with_tzinfo(self):
|
||||
sample = datetime.datetime(2024, 9, 15, 13, 30, tzinfo=datetime.timezone.utc)
|
||||
|
||||
# has tzinfo by default
|
||||
dt = mod.localtime(sample)
|
||||
self.assertIsInstance(dt, datetime.datetime)
|
||||
self.assertIsNotNone(dt.tzinfo)
|
||||
|
||||
# no tzinfo
|
||||
dt = mod.localtime(sample, tzinfo=False)
|
||||
self.assertIsInstance(dt, datetime.datetime)
|
||||
self.assertIsNone(dt.tzinfo)
|
||||
|
||||
def test_convert_without_tzinfo(self):
|
||||
sample = datetime.datetime(2024, 9, 15, 13, 30)
|
||||
|
||||
# has tzinfo by default
|
||||
dt = mod.localtime(sample)
|
||||
self.assertIsInstance(dt, datetime.datetime)
|
||||
self.assertIsNotNone(dt.tzinfo)
|
||||
|
||||
# no tzinfo
|
||||
dt = mod.localtime(sample, tzinfo=False)
|
||||
self.assertIsInstance(dt, datetime.datetime)
|
||||
self.assertIsNone(dt.tzinfo)
|
||||
result = mod.localtime(want_tzinfo=False)
|
||||
self.assertIsNone(result.tzinfo)
|
||||
|
||||
|
||||
class TestMakeUTC(TestCase):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue