1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
/*!
Implements [TZif] time zone parsing and transition handling.
The TZif format is used by the [Time Zone Database].
The parser in this module is designed to handle untrusted input. That is, there
is no input that should cause it to panic or allocate memory in a way that is
not proportional to the size of the TZif data.
These binary files are the ones commonly found in Unix distributions in the
`/usr/share/zoneinfo` directory.
[Time Zone Database]: https://www.iana.org/time-zones
[TZif]: https://datatracker.ietf.org/doc/rfc9636/
*/
use crate::;
pub use ParseError;
/// A representation of a possibly named time zone backed by [TZif] data.
///
/// It is useful to represent a named time zone as distinct from the time zone
/// itself because a name is not inherently part of the TZif data. Indeed, there
/// are a couple reasons why it's important to separate the name:
///
/// * It's possible that no meaningful name exists. For example, a
/// `/etc/localtime` file with no obvious mapping to a name. (e.g., No symlink
/// and no discoverable Time Zone Database.)
/// * Some IANA time zone identifiers correspond to different regions, but
/// share the same TZif data.
///
/// [TZif]: https://datatracker.ietf.org/doc/rfc9636/
/// A representation of an unnamed time zone backed by [TZif] data.
///
/// Two time zones are considered equivalent when their CRC32 sums are
/// equivalent.
///
/// [TZif]: https://datatracker.ietf.org/doc/rfc9636/
// This ensures the alignment of this type is always *at least* 8 bytes. This
// is required for the pointer tagging inside of `TimeZone` to be sound. At
// time of writing (2024-02-24), this explicit `repr` isn't required on 64-bit
// systems since the type definition is such that it will have an alignment of
// at least 8 bytes anyway. But this *is* required for 32-bit systems, where
// the type definition at present only has an alignment of 4 bytes.
/// A "local time type" from TZif data.
///
/// This is referenced by time zone transitions. It may be used by one or
/// more time zone transitions. This contains information about whether
/// the transition moves into DST, its time zone abbreviation, and most
/// importantly, the offset.
/// The possible indicator values for standard/wall and UT/local.
///
/// Their purpose, as of 2026-07-06, is unknown to Jiff's author. But they are
/// represented here for completeness.
// Note that UT+Wall is not allowed.
//
// I honestly have no earthly clue what they mean. I've read the section about
// them in RFC 8536 several times and I can't make sense of it. I've even
// looked at data files that have these set and still can't make sense of
// them. I've even looked at what other datetime libraries do with these, and
// they all seem to just ignore them. Like, WTF. I've spent the last couple
// months of my life steeped in time, and I just cannot figure this out. Am I
// just dumb?
//
// Anyway, we parse them, but otherwise ignore them because that's what all
// the cool kids do.
//
// The default is `LocalWall`, which also occurs when no indicators are
// present.
//
// I tried again and still don't get it. Here's a dump for `Pacific/Honolulu`:
//
// ```text
// $ ./scripts/jiff-debug tzif /usr/share/zoneinfo/Pacific/Honolulu
// TIME ZONE NAME
// /usr/share/zoneinfo/Pacific/Honolulu
// LOCAL TIME TYPES
// 000: offset=-10:31:26, is_dst=false, designation=LMT, indicator=local/wall
// 001: offset=-10:30, is_dst=false, designation=HST, indicator=local/wall
// 002: offset=-09:30, is_dst=true, designation=HDT, indicator=local/wall
// 003: offset=-09:30, is_dst=true, designation=HWT, indicator=local/wall
// 004: offset=-09:30, is_dst=true, designation=HPT, indicator=ut/std
// 005: offset=-10, is_dst=false, designation=HST, indicator=local/wall
// TRANSITIONS
// 0000: -9999-01-02T01:59:59 :: -377705023201 :: type=0, -10:31:26, is_dst=false, LMT, local/wall
// 0001: 1896-01-13T22:31:26 :: -2334101314 :: type=1, -10:30, is_dst=false, HST, local/wall
// 0002: 1933-04-30T12:30:00 :: -1157283000 :: type=2, -09:30, is_dst=true, HDT, local/wall
// 0003: 1933-05-21T21:30:00 :: -1155436200 :: type=1, -10:30, is_dst=false, HST, local/wall
// 0004: 1942-02-09T12:30:00 :: -880198200 :: type=3, -09:30, is_dst=true, HWT, local/wall
// 0005: 1945-08-14T23:00:00 :: -769395600 :: type=4, -09:30, is_dst=true, HPT, ut/std
// 0006: 1945-09-30T11:30:00 :: -765376200 :: type=1, -10:30, is_dst=false, HST, local/wall
// 0007: 1947-06-08T12:30:00 :: -712150200 :: type=5, -10, is_dst=false, HST, local/wall
// POSIX TIME ZONE STRING
// HST10
// ```
//
// See how type 004 has a ut/std indicator? What the fuck does that mean?
// All transitions are defined in terms of UTC. I confirmed this with `zdump`:
//
// ```text
// $ zdump -v Pacific/Honolulu | rg 1945
// Pacific/Honolulu Tue Aug 14 22:59:59 1945 UT = Tue Aug 14 13:29:59 1945 HWT isdst=1 gmtoff=-34200
// Pacific/Honolulu Tue Aug 14 23:00:00 1945 UT = Tue Aug 14 13:30:00 1945 HPT isdst=1 gmtoff=-34200
// Pacific/Honolulu Sun Sep 30 11:29:59 1945 UT = Sun Sep 30 01:59:59 1945 HPT isdst=1 gmtoff=-34200
// Pacific/Honolulu Sun Sep 30 11:30:00 1945 UT = Sun Sep 30 01:00:00 1945 HST isdst=0 gmtoff=-37800
// ```
//
// The times match up. All of them. The indicators don't seem to make a
// difference. I'm clearly missing something.
/// The set of transitions in TZif data, laid out in column orientation.
///
/// The column orientation is used to make TZ lookups faster. Specifically,
/// for finding an offset for a timestamp, we do a binary search on
/// `timestamps`. For finding an offset for a local datetime, we do a binary
/// search on `civil_starts`. By making these two distinct sequences with
/// nothing else in them, we make them as small as possible and thus improve
/// cache locality.
///
/// All sequences in this type are in correspondence with one another. They
/// are all guaranteed to have the same length.
/// TZif transition info beyond the timestamp and civil datetime.
///
/// For example, this contains a transition's "local type index," which in
/// turn gives access to the offset (among other metadata) for that transition.
/// The kind of a transition.
///
/// This is used when trying to determine the offset for a local datetime. It
/// indicates how the corresponding civil datetimes in `civil_starts` and
/// `civil_ends` should be interpreted. That is, there are three possible
/// cases:
///
/// 1. The offset of this transition is equivalent to the offset of the
/// previous transition. That means there are no ambiguous civil datetimes
/// between the transitions. This can occur, e.g., when the time zone
/// abbreviation changes.
/// 2. The offset of the transition is greater than the offset of the previous
/// transition. That means there is a "gap" in local time between the
/// transitions. This typically corresponds to entering daylight saving time.
/// It is usually, but not always, 1 hour.
/// 3. The offset of the transition is less than the offset of the previous
/// transition. That means there is a "fold" in local time where time is
/// repeated. This typically corresponds to leaving daylight saving time. It
/// is usually, but not always, 1 hour.
///
/// # More explanation
///
/// This, when combined with `civil_starts` and `civil_ends` in
/// `Transitions`, explicitly represents ambiguous wall clock times that
/// occur at the boundaries of transitions.
///
/// The start of the wall clock time is always the earlier possible wall clock
/// time that could occur with this transition's corresponding offset. For a
/// gap, it's the previous transition's offset. For a fold, it's the current
/// transition's offset.
///
/// For example, DST for `America/New_York` began on `2024-03-10T07:00:00+00`.
/// The offset prior to this instant in time is `-05`, corresponding
/// to standard time (EST). Thus, in wall clock time, DST began at
/// `2024-03-10T02:00:00`. And since this is a DST transition that jumps ahead
/// an hour, the start of DST also corresponds to the start of a gap. That is,
/// the times `02:00:00` through `02:59:59` never appear on a clock for this
/// hour. The question is thus: which offset should we apply to `02:00:00`?
/// We could apply the offset from the earlier transition `-05` and get
/// `2024-03-10T01:00:00-05` (that's `2024-03-10T06:00:00+00`), or we could
/// apply the offset from the later transition `-04` and get
/// `2024-03-10T03:00:00-04` (that's `2024-03-10T07:00:00+00`).
///
/// So in the above, we would have a `Gap` variant where `start` (inclusive) is
/// `2024-03-10T02:00:00` and `end` (exclusive) is `2024-03-10T03:00:00`.
///
/// The fold case is the same idea, but where the same time is repeated.
/// For example, in `America/New_York`, standard time began on
/// `2024-11-03T06:00:00+00`. The offset prior to this instant in time
/// is `-04`, corresponding to DST (EDT). Thus, in wall clock time, DST
/// ended at `2024-11-03T02:00:00`. However, since this is a fold, the
/// actual set of ambiguous times begins at `2024-11-03T01:00:00` and
/// ends at `2024-11-03T01:59:59.999999999`. That is, the wall clock time
/// `2024-11-03T02:00:00` is unambiguous.
///
/// So in the fold case above, we would have a `Fold` variant where
/// `start` (inclusive) is `2024-11-03T01:00:00` and `end` (exclusive) is
/// `2024-11-03T02:00:00`.
///
/// Since this gets bundled in with the sorted sequence of transitions, we'll
/// use the "start" time in all three cases as our target of binary search.
/// Once we land on a transition, we'll know our given wall clock time is
/// greater than or equal to its start wall clock time. At that point, to
/// determine if there is ambiguity, we merely need to determine if the given
/// wall clock time is less than the corresponding `end` time. If it is, then
/// it falls in a gap or fold. Otherwise, it's unambiguous.
///
/// Note that we could compute these datetime values while searching for the
/// correct transition, but there's a fair bit of math involved in going
/// between timestamps (which is what TZif gives us) and calendar datetimes
/// (which is what we're given as input). It is also necessary that we offset
/// the timestamp given in TZif at some point, since it is in UTC and the
/// datetime given is in wall clock time. So I decided it would be worth
/// pre-computing what we need in terms of what the input is. This way, we
/// don't need to do any conversions, or indeed, any arithmetic at all, for
/// time zone lookups. We *could* store these as transitions, but then the
/// input datetime would need to be converted to a timestamp before searching
/// the transitions.
/// The representation for a timestamp used by the TZif implementation.
///
/// We don't use [`Timestamp`](crate::Timestamp) from the root of this crate
/// because TZif data doesn't require nanosecond resolution. Instead, this
/// representation uses only second resolution. This makes for a more compact
/// sequence of transitions, which means more data fits into cache and thus
/// faster binary search.
/// The representation for a civil datetime used by the TZif implementation.
///
/// We don't use [`civil::DateTime`] here because we specifically
/// do not need to represent fractional seconds. This lets us easily represent
/// what we need in 8 bytes.
///
/// Moreover, we pack the fields into a single `i64` to make comparisons
/// extremely cheap. This is especially useful since we do a binary search on
/// civil datetimes when determining the instant that corresponds to a civil
/// datetime.
/// Creates a new bit packed datetime from jiff-core's standard datetime type.
///
/// Note that this completely ignores any fractional second component on the
/// provided datetime.
/// Returns true if the data might be in TZif format.
///
/// It is possible that this returns true even if the given data is not in TZif
/// format. However, it is impossible for this to return false when the given
/// data is TZif. That is, a false positive is allowed but a false negative is
/// not.
// If you're looking for tests, they can be found in Jiff's `tz::timezone`
// and `tz::tzif` modules.