0
# Testing Utilities
1
2
Time manipulation utilities for deterministic testing. Pendulum provides comprehensive testing support including freezing time, traveling to specific moments, and managing time flow for reproducible tests.
3
4
## Capabilities
5
6
### Time Freezing
7
8
Functions for freezing time at specific moments for deterministic testing.
9
10
```python { .api }
11
def freeze() -> None:
12
"""
13
Freeze time at current moment.
14
15
All subsequent calls to now(), today(), etc. will return the same time
16
until travel_back() is called or time is unfrozen.
17
"""
18
19
def freeze(dt: DateTime) -> None:
20
"""
21
Freeze time at specific DateTime.
22
23
Parameters:
24
- dt: DateTime to freeze time at
25
"""
26
```
27
28
### Time Travel
29
30
Functions for moving through time during tests.
31
32
```python { .api }
33
def travel(
34
years: int = 0,
35
months: int = 0,
36
weeks: int = 0,
37
days: int = 0,
38
hours: int = 0,
39
minutes: int = 0,
40
seconds: int = 0,
41
microseconds: int = 0
42
) -> None:
43
"""
44
Travel forward or backward in time by specified duration.
45
46
Parameters:
47
- years: Years to travel (positive for future, negative for past)
48
- months: Months to travel
49
- weeks: Weeks to travel
50
- days: Days to travel
51
- hours: Hours to travel
52
- minutes: Minutes to travel
53
- seconds: Seconds to travel
54
- microseconds: Microseconds to travel
55
56
Note: Time continues to flow normally after traveling
57
"""
58
59
def travel_to(dt: DateTime, freeze: bool = False) -> None:
60
"""
61
Travel to specific DateTime.
62
63
Parameters:
64
- dt: Target DateTime to travel to
65
- freeze: Whether to freeze time at the target DateTime
66
"""
67
68
def travel_back() -> None:
69
"""
70
Return to real time.
71
72
Unfreezes time and restores normal time flow.
73
"""
74
```
75
76
### Traveller Class
77
78
The underlying time manipulation class that provides testing functionality.
79
80
```python { .api }
81
class Traveller:
82
def __init__(self, datetime_class: type[DateTime]):
83
"""
84
Initialize Traveller for specific DateTime class.
85
86
Parameters:
87
- datetime_class: DateTime class to control
88
"""
89
90
def freeze(self, dt: DateTime | None = None) -> None:
91
"""
92
Freeze time at specific DateTime or current time.
93
94
Parameters:
95
- dt: DateTime to freeze at (None for current time)
96
"""
97
98
def travel(
99
self,
100
years: int = 0,
101
months: int = 0,
102
weeks: int = 0,
103
days: int = 0,
104
hours: int = 0,
105
minutes: int = 0,
106
seconds: int = 0,
107
microseconds: int = 0
108
) -> None:
109
"""Travel by specified duration"""
110
111
def travel_to(self, dt: DateTime, freeze: bool = False) -> None:
112
"""Travel to specific DateTime"""
113
114
def travel_back(self) -> None:
115
"""Return to real time"""
116
```
117
118
## Usage Examples
119
120
### Basic Time Freezing
121
122
```python
123
import pendulum
124
import time
125
126
# Freeze time at current moment
127
print("Before freeze:", pendulum.now())
128
pendulum.freeze()
129
130
# Time is now frozen
131
time.sleep(2) # Wait 2 seconds
132
print("After 2 seconds:", pendulum.now()) # Same time as before
133
134
# All time functions return frozen time
135
print("Today:", pendulum.today())
136
print("Now:", pendulum.now())
137
print("Now UTC:", pendulum.now('UTC'))
138
139
# Unfreeze time
140
pendulum.travel_back()
141
print("After unfreeze:", pendulum.now()) # Real current time
142
```
143
144
### Freezing at Specific Time
145
146
```python
147
import pendulum
148
149
# Freeze at specific date/time
150
target_dt = pendulum.datetime(2024, 12, 25, 10, 30, 0, tz='UTC')
151
pendulum.freeze(target_dt)
152
153
print("Frozen at:", pendulum.now()) # 2024-12-25T10:30:00+00:00
154
print("Today:", pendulum.today()) # 2024-12-25T00:00:00+00:00
155
156
# All timezone-aware functions work with frozen time
157
print("Paris time:", pendulum.now('Europe/Paris'))
158
print("Tokyo time:", pendulum.now('Asia/Tokyo'))
159
160
pendulum.travel_back()
161
```
162
163
### Time Travel
164
165
```python
166
import pendulum
167
168
print("Real time:", pendulum.now())
169
170
# Travel to the future
171
pendulum.travel(days=7, hours=3)
172
print("Future time:", pendulum.now()) # 7 days and 3 hours later
173
174
# Travel to the past
175
pendulum.travel(days=-14) # 14 days ago from current position
176
print("Past time:", pendulum.now())
177
178
# Travel back to real time
179
pendulum.travel_back()
180
print("Back to real time:", pendulum.now())
181
```
182
183
### Travel to Specific DateTime
184
185
```python
186
import pendulum
187
188
# Travel to specific moment
189
birthday = pendulum.datetime(2024, 6, 15, 12, 0, 0)
190
pendulum.travel_to(birthday)
191
192
print("Traveled to:", pendulum.now())
193
print("Is birthday:", pendulum.now().format('MMMM Do, YYYY'))
194
195
# Travel and freeze
196
new_year = pendulum.datetime(2025, 1, 1, 0, 0, 0)
197
pendulum.travel_to(new_year, freeze=True)
198
199
print("New Year (frozen):", pendulum.now())
200
# Time is now frozen at New Year
201
202
pendulum.travel_back()
203
```
204
205
### Testing Date-Dependent Code
206
207
```python
208
import pendulum
209
210
def get_age(birth_date):
211
"""Calculate age based on current date"""
212
today = pendulum.today()
213
return today.year - birth_date.year - (
214
(today.month, today.day) < (birth_date.month, birth_date.day)
215
)
216
217
# Test age calculation
218
birth_date = pendulum.date(1990, 3, 15)
219
220
# Test before birthday in 2024
221
pendulum.travel_to(pendulum.datetime(2024, 3, 10))
222
age_before = get_age(birth_date)
223
print(f"Age before birthday: {age_before}") # 33
224
225
# Test after birthday in 2024
226
pendulum.travel_to(pendulum.datetime(2024, 3, 20))
227
age_after = get_age(birth_date)
228
print(f"Age after birthday: {age_after}") # 34
229
230
pendulum.travel_back()
231
```
232
233
### Testing Time-Sensitive Operations
234
235
```python
236
import pendulum
237
238
def is_business_hours():
239
"""Check if current time is during business hours (9 AM - 5 PM, Mon-Fri)"""
240
now = pendulum.now('America/New_York')
241
242
# Check if weekday (Monday=0, Friday=4)
243
if now.day_of_week > 4: # Saturday=5, Sunday=6
244
return False
245
246
# Check if between 9 AM and 5 PM
247
return 9 <= now.hour < 17
248
249
# Test during business hours
250
business_time = pendulum.datetime(2024, 3, 15, 14, 30, tz='America/New_York') # Friday 2:30 PM
251
pendulum.travel_to(business_time)
252
print(f"Business hours: {is_business_hours()}") # True
253
254
# Test outside business hours
255
weekend_time = pendulum.datetime(2024, 3, 16, 14, 30, tz='America/New_York') # Saturday 2:30 PM
256
pendulum.travel_to(weekend_time)
257
print(f"Weekend: {is_business_hours()}") # False
258
259
# Test after hours
260
evening_time = pendulum.datetime(2024, 3, 15, 18, 30, tz='America/New_York') # Friday 6:30 PM
261
pendulum.travel_to(evening_time)
262
print(f"After hours: {is_business_hours()}") # False
263
264
pendulum.travel_back()
265
```
266
267
### Testing with Context Managers
268
269
```python
270
import pendulum
271
from contextlib import contextmanager
272
273
@contextmanager
274
def frozen_time(dt):
275
"""Context manager for temporarily freezing time"""
276
pendulum.freeze(dt)
277
try:
278
yield
279
finally:
280
pendulum.travel_back()
281
282
@contextmanager
283
def time_travel(dt):
284
"""Context manager for temporarily traveling to specific time"""
285
pendulum.travel_to(dt)
286
try:
287
yield
288
finally:
289
pendulum.travel_back()
290
291
# Use with context managers
292
test_time = pendulum.datetime(2024, 7, 4, 12, 0, 0)
293
294
with frozen_time(test_time):
295
print("Inside frozen context:", pendulum.now())
296
# Time is frozen at test_time
297
298
print("Outside context:", pendulum.now()) # Real time
299
300
with time_travel(test_time):
301
print("Inside travel context:", pendulum.now())
302
# Time continues flowing from test_time
303
304
print("Back to real time:", pendulum.now())
305
```
306
307
### Testing Date Arithmetic
308
309
```python
310
import pendulum
311
312
def days_until_next_friday():
313
"""Calculate days until next Friday"""
314
today = pendulum.today()
315
days_ahead = 4 - today.day_of_week # Friday is day 4
316
if days_ahead <= 0: # Today is Friday or later
317
days_ahead += 7
318
return days_ahead
319
320
# Test from different days of the week
321
test_cases = [
322
("Monday", pendulum.date(2024, 3, 11)), # Monday
323
("Wednesday", pendulum.date(2024, 3, 13)), # Wednesday
324
("Friday", pendulum.date(2024, 3, 15)), # Friday
325
("Sunday", pendulum.date(2024, 3, 17)), # Sunday
326
]
327
328
for day_name, test_date in test_cases:
329
pendulum.travel_to(pendulum.datetime(test_date.year, test_date.month, test_date.day))
330
days = days_until_next_friday()
331
print(f"{day_name}: {days} days until Friday")
332
333
pendulum.travel_back()
334
```
335
336
### Testing Timezone-Dependent Code
337
338
```python
339
import pendulum
340
341
def get_market_status():
342
"""Get stock market status based on NYC time"""
343
ny_time = pendulum.now('America/New_York')
344
345
# Market closed on weekends
346
if ny_time.day_of_week > 4:
347
return "Closed (Weekend)"
348
349
# Market hours: 9:30 AM - 4:00 PM EST/EDT
350
if ny_time.time() < pendulum.time(9, 30):
351
return "Pre-market"
352
elif ny_time.time() < pendulum.time(16, 0):
353
return "Open"
354
else:
355
return "After-hours"
356
357
# Test during different times
358
test_times = [
359
("Pre-market", pendulum.datetime(2024, 3, 15, 8, 0, tz='America/New_York')),
360
("Market open", pendulum.datetime(2024, 3, 15, 12, 0, tz='America/New_York')),
361
("After hours", pendulum.datetime(2024, 3, 15, 18, 0, tz='America/New_York')),
362
("Weekend", pendulum.datetime(2024, 3, 16, 12, 0, tz='America/New_York')),
363
]
364
365
for description, test_time in test_times:
366
pendulum.travel_to(test_time)
367
status = get_market_status()
368
print(f"{description}: {status}")
369
370
pendulum.travel_back()
371
```
372
373
### Unit Testing Integration
374
375
```python
376
import pendulum
377
import unittest
378
379
class TestTimeDependent(unittest.TestCase):
380
def setUp(self):
381
"""Set up test with known time"""
382
self.test_time = pendulum.datetime(2024, 3, 15, 10, 30, 0)
383
pendulum.freeze(self.test_time)
384
385
def tearDown(self):
386
"""Restore real time after each test"""
387
pendulum.travel_back()
388
389
def test_current_time(self):
390
"""Test that time is frozen during test"""
391
now1 = pendulum.now()
392
now2 = pendulum.now()
393
self.assertEqual(now1, now2)
394
self.assertEqual(now1, self.test_time)
395
396
def test_date_calculation(self):
397
"""Test date calculations with frozen time"""
398
today = pendulum.today()
399
expected = pendulum.date(2024, 3, 15)
400
self.assertEqual(today.date(), expected)
401
402
tomorrow = today.add(days=1)
403
expected_tomorrow = pendulum.date(2024, 3, 16)
404
self.assertEqual(tomorrow.date(), expected_tomorrow)
405
406
# Running the tests would work with frozen time
407
```