Skip to content

Commit f48ff0a

Browse files
authored
Add support for datetime destinations with tzinfo=datetime.UTC (#502)
1 parent babef2c commit f48ff0a

4 files changed

Lines changed: 46 additions & 2 deletions

File tree

docs/changelog.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ Unreleased
1111

1212
Thanks to Javier Buzzi in `PR #531 <https://github.com/adamchainz/time-machine/pull/531>`__.
1313

14+
* Add support to ``travel()`` for ``datetime`` destinations with ``tzinfo`` set to ``datetime.UTC`` (``datetime.timezone.utc``).
15+
16+
Thanks to Lawrence Law in `PR #502 <https://github.com/adamchainz/time-machine/pull/502>`__.
17+
1418
* Prevent segmentation faults in unlikely scenarios, such as if the ``time_machine`` module cannot be imported.
1519

1620
2.17.0 (2025-08-05)

docs/usage.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ It may be:
2828

2929
* A ``datetime.datetime``.
3030
If it is naive, it will be assumed to have the UTC timezone.
31-
If it has ``tzinfo`` set to a |zoneinfo-instance|_, the current timezone will also be mocked.
31+
If it has ``tzinfo`` set to a |zoneinfo-instance|_ or |datetime.UTC|_, the current timezone will also be mocked.
3232
* A ``datetime.date``.
3333
This will be converted to a UTC datetime with the time 00:00:00.
3434
* A ``datetime.timedelta``.
@@ -40,6 +40,8 @@ It may be:
4040

4141
.. |zoneinfo-instance| replace:: ``zoneinfo.ZoneInfo`` instance
4242
.. _zoneinfo-instance: https://docs.python.org/3/library/zoneinfo.html#zoneinfo.ZoneInfo
43+
.. |datetime.UTC| replace:: ``datetime.UTC`` (``datetime.timezone.utc``)
44+
.. _datetime.UTC: https://docs.python.org/3/library/datetime.html#datetime.UTC
4345

4446
Additionally, you can provide some more complex types:
4547

src/time_machine/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,9 @@ def extract_timestamp_tzname(
102102
elif isinstance(dest, dt.datetime):
103103
if isinstance(dest.tzinfo, ZoneInfo):
104104
tzname = dest.tzinfo.key
105-
if dest.tzinfo is None:
105+
elif dest.tzinfo == dt.timezone.utc:
106+
tzname = "UTC"
107+
elif dest.tzinfo is None:
106108
dest = dest.replace(tzinfo=dt.timezone.utc)
107109
timestamp = dest.timestamp()
108110
elif isinstance(dest, dt.timedelta):

tests/test_time_machine.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,42 @@ def test_destination_datetime_tzinfo_zoneinfo_no_orig_tz():
434434
assert time.tzname == orig_tzname
435435

436436

437+
def test_destination_datetime_tzinfo_zoneinfo_utc_no_orig_tz():
438+
with change_local_timezone(None):
439+
orig_tzname = time.tzname
440+
dest = LIBRARY_EPOCH_DATETIME.replace(tzinfo=ZoneInfo("UTC"))
441+
442+
with time_machine.travel(dest):
443+
assert time.tzname == ("UTC", "UTC")
444+
445+
assert time.tzname == orig_tzname
446+
447+
448+
def test_destination_datetime_tzinfo_datetime_timezone_utc_no_orig_tz():
449+
with change_local_timezone(None):
450+
orig_tzname = time.tzname
451+
dest = LIBRARY_EPOCH_DATETIME.replace(tzinfo=dt.timezone.utc)
452+
453+
with time_machine.travel(dest):
454+
assert time.tzname == ("UTC", "UTC")
455+
456+
assert time.tzname == orig_tzname
457+
458+
459+
@pytest.mark.skipif(
460+
sys.version_info < (3, 11), reason="datetime.UTC was introduced in Python 3.11"
461+
)
462+
def test_destination_datetime_tzinfo_datetime_utc_no_orig_tz():
463+
with change_local_timezone(None):
464+
orig_tzname = time.tzname
465+
dest = LIBRARY_EPOCH_DATETIME.replace(tzinfo=dt.UTC)
466+
467+
with time_machine.travel(dest):
468+
assert time.tzname == ("UTC", "UTC")
469+
470+
assert time.tzname == orig_tzname
471+
472+
437473
def test_destination_datetime_tzinfo_zoneinfo_windows():
438474
orig_timezone = time.timezone
439475

0 commit comments

Comments
 (0)