0
# Core Time Travel
1
2
The core time travel functionality enables deterministic testing of time-dependent code by mocking Python's time and datetime functions. The system supports multiple destination formats, nested time travel, and both static and advancing time modes.
3
4
## Capabilities
5
6
### Travel Class
7
8
The primary interface for time travel, usable as a context manager, decorator, or imperative controller. Supports various destination formats and tick modes for different testing scenarios.
9
10
```python { .api }
11
class travel:
12
def __init__(self, destination: DestinationType, *, tick: bool = True):
13
"""
14
Initialize time travel to a specific destination.
15
16
Parameters:
17
- destination: Target time (timestamp, datetime, string, etc.)
18
- tick: Whether time advances during travel (default: True)
19
"""
20
21
def start(self) -> Coordinates:
22
"""
23
Start time travel and return coordinates for manipulation.
24
25
Returns:
26
Coordinates object for time manipulation during travel
27
"""
28
29
def stop(self) -> None:
30
"""Stop time travel and restore real time."""
31
32
def __enter__(self) -> Coordinates:
33
"""Context manager entry - starts time travel."""
34
35
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
36
"""Context manager exit - stops time travel."""
37
38
async def __aenter__(self) -> Coordinates:
39
"""Async context manager entry - starts time travel."""
40
41
async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
42
"""Async context manager exit - stops time travel."""
43
44
def __call__(self, wrapped):
45
"""
46
Decorator functionality for functions, async functions, and TestCase classes.
47
48
Parameters:
49
- wrapped: Function, async function, or unittest.TestCase subclass
50
51
Returns:
52
Decorated function or class with time travel applied
53
"""
54
```
55
56
Usage examples:
57
58
```python
59
import time_machine
60
from datetime import datetime, timezone, timedelta
61
62
# Context manager with datetime object
63
with time_machine.travel(datetime(2023, 1, 1, tzinfo=timezone.utc)):
64
print(datetime.now()) # 2023-01-01 00:00:00+00:00
65
66
# Context manager with timestamp
67
with time_machine.travel(0): # Unix epoch
68
print(datetime.now()) # 1970-01-01 00:00:00
69
70
# Context manager with ISO string
71
with time_machine.travel("2023-06-15T10:30:00Z"):
72
print(datetime.now()) # 2023-06-15 10:30:00+00:00
73
74
# Decorator on function
75
@time_machine.travel("2023-01-01")
76
def test_new_year():
77
assert datetime.now().year == 2023
78
79
# Decorator on async function
80
@time_machine.travel(datetime(2023, 1, 1))
81
async def async_test():
82
assert datetime.now().year == 2023
83
84
# Static time mode (tick=False)
85
with time_machine.travel("2023-01-01", tick=False):
86
time1 = datetime.now()
87
time.sleep(0.1) # Won't advance time
88
time2 = datetime.now()
89
assert time1 == time2
90
91
# Advancing time mode (tick=True, default)
92
with time_machine.travel("2023-01-01", tick=True):
93
time1 = datetime.now()
94
time.sleep(0.1) # Time advances
95
time2 = datetime.now()
96
assert time2 > time1
97
```
98
99
### Coordinates Class
100
101
Represents the current time travel state and provides methods for manipulating time during travel. Returned by `travel.start()` and passed to context manager blocks.
102
103
```python { .api }
104
class Coordinates:
105
def time(self) -> float:
106
"""
107
Get current travel time as Unix timestamp.
108
109
Returns:
110
Current time as float seconds since Unix epoch
111
"""
112
113
def time_ns(self) -> int:
114
"""
115
Get current travel time as nanosecond timestamp.
116
117
Returns:
118
Current time as int nanoseconds since Unix epoch
119
"""
120
121
def shift(self, delta: dt.timedelta | int | float) -> None:
122
"""
123
Shift time by a relative amount.
124
125
Parameters:
126
- delta: Time delta as timedelta object, or seconds as int/float
127
"""
128
129
def move_to(self, destination: DestinationType, tick: bool | None = None) -> None:
130
"""
131
Move to a new absolute time destination.
132
133
Parameters:
134
- destination: New time destination (same formats as travel())
135
- tick: Override tick mode for new destination (optional)
136
"""
137
```
138
139
Usage examples:
140
141
```python
142
# Time manipulation with shift()
143
with time_machine.travel("2023-01-01 12:00:00") as traveller:
144
print(datetime.now()) # 2023-01-01 12:00:00
145
146
# Shift forward 1 hour
147
traveller.shift(3600)
148
print(datetime.now()) # 2023-01-01 13:00:00
149
150
# Shift backward 30 minutes using timedelta
151
traveller.shift(timedelta(minutes=-30))
152
print(datetime.now()) # 2023-01-01 12:30:00
153
154
# Time manipulation with move_to()
155
with time_machine.travel(0) as traveller:
156
print(datetime.now()) # 1970-01-01 00:00:00
157
158
# Jump to different date
159
traveller.move_to("2023-12-25 09:00:00")
160
print(datetime.now()) # 2023-12-25 09:00:00
161
162
# Change tick mode during travel
163
traveller.move_to("2023-01-01", tick=False)
164
# Time is now static until travel ends
165
```
166
167
### Utility Functions
168
169
Functions for extracting time information and converting between formats.
170
171
```python { .api }
172
def extract_timestamp_tzname(destination: DestinationType) -> tuple[float, str | None]:
173
"""
174
Extract timestamp and timezone name from a destination.
175
176
Parameters:
177
- destination: Time destination in any supported format
178
179
Returns:
180
Tuple of (timestamp_float, timezone_name_or_none)
181
"""
182
```
183
184
### Nested Time Travel
185
186
Time Machine supports nested time travel with a coordinate stack system:
187
188
```python
189
# Nested time travel
190
with time_machine.travel("2023-01-01"):
191
print(datetime.now().year) # 2023
192
193
with time_machine.travel("2024-06-15"):
194
print(datetime.now().year) # 2024
195
print(datetime.now().month) # 6
196
197
print(datetime.now().year) # Back to 2023
198
```
199
200
### Timezone Handling
201
202
Time Machine supports timezone-aware time travel:
203
204
```python
205
from zoneinfo import ZoneInfo
206
207
# Travel with timezone
208
eastern = ZoneInfo("America/New_York")
209
utc_time = datetime(2023, 6, 15, 16, 0, tzinfo=timezone.utc)
210
eastern_time = utc_time.astimezone(eastern)
211
212
with time_machine.travel(eastern_time):
213
# Environment TZ is set to America/New_York
214
local_time = datetime.now()
215
print(local_time) # Reflects Eastern timezone
216
```
217
218
## Error Handling
219
220
Time Machine validates destination formats and raises appropriate exceptions:
221
222
```python
223
# Invalid destination type
224
try:
225
time_machine.travel([1, 2, 3]) # Lists not supported
226
except TypeError as e:
227
print(f"Unsupported destination: {e}")
228
229
# Invalid string format
230
try:
231
time_machine.travel("not-a-date")
232
except ValueError as e:
233
print(f"Cannot parse date: {e}")
234
```
235
236
### Module-level Time Functions
237
238
Direct access to time-machine's patched time functions. These functions are automatically patched during time travel to return mocked time values.
239
240
```python { .api }
241
def now(tz: dt.tzinfo | None = None) -> dt.datetime:
242
"""
243
Get current datetime during time travel.
244
245
Parameters:
246
- tz: Optional timezone info
247
248
Returns:
249
Current datetime object (mocked during travel)
250
"""
251
252
def utcnow() -> dt.datetime:
253
"""
254
Get current UTC datetime (naive) during time travel.
255
256
Returns:
257
Current UTC datetime without timezone info (mocked during travel)
258
"""
259
260
def clock_gettime(clk_id: int) -> float:
261
"""
262
Get clock time for specified clock ID.
263
264
Parameters:
265
- clk_id: Clock identifier (e.g., time.CLOCK_REALTIME)
266
267
Returns:
268
Clock time as float seconds (mocked for CLOCK_REALTIME during travel)
269
"""
270
271
def clock_gettime_ns(clk_id: int) -> int:
272
"""
273
Get clock time in nanoseconds for specified clock ID.
274
275
Parameters:
276
- clk_id: Clock identifier
277
278
Returns:
279
Clock time as int nanoseconds (mocked for CLOCK_REALTIME during travel)
280
"""
281
282
def gmtime(secs: float | None = None) -> struct_time:
283
"""
284
Convert timestamp to GMT time struct.
285
286
Parameters:
287
- secs: Optional timestamp, uses current travel time if None
288
289
Returns:
290
GMT time as struct_time (mocked during travel)
291
"""
292
293
def localtime(secs: float | None = None) -> struct_time:
294
"""
295
Convert timestamp to local time struct.
296
297
Parameters:
298
- secs: Optional timestamp, uses current travel time if None
299
300
Returns:
301
Local time as struct_time (mocked during travel)
302
"""
303
304
def strftime(format: str, t: tuple | struct_time | None = None) -> str:
305
"""
306
Format time using strftime.
307
308
Parameters:
309
- format: Format string
310
- t: Optional time tuple, uses current travel time if None
311
312
Returns:
313
Formatted time string (mocked during travel)
314
"""
315
316
def time() -> float:
317
"""
318
Get current time as Unix timestamp.
319
320
Returns:
321
Current time as float seconds since Unix epoch (mocked during travel)
322
"""
323
324
def time_ns() -> int:
325
"""
326
Get current time as nanosecond timestamp.
327
328
Returns:
329
Current time as int nanoseconds since Unix epoch (mocked during travel)
330
"""
331
```
332
333
Usage examples:
334
335
```python
336
import time_machine
337
import time
338
from datetime import datetime
339
from time import struct_time
340
341
# Module-level functions are automatically patched during travel
342
with time_machine.travel("2023-01-01 12:00:00"):
343
# These all return mocked time values
344
current_time = time_machine.time() # 1672574400.0 (2023-01-01 12:00:00)
345
current_ns = time_machine.time_ns() # 1672574400000000000
346
current_dt = time_machine.now() # 2023-01-01 12:00:00
347
current_utc = time_machine.utcnow() # 2023-01-01 12:00:00 (naive)
348
349
# Time formatting functions
350
gmt_struct = time_machine.gmtime() # struct_time for 2023-01-01 12:00:00 GMT
351
local_struct = time_machine.localtime() # struct_time for 2023-01-01 12:00:00 local
352
formatted = time_machine.strftime("%Y-%m-%d %H:%M:%S") # "2023-01-01 12:00:00"
353
354
# Clock functions (CLOCK_REALTIME is mocked, others use real values)
355
real_time = time_machine.clock_gettime(time.CLOCK_REALTIME) # Mocked
356
real_time_ns = time_machine.clock_gettime_ns(time.CLOCK_REALTIME) # Mocked
357
```
358
359
## Type Definitions
360
361
```python { .api }
362
from time import struct_time
363
364
DestinationBaseType = Union[
365
int, # Unix timestamp (seconds)
366
float, # Unix timestamp with fractional seconds
367
dt.datetime, # Datetime object (timezone-aware recommended)
368
dt.timedelta, # Relative time from current moment
369
dt.date, # Date object (converted to midnight UTC)
370
str, # ISO 8601 datetime string or parseable date string
371
]
372
373
DestinationType = Union[
374
DestinationBaseType,
375
Callable[[], DestinationBaseType], # Function returning destination
376
Generator[DestinationBaseType, None, None], # Generator yielding destinations
377
]
378
379
_TimeTuple = tuple[int, int, int, int, int, int, int, int, int]
380
```