1 | /*! |
2 | Defines data types shared between `jiff` and `jiff-static`. |
3 | |
4 | While this module exposes types that can be imported outside of `jiff` itself, |
5 | there are *no* semver guarantees provided. That is, this module is _not_ part |
6 | of Jiff's public API. The only guarantee of compatibility that is provided |
7 | is that `jiff-static x.y.z` works with one and only one version of Jiff, |
8 | corresponding to `jiff x.y.z` (i.e., the same version number). |
9 | |
10 | # Design |
11 | |
12 | This module is really accomplishing two different things at the same time. |
13 | |
14 | Firstly, it is a way to provide types that can be used to construct a static |
15 | `TimeZone`. The proc macros in `jiff-static` generate code using these |
16 | types (and a few routines). |
17 | |
18 | Secondly, it provides a way to parse TZif data without `jiff-static` |
19 | depending on `jiff` via a Cargo dependency. This actually requires copying |
20 | the code in this module (which is why it is kinda sectioned off from the rest |
21 | of jiff) into the `jiff-static` crate. This can be done automatically with |
22 | `jiff-cli`: |
23 | |
24 | ```text |
25 | jiff-cli generate shared |
26 | ``` |
27 | |
28 | The copying of code is pretty unfortunate, because it means both crates have to |
29 | compile it. However, the alternatives aren't great either. |
30 | |
31 | One alternative is to have `jiff-static` explicitly depend on `jiff` in its |
32 | `Cargo.toml`. Then Jiff could expose the parsing routines, as it does here, |
33 | and `jiff-static` could use them directly. Unfortunately, this means that |
34 | `jiff` cannot depend on `jiff-static`. And that in turn means that `jiff` |
35 | cannot re-export the macros. Users will need to explicitly depend on and use |
36 | `jiff-static`. Moreover, this could result in some potential surprises |
37 | since `jiff-static` will need to have an `=x.y.z` dependency on Jiff for |
38 | compatibility reasons. That in turn means that the version of Jiff actually |
39 | used is not determine by the user's `jiff = "x.y.z"` line, but rather by the |
40 | user's `jiff-static = "x'.y'.z'"` line. This is overall annoying and not a |
41 | good user experience. Plus, it inverts the typical relationship between crates |
42 | and their proc macros (e.g., `serde` and `serde_derive`) and thus could result |
43 | in other unanticipated surprises. |
44 | |
45 | Another obvious alternative is to split this code out into a separate crate |
46 | that both `jiff` and `jiff-static` depend on. However, the API exposed in |
47 | this module does not provide a coherent user experience. It would either need a |
48 | ton of work to turn it into a coherent user experience or it would need to be |
49 | published as a `jiff-internal-use-only` crate that I find to be very annoying |
50 | and confusing. Moreover, a separate crate introduces a new semver boundary |
51 | beneath Jiff. I've found these sorts of things to overall increase maintenance |
52 | burden (see ripgrep and regex for cases where I did this). |
53 | |
54 | I overall decided that the least bad choice was to copy a little code (under |
55 | 2,000 source lines of code at present I believe). Since the copy is managed |
56 | automatically via `jiff-cli generate shared`, we remove the downside of the |
57 | code getting out of sync. The only downside is extra compile time. Since I |
58 | generally only expect `jiff-static` to be used in niche circumstances, I |
59 | prefer this trade-off over the other choices. |
60 | |
61 | More context on how I arrived at this design can be found here: |
62 | <https://github.com/BurntSushi/jiff/issues/256> |
63 | |
64 | # Particulars |
65 | |
66 | When this code is copied to `jiff-static`, the following transformations are |
67 | done: |
68 | |
69 | * A header is added to indicate that the copied file is auto-generated. |
70 | * All `#[cfg(feature = "alloc")]` annotations are removed. The `jiff-static` |
71 | proc macro always runs in a context where the standard library is available. |
72 | * Any code between `// only-jiff-start` and `// only-jiff-end` comments is |
73 | removed. Nesting isn't supported. |
74 | |
75 | Otherwise, this module is specifically organized in a way that doesn't rely on |
76 | any other part of Jiff. The one exception are routines to convert from these |
77 | exposed types to other internal types inside of Jiff. This is necessary for |
78 | building a static `TimeZone`. But these conversion routines are removed when |
79 | this module is copied to `jiff-static`. |
80 | */ |
81 | |
82 | /// An alias for TZif data whose backing storage has a `'static` lifetime. |
83 | // only-jiff-start |
84 | pub type TzifStatic = Tzif< |
85 | &'static str, |
86 | &'static str, |
87 | &'static [TzifLocalTimeType], |
88 | &'static [i64], |
89 | &'static [TzifDateTime], |
90 | &'static [TzifDateTime], |
91 | &'static [TzifTransitionInfo], |
92 | >; |
93 | // only-jiff-end |
94 | |
95 | /// An alias for TZif data whose backing storage is on the heap. |
96 | #[cfg (feature = "alloc" )] |
97 | pub type TzifOwned = Tzif< |
98 | alloc::string::String, |
99 | self::util::array_str::Abbreviation, |
100 | alloc::vec::Vec<TzifLocalTimeType>, |
101 | alloc::vec::Vec<i64>, |
102 | alloc::vec::Vec<TzifDateTime>, |
103 | alloc::vec::Vec<TzifDateTime>, |
104 | alloc::vec::Vec<TzifTransitionInfo>, |
105 | >; |
106 | |
107 | /// An alias for TZif transition data whose backing storage is on the heap. |
108 | #[cfg (feature = "alloc" )] |
109 | pub type TzifTransitionsOwned = TzifTransitions< |
110 | alloc::vec::Vec<i64>, |
111 | alloc::vec::Vec<TzifDateTime>, |
112 | alloc::vec::Vec<TzifDateTime>, |
113 | alloc::vec::Vec<TzifTransitionInfo>, |
114 | >; |
115 | |
116 | #[derive (Clone, Debug)] |
117 | pub struct Tzif<STR, ABBREV, TYPES, TIMESTAMPS, STARTS, ENDS, INFOS> { |
118 | pub fixed: TzifFixed<STR, ABBREV>, |
119 | pub types: TYPES, |
120 | pub transitions: TzifTransitions<TIMESTAMPS, STARTS, ENDS, INFOS>, |
121 | } |
122 | |
123 | #[derive (Clone, Debug)] |
124 | pub struct TzifFixed<STR, ABBREV> { |
125 | pub name: Option<STR>, |
126 | /// An ASCII byte corresponding to the version number. So, 0x50 is '2'. |
127 | /// |
128 | /// This is unused. It's only used in `test` compilation for emitting |
129 | /// diagnostic data about TZif files. If we really need to use this, we |
130 | /// should probably just convert it to an actual integer. |
131 | pub version: u8, |
132 | pub checksum: u32, |
133 | pub designations: STR, |
134 | pub posix_tz: Option<PosixTimeZone<ABBREV>>, |
135 | } |
136 | |
137 | #[derive (Clone, Copy, Debug)] |
138 | pub struct TzifLocalTimeType { |
139 | pub offset: i32, |
140 | pub is_dst: bool, |
141 | pub designation: (u8, u8), // inclusive..exclusive |
142 | pub indicator: TzifIndicator, |
143 | } |
144 | |
145 | /// This enum corresponds to the possible indicator values for standard/wall |
146 | /// and UT/local. |
147 | /// |
148 | /// Note that UT+Wall is not allowed. |
149 | /// |
150 | /// I honestly have no earthly clue what they mean. I've read the section about |
151 | /// them in RFC 8536 several times and I can't make sense of it. I've even |
152 | /// looked at data files that have these set and still can't make sense of |
153 | /// them. I've even looked at what other datetime libraries do with these, and |
154 | /// they all seem to just ignore them. Like, WTF. I've spent the last couple |
155 | /// months of my life steeped in time, and I just cannot figure this out. Am I |
156 | /// just dumb? |
157 | /// |
158 | /// Anyway, we parse them, but otherwise ignore them because that's what all |
159 | /// the cool kids do. |
160 | /// |
161 | /// The default is `LocalWall`, which also occurs when no indicators are |
162 | /// present. |
163 | /// |
164 | /// I tried again and still don't get it. Here's a dump for `Pacific/Honolulu`: |
165 | /// |
166 | /// ```text |
167 | /// $ ./scripts/jiff-debug tzif /usr/share/zoneinfo/Pacific/Honolulu |
168 | /// TIME ZONE NAME |
169 | /// /usr/share/zoneinfo/Pacific/Honolulu |
170 | /// LOCAL TIME TYPES |
171 | /// 000: offset=-10:31:26, is_dst=false, designation=LMT, indicator=local/wall |
172 | /// 001: offset=-10:30, is_dst=false, designation=HST, indicator=local/wall |
173 | /// 002: offset=-09:30, is_dst=true, designation=HDT, indicator=local/wall |
174 | /// 003: offset=-09:30, is_dst=true, designation=HWT, indicator=local/wall |
175 | /// 004: offset=-09:30, is_dst=true, designation=HPT, indicator=ut/std |
176 | /// 005: offset=-10, is_dst=false, designation=HST, indicator=local/wall |
177 | /// TRANSITIONS |
178 | /// 0000: -9999-01-02T01:59:59 :: -377705023201 :: type=0, -10:31:26, is_dst=false, LMT, local/wall |
179 | /// 0001: 1896-01-13T22:31:26 :: -2334101314 :: type=1, -10:30, is_dst=false, HST, local/wall |
180 | /// 0002: 1933-04-30T12:30:00 :: -1157283000 :: type=2, -09:30, is_dst=true, HDT, local/wall |
181 | /// 0003: 1933-05-21T21:30:00 :: -1155436200 :: type=1, -10:30, is_dst=false, HST, local/wall |
182 | /// 0004: 1942-02-09T12:30:00 :: -880198200 :: type=3, -09:30, is_dst=true, HWT, local/wall |
183 | /// 0005: 1945-08-14T23:00:00 :: -769395600 :: type=4, -09:30, is_dst=true, HPT, ut/std |
184 | /// 0006: 1945-09-30T11:30:00 :: -765376200 :: type=1, -10:30, is_dst=false, HST, local/wall |
185 | /// 0007: 1947-06-08T12:30:00 :: -712150200 :: type=5, -10, is_dst=false, HST, local/wall |
186 | /// POSIX TIME ZONE STRING |
187 | /// HST10 |
188 | /// ``` |
189 | /// |
190 | /// See how type 004 has a ut/std indicator? What the fuck does that mean? |
191 | /// All transitions are defined in terms of UTC. I confirmed this with `zdump`: |
192 | /// |
193 | /// ```text |
194 | /// $ zdump -v Pacific/Honolulu | rg 1945 |
195 | /// Pacific/Honolulu Tue Aug 14 22:59:59 1945 UT = Tue Aug 14 13:29:59 1945 HWT isdst=1 gmtoff=-34200 |
196 | /// Pacific/Honolulu Tue Aug 14 23:00:00 1945 UT = Tue Aug 14 13:30:00 1945 HPT isdst=1 gmtoff=-34200 |
197 | /// Pacific/Honolulu Sun Sep 30 11:29:59 1945 UT = Sun Sep 30 01:59:59 1945 HPT isdst=1 gmtoff=-34200 |
198 | /// Pacific/Honolulu Sun Sep 30 11:30:00 1945 UT = Sun Sep 30 01:00:00 1945 HST isdst=0 gmtoff=-37800 |
199 | /// ``` |
200 | /// |
201 | /// The times match up. All of them. The indicators don't seem to make a |
202 | /// difference. I'm clearly missing something. |
203 | #[derive (Clone, Copy, Debug)] |
204 | pub enum TzifIndicator { |
205 | LocalWall, |
206 | LocalStandard, |
207 | UTStandard, |
208 | } |
209 | |
210 | /// The set of transitions in TZif data, laid out in column orientation. |
211 | /// |
212 | /// The column orientation is used to make TZ lookups faster. Specifically, |
213 | /// for finding an offset for a timestamp, we do a binary search on |
214 | /// `timestamps`. For finding an offset for a local datetime, we do a binary |
215 | /// search on `civil_starts`. By making these two distinct sequences with |
216 | /// nothing else in them, we make them as small as possible and thus improve |
217 | /// cache locality. |
218 | /// |
219 | /// All sequences in this type are in correspondence with one another. They |
220 | /// are all guaranteed to have the same length. |
221 | #[derive (Clone, Debug)] |
222 | pub struct TzifTransitions<TIMESTAMPS, STARTS, ENDS, INFOS> { |
223 | /// The timestamp at which this transition begins. |
224 | pub timestamps: TIMESTAMPS, |
225 | /// The wall clock time for when a transition begins. |
226 | pub civil_starts: STARTS, |
227 | /// The wall clock time for when a transition ends. |
228 | /// |
229 | /// This is only non-zero when the transition kind is a gap or a fold. |
230 | pub civil_ends: ENDS, |
231 | /// Any other relevant data about a transition, such as its local type |
232 | /// index and the transition kind. |
233 | pub infos: INFOS, |
234 | } |
235 | |
236 | /// TZif transition info beyond the timestamp and civil datetime. |
237 | /// |
238 | /// For example, this contains a transition's "local type index," which in |
239 | /// turn gives access to the offset (among other metadata) for that transition. |
240 | #[derive (Clone, Copy, Debug)] |
241 | pub struct TzifTransitionInfo { |
242 | /// The index into the sequence of local time type records. This is what |
243 | /// provides the correct offset (from UTC) that is active beginning at |
244 | /// this transition. |
245 | pub type_index: u8, |
246 | /// The boundary condition for quickly determining if a given wall clock |
247 | /// time is ambiguous (i.e., falls in a gap or a fold). |
248 | pub kind: TzifTransitionKind, |
249 | } |
250 | |
251 | /// The kind of a transition. |
252 | /// |
253 | /// This is used when trying to determine the offset for a local datetime. It |
254 | /// indicates how the corresponding civil datetimes in `civil_starts` and |
255 | /// `civil_ends` should be interpreted. That is, there are three possible |
256 | /// cases: |
257 | /// |
258 | /// 1. The offset of this transition is equivalent to the offset of the |
259 | /// previous transition. That means there are no ambiguous civil datetimes |
260 | /// between the transitions. This can occur, e.g., when the time zone |
261 | /// abbreviation changes. |
262 | /// 2. The offset of the transition is greater than the offset of the previous |
263 | /// transition. That means there is a "gap" in local time between the |
264 | /// transitions. This typically corresponds to entering daylight saving time. |
265 | /// It is usually, but not always, 1 hour. |
266 | /// 3. The offset of the transition is less than the offset of the previous |
267 | /// transition. That means there is a "fold" in local time where time is |
268 | /// repeated. This typically corresponds to leaving daylight saving time. It |
269 | /// is usually, but not always, 1 hour. |
270 | /// |
271 | /// # More explanation |
272 | /// |
273 | /// This, when combined with `civil_starts` and `civil_ends` in |
274 | /// `TzifTransitions`, explicitly represents ambiguous wall clock times that |
275 | /// occur at the boundaries of transitions. |
276 | /// |
277 | /// The start of the wall clock time is always the earlier possible wall clock |
278 | /// time that could occur with this transition's corresponding offset. For a |
279 | /// gap, it's the previous transition's offset. For a fold, it's the current |
280 | /// transition's offset. |
281 | /// |
282 | /// For example, DST for `America/New_York` began on `2024-03-10T07:00:00+00`. |
283 | /// The offset prior to this instant in time is `-05`, corresponding |
284 | /// to standard time (EST). Thus, in wall clock time, DST began at |
285 | /// `2024-03-10T02:00:00`. And since this is a DST transition that jumps ahead |
286 | /// an hour, the start of DST also corresponds to the start of a gap. That is, |
287 | /// the times `02:00:00` through `02:59:59` never appear on a clock for this |
288 | /// hour. The question is thus: which offset should we apply to `02:00:00`? |
289 | /// We could apply the offset from the earlier transition `-05` and get |
290 | /// `2024-03-10T01:00:00-05` (that's `2024-03-10T06:00:00+00`), or we could |
291 | /// apply the offset from the later transition `-04` and get |
292 | /// `2024-03-10T03:00:00-04` (that's `2024-03-10T07:00:00+00`). |
293 | /// |
294 | /// So in the above, we would have a `Gap` variant where `start` (inclusive) is |
295 | /// `2024-03-10T02:00:00` and `end` (exclusive) is `2024-03-10T03:00:00`. |
296 | /// |
297 | /// The fold case is the same idea, but where the same time is repeated. |
298 | /// For example, in `America/New_York`, standard time began on |
299 | /// `2024-11-03T06:00:00+00`. The offset prior to this instant in time |
300 | /// is `-04`, corresponding to DST (EDT). Thus, in wall clock time, DST |
301 | /// ended at `2024-11-03T02:00:00`. However, since this is a fold, the |
302 | /// actual set of ambiguous times begins at `2024-11-03T01:00:00` and |
303 | /// ends at `2024-11-03T01:59:59.999999999`. That is, the wall clock time |
304 | /// `2024-11-03T02:00:00` is unambiguous. |
305 | /// |
306 | /// So in the fold case above, we would have a `Fold` variant where |
307 | /// `start` (inclusive) is `2024-11-03T01:00:00` and `end` (exclusive) is |
308 | /// `2024-11-03T02:00:00`. |
309 | /// |
310 | /// Since this gets bundled in with the sorted sequence of transitions, we'll |
311 | /// use the "start" time in all three cases as our target of binary search. |
312 | /// Once we land on a transition, we'll know our given wall clock time is |
313 | /// greater than or equal to its start wall clock time. At that point, to |
314 | /// determine if there is ambiguity, we merely need to determine if the given |
315 | /// wall clock time is less than the corresponding `end` time. If it is, then |
316 | /// it falls in a gap or fold. Otherwise, it's unambiguous. |
317 | /// |
318 | /// Note that we could compute these datetime values while searching for the |
319 | /// correct transition, but there's a fair bit of math involved in going |
320 | /// between timestamps (which is what TZif gives us) and calendar datetimes |
321 | /// (which is what we're given as input). It is also necessary that we offset |
322 | /// the timestamp given in TZif at some point, since it is in UTC and the |
323 | /// datetime given is in wall clock time. So I decided it would be worth |
324 | /// pre-computing what we need in terms of what the input is. This way, we |
325 | /// don't need to do any conversions, or indeed, any arithmetic at all, for |
326 | /// time zone lookups. We *could* store these as transitions, but then the |
327 | /// input datetime would need to be converted to a timestamp before searching |
328 | /// the transitions. |
329 | #[derive (Clone, Copy, Debug)] |
330 | pub enum TzifTransitionKind { |
331 | /// This transition cannot possibly lead to an unambiguous offset because |
332 | /// its offset is equivalent to the offset of the previous transition. |
333 | /// |
334 | /// Has an entry in `civil_starts`, but corresponding entry in `civil_ends` |
335 | /// is always zeroes (i.e., meaningless). |
336 | Unambiguous, |
337 | /// This occurs when this transition's offset is strictly greater than the |
338 | /// previous transition's offset. This effectively results in a "gap" of |
339 | /// time equal to the difference in the offsets between the two |
340 | /// transitions. |
341 | /// |
342 | /// Has an entry in `civil_starts` for when the gap starts (inclusive) in |
343 | /// local time. Also has an entry in `civil_ends` for when the fold ends |
344 | /// (exclusive) in local time. |
345 | Gap, |
346 | /// This occurs when this transition's offset is strictly less than the |
347 | /// previous transition's offset. This results in a "fold" of time where |
348 | /// the two transitions have an overlap where it is ambiguous which one |
349 | /// applies given a wall clock time. In effect, a span of time equal to the |
350 | /// difference in the offsets is repeated. |
351 | /// |
352 | /// Has an entry in `civil_starts` for when the fold starts (inclusive) in |
353 | /// local time. Also has an entry in `civil_ends` for when the fold ends |
354 | /// (exclusive) in local time. |
355 | Fold, |
356 | } |
357 | |
358 | /// The representation we use to represent a civil datetime. |
359 | /// |
360 | /// We don't use `shared::util::itime::IDateTime` here because we specifically |
361 | /// do not need to represent fractional seconds. This lets us easily represent |
362 | /// what we need in 8 bytes instead of the 12 bytes used by `IDateTime`. |
363 | /// |
364 | /// Moreover, we pack the fields into a single `i64` to make comparisons |
365 | /// extremely cheap. This is especially useful since we do a binary search on |
366 | /// `&[TzifDateTime]` when doing a TZ lookup for a civil datetime. |
367 | #[derive (Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] |
368 | pub struct TzifDateTime { |
369 | bits: i64, |
370 | } |
371 | |
372 | impl TzifDateTime { |
373 | pub const ZERO: TzifDateTime = TzifDateTime::new(0, 0, 0, 0, 0, 0); |
374 | |
375 | pub const fn new( |
376 | year: i16, |
377 | month: i8, |
378 | day: i8, |
379 | hour: i8, |
380 | minute: i8, |
381 | second: i8, |
382 | ) -> TzifDateTime { |
383 | // TzifDateTime { year, month, day, hour, minute, second } |
384 | let mut bits = (year as u64) << 48; |
385 | bits |= (month as u64) << 40; |
386 | bits |= (day as u64) << 32; |
387 | bits |= (hour as u64) << 24; |
388 | bits |= (minute as u64) << 16; |
389 | bits |= (second as u64) << 8; |
390 | // The least significant 8 bits remain 0. |
391 | TzifDateTime { bits: bits as i64 } |
392 | } |
393 | |
394 | pub const fn year(self) -> i16 { |
395 | (self.bits as u64 >> 48) as u16 as i16 |
396 | } |
397 | |
398 | pub const fn month(self) -> i8 { |
399 | (self.bits as u64 >> 40) as u8 as i8 |
400 | } |
401 | |
402 | pub const fn day(self) -> i8 { |
403 | (self.bits as u64 >> 32) as u8 as i8 |
404 | } |
405 | |
406 | pub const fn hour(self) -> i8 { |
407 | (self.bits as u64 >> 24) as u8 as i8 |
408 | } |
409 | |
410 | pub const fn minute(self) -> i8 { |
411 | (self.bits as u64 >> 16) as u8 as i8 |
412 | } |
413 | |
414 | pub const fn second(self) -> i8 { |
415 | (self.bits as u64 >> 8) as u8 as i8 |
416 | } |
417 | } |
418 | |
419 | #[derive (Clone, Copy, Debug, Eq, PartialEq)] |
420 | pub struct PosixTimeZone<ABBREV> { |
421 | pub std_abbrev: ABBREV, |
422 | pub std_offset: PosixOffset, |
423 | pub dst: Option<PosixDst<ABBREV>>, |
424 | } |
425 | |
426 | #[derive (Clone, Copy, Debug, Eq, PartialEq)] |
427 | pub struct PosixDst<ABBREV> { |
428 | pub abbrev: ABBREV, |
429 | pub offset: PosixOffset, |
430 | pub rule: PosixRule, |
431 | } |
432 | |
433 | #[derive (Clone, Copy, Debug, Eq, PartialEq)] |
434 | pub struct PosixRule { |
435 | pub start: PosixDayTime, |
436 | pub end: PosixDayTime, |
437 | } |
438 | |
439 | #[derive (Clone, Copy, Debug, Eq, PartialEq)] |
440 | pub struct PosixDayTime { |
441 | pub date: PosixDay, |
442 | pub time: PosixTime, |
443 | } |
444 | |
445 | #[derive (Clone, Copy, Debug, Eq, PartialEq)] |
446 | pub enum PosixDay { |
447 | /// Julian day in a year, no counting for leap days. |
448 | /// |
449 | /// Valid range is `1..=365`. |
450 | JulianOne(i16), |
451 | /// Julian day in a year, counting for leap days. |
452 | /// |
453 | /// Valid range is `0..=365`. |
454 | JulianZero(i16), |
455 | /// The nth weekday of a month. |
456 | WeekdayOfMonth { |
457 | /// The month. |
458 | /// |
459 | /// Valid range is: `1..=12`. |
460 | month: i8, |
461 | /// The week. |
462 | /// |
463 | /// Valid range is `1..=5`. |
464 | /// |
465 | /// One interesting thing to note here (or my interpretation anyway), |
466 | /// is that a week of `4` means the "4th weekday in a month" where as |
467 | /// a week of `5` means the "last weekday in a month, even if it's the |
468 | /// 4th weekday." |
469 | week: i8, |
470 | /// The weekday. |
471 | /// |
472 | /// Valid range is `0..=6`, with `0` corresponding to Sunday. |
473 | weekday: i8, |
474 | }, |
475 | } |
476 | |
477 | #[derive (Clone, Copy, Debug, Eq, PartialEq)] |
478 | pub struct PosixTime { |
479 | pub second: i32, |
480 | } |
481 | |
482 | #[derive (Clone, Copy, Debug, Eq, PartialEq)] |
483 | pub struct PosixOffset { |
484 | pub second: i32, |
485 | } |
486 | |
487 | // only-jiff-start |
488 | impl TzifStatic { |
489 | pub const fn into_jiff(self) -> crate::tz::tzif::TzifStatic { |
490 | crate::tz::tzif::TzifStatic::from_shared_const(self) |
491 | } |
492 | } |
493 | // only-jiff-end |
494 | |
495 | // only-jiff-start |
496 | impl PosixTimeZone<&'static str> { |
497 | pub const fn into_jiff(self) -> crate::tz::posix::PosixTimeZoneStatic { |
498 | crate::tz::posix::PosixTimeZone::from_shared_const(self) |
499 | } |
500 | } |
501 | // only-jiff-end |
502 | |
503 | // Does not require `alloc`, but is only used when `alloc` is enabled. |
504 | #[cfg (feature = "alloc" )] |
505 | pub(crate) mod crc32; |
506 | pub(crate) mod posix; |
507 | #[cfg (feature = "alloc" )] |
508 | pub(crate) mod tzif; |
509 | pub(crate) mod util; |
510 | |