or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli-migration.mdcore-time-travel.mdescape-hatch.mdindex.mdpytest-integration.md

core-time-travel.mddocs/

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

```