Repository: adamchainz/time-machine

Issue:
Freeze time instead of just shifting it
Hi! This library looks very cool, even more with the added support for pytest. I have a question which might be more related with me not understanding the use cases or purpose than the tool itself.

I thought I would be able to tell the library: everytime I call now I want you to return this. Instead what I found is that when I call the `move_to` method in the time_machine fixture, time goes back to that time but the starts moving forward accurately. What I wanted to be able to do is to always get the same value when utcnow is called so I can test the value of queries that I am generating on the fly.

Is this possible? Thanks!


Discussion:
codecov-commenter: # [Codecov](https://codecov.io/gh/adamchainz/time-machine/pull/152?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Adam+Johnson) Report
> Merging [#152](https://codecov.io/gh/adamchainz/time-machine/pull/152?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Adam+Johnson) (8e13b24) into [main](https://codecov.io/gh/adamchainz/time-machine/commit/665670beddc1f330ce182984bfd1fddeda0587a1?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Adam+Johnson) (665670b) will **decrease** coverage by `4.14%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/adamchainz/time-machine/pull/152/graphs/tree.svg?width=650&height=150&src=pr&token=X0GKFD0HT5&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Adam+Johnson)](https://codecov.io/gh/adamchainz/time-machine/pull/152?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Adam+Johnson)

```diff
@@             Coverage Diff             @@
##              main     #152      +/-   ##
===========================================
- Coverage   100.00%   95.85%   -4.15%     
===========================================
  Files            1        1              
  Lines          237      241       +4     
  Branches        39       41       +2     
===========================================
- Hits           237      231       -6     
- Misses           0        5       +5     
- Partials         0        5       +5     
```


| [Impacted Files](https://codecov.io/gh/adamchainz/time-machine/pull/152?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Adam+Johnson) | Coverage Δ | |
|---|---|---|
| [src/time\_machine/\_\_init\_\_.py](https://codecov.io/gh/adamchainz/time-machine/pull/152/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Adam+Johnson#diff-c3JjL3RpbWVfbWFjaGluZS9fX2luaXRfXy5weQ==) | `95.85% <100.00%> (-4.15%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/adamchainz/time-machine/pull/152?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Adam+Johnson).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Adam+Johnson)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/adamchainz/time-machine/pull/152?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Adam+Johnson). Last update [665670b...8e13b24](https://codecov.io/gh/adamchainz/time-machine/pull/152?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Adam+Johnson). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Adam+Johnson).


Patch:
diff --git a/HISTORY.rst b/HISTORY.rst
index 3265158..5390cf4 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -2,6 +2,10 @@
 History
 =======
 
+* Allow passing ``tick`` to ``Coordinates.move_to()`` and the pytest fixture’s
+  ``time_machine.move_to()``. This allows freezing or unfreezing of time when
+  travelling.
+
 2.2.0 (2021-07-02)
 ------------------
 
diff --git a/README.rst b/README.rst
index c4dc27b..d95ff70 100644
--- a/README.rst
+++ b/README.rst
@@ -235,12 +235,14 @@ Here’s a worked example changing the current timezone:
 The ``start()`` method and entry of the context manager both return a ``Coordinates`` object that corresponds to the given "trip" in time.
 This has a couple methods that can be used to travel to other times.
 
-``move_to(destination)``
-^^^^^^^^^^^^^^^^^^^^^^^^
+``move_to(destination, tick=None)``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 ``move_to()`` moves the current time to a new destination.
 ``destination`` may be any of the types supported by ``travel``.
 
+``tick`` may be set to a boolean, to change the ``tick`` flag of ``travel``.
+
 For example:
 
 .. code-block:: python
diff --git a/src/time_machine/__init__.py b/src/time_machine/__init__.py
index 7f297a9..a8302b8 100644
--- a/src/time_machine/__init__.py
+++ b/src/time_machine/__init__.py
@@ -163,12 +163,18 @@ class Coordinates:
 
         self._destination_timestamp_ns += int(total_seconds * NANOSECONDS_PER_SECOND)
 
-    def move_to(self, destination: DestinationType) -> None:
+    def move_to(
+        self,
+        destination: DestinationType,
+        tick: Optional[bool] = None,
+    ) -> None:
         self._stop()
         timestamp, self._destination_tzname = extract_timestamp_tzname(destination)
         self._destination_timestamp_ns = int(timestamp * NANOSECONDS_PER_SECOND)
         self._requested = False
         self._start()
+        if tick is not None:
+            self._tick = tick
 
     def _start(self) -> None:
         if HAVE_TZSET and self._destination_tzname is not None:
@@ -421,13 +427,19 @@ if pytest is not None:  # pragma: no branch
             self.traveller = None
             self.coordinates = None
 
-        def move_to(self, destination: DestinationType) -> None:
+        def move_to(
+            self,
+            destination: DestinationType,
+            tick: Optional[bool] = None,
+        ) -> None:
             if self.traveller is None:
-                self.traveller = travel(destination)
+                if tick is None:
+                    tick = True
+                self.traveller = travel(destination, tick=tick)
                 self.coordinates = self.traveller.start()
             else:
                 assert self.coordinates is not None
-                self.coordinates.move_to(destination)
+                self.coordinates.move_to(destination, tick=tick)
 
         def stop(self) -> None:
             if self.traveller is not None:


Test patch:
diff --git a/tests/test_time_machine.py b/tests/test_time_machine.py
index ccf3862..175a7d5 100644
--- a/tests/test_time_machine.py
+++ b/tests/test_time_machine.py
@@ -633,6 +633,20 @@ def test_move_to_datetime_with_tzinfo_zoneinfo():
     assert time.daylight == orig_daylight
 
 
+def test_move_to_datetime_change_tick_on():
+    with time_machine.travel(EPOCH, tick=False) as traveller:
+        traveller.move_to(EPOCH_PLUS_ONE_YEAR_DATETIME, tick=True)
+        assert time.time() == EPOCH_PLUS_ONE_YEAR
+        assert time.time() > EPOCH_PLUS_ONE_YEAR
+
+
+def test_move_to_datetime_change_tick_off():
+    with time_machine.travel(EPOCH, tick=True) as traveller:
+        traveller.move_to(EPOCH_PLUS_ONE_YEAR_DATETIME, tick=False)
+        assert time.time() == EPOCH_PLUS_ONE_YEAR
+        assert time.time() == EPOCH_PLUS_ONE_YEAR
+
+
 # uuid tests
 
 
@@ -664,6 +678,19 @@ def test_fixture_used(time_machine):
     assert time.time() == EPOCH
 
 
+def test_fixture_used_tick_false(time_machine):
+    time_machine.move_to(EPOCH, tick=False)
+    assert time.time() == EPOCH
+    assert time.time() == EPOCH
+
+
+def test_fixture_used_tick_true(time_machine):
+    time_machine.move_to(EPOCH, tick=True)
+    original = time.time()
+    assert original == EPOCH
+    assert original < time.time() < EPOCH + 10.0
+
+
 def test_fixture_used_twice(time_machine):
     time_machine.move_to(EPOCH)
     assert time.time() == EPOCH