Repository: adamchainz/time-machine

Issue:
Allow passing dt.timedelta objects to travel()
It would offset the current time by the given amount, which would be pretty useful for some tests that check "X is true until N minutes in the future"

Discussion:
AgDude: @adamchainz I think your concerns have been addressed. Please clarify if I am still missing something.
adamchainz: Yea sure, no need to comment, I’ll get around to reviewing at some point. Busy with life stuff atm.

Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index c524071..71b1598 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -2,6 +2,10 @@
 Changelog
 =========
 
+* Add support for ``datetime.timedelta`` to ``time_machine.travel()``.
+
+  Thanks to Nate Dudenhoeffer in `PR #298 <https://github.com/adamchainz/time-machine/pull/298>`__.
+
 * Add ``shift()`` method to the ``time_machine`` pytest fixture.
 
   Thanks to Stefaan Lippens in `PR #312 <https://github.com/adamchainz/time-machine/pull/312>`__.
diff --git a/README.rst b/README.rst
index 755325c..88db11f 100644
--- a/README.rst
+++ b/README.rst
@@ -77,6 +77,9 @@ It may be:
   If it has ``tzinfo`` set to a |zoneinfo-instance|_, the current timezone will also be mocked.
 * A ``datetime.date``.
   This will be converted to a UTC datetime with the time 00:00:00.
+* A ``datetime.timedelta``.
+  This will be interpreted relative to the current time.
+  If already within a ``travel()`` block, the ``shift()`` method is easier to use (documented below).
 * A ``float`` or ``int`` specifying a `Unix timestamp <https://en.m.wikipedia.org/wiki/Unix_time>`__
 * A string, which will be parsed with `dateutil.parse <https://dateutil.readthedocs.io/en/stable/parser.html>`__ and converted to a timestamp.
   Again, if the result is naive, it will be assumed to have the UTC time zone.
diff --git a/src/time_machine/__init__.py b/src/time_machine/__init__.py
index c0d6bda..4fa2142 100644
--- a/src/time_machine/__init__.py
+++ b/src/time_machine/__init__.py
@@ -84,6 +84,7 @@ DestinationBaseType = Union[
     int,
     float,
     dt.datetime,
+    dt.timedelta,
     dt.date,
     str,
 ]
@@ -124,6 +125,8 @@ def extract_timestamp_tzname(
         if dest.tzinfo is None:
             dest = dest.replace(tzinfo=dt.timezone.utc)
         timestamp = dest.timestamp()
+    elif isinstance(dest, dt.timedelta):
+        timestamp = time() + dest.total_seconds()
     elif isinstance(dest, dt.date):
         timestamp = dt.datetime.combine(
             dest, dt.time(0, 0), tzinfo=dt.timezone.utc


Test patch:
diff --git a/tests/test_time_machine.py b/tests/test_time_machine.py
index 9736be4..0f086bf 100644
--- a/tests/test_time_machine.py
+++ b/tests/test_time_machine.py
@@ -444,6 +444,24 @@ def test_destination_date():
     assert time.time() == EPOCH
 
 
+def test_destination_timedelta():
+    now = time.time()
+    with time_machine.travel(dt.timedelta(seconds=3600)):
+        assert now + 3600 <= time.time() <= now + 3601
+
+
+def test_destination_timedelta_negative():
+    now = time.time()
+    with time_machine.travel(dt.timedelta(seconds=-3600)):
+        assert now - 3600 <= time.time() <= now - 3599
+
+
+def test_destination_timedelta_nested():
+    with time_machine.travel(EPOCH):
+        with time_machine.travel(dt.timedelta(seconds=10)):
+            assert time.time() == EPOCH + 10.0
+
+
 @time_machine.travel("1970-01-01 00:01 +0000")
 def test_destination_string():
     assert time.time() == EPOCH + 60.0