Skip to main content

time/formatting/
component_provider.rs

1use core::mem::MaybeUninit;
2use core::num::NonZero;
3
4use num_conv::prelude::*;
5
6use crate::format_description::Period;
7use crate::{
8    Date, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcDateTime, UtcOffset, Weekday,
9};
10
11/// State used by date-providing types to cache computed values.
12///
13/// This is used to avoid redundant computations when multiple date components are almost certainly
14/// going to be requested within the same formatting invocation.
15#[derive(Debug)]
16pub(crate) struct DateState {
17    day: Option<NonZero<u8>>,
18    month: Option<Month>,
19    iso_week: Option<NonZero<u8>>,
20    iso_year_is_initialized: bool,
21    iso_year: MaybeUninit<i32>,
22}
23
24impl Default for DateState {
25    fn default() -> Self {
26        Self {
27            day: Default::default(),
28            month: Default::default(),
29            iso_week: Default::default(),
30            iso_year_is_initialized: Default::default(),
31            iso_year: MaybeUninit::uninit(),
32        }
33    }
34}
35
36macro_rules! unimplemented_methods {
37    ($(
38        $(#[$meta:meta])*
39        ($component:literal) $name:ident => $ret:ty;
40    )*) => {
41        $(
42            $(#[$meta])*
43            #[track_caller]
44            #[expect(unused_variables, reason = "better for auto-generation of method stubs")]
45            fn $name(&self, state: &mut Self::State) -> $ret {
46                unimplemented!(concat!("type does not supply ", $component, " components"))
47            }
48        )*
49    };
50}
51
52macro_rules! delegate_providers {
53    (
54        $target:ident {
55            $($method:ident -> $return:ty)*
56        }
57    ) => {$(
58        #[inline]
59        fn $method(&self, state: &mut Self::State) -> $return {
60            ComponentProvider::$method(&self.$target(), state)
61        }
62    )*};
63    (
64        $target:ident ($state:expr) {
65            $($method:ident -> $return:ty)*
66        }
67    ) => {$(
68        #[inline]
69        fn $method(&self, _: &mut Self::State) -> $return {
70            ComponentProvider::$method(&self.$target(), $state)
71        }
72    )*};
73}
74
75/// A type with the ability to provide date, time, offset, and/or timestamp components on demand.
76///
77/// Note that while all methods have a default body, implementations are expected to override the
78/// body for all components that they provide. The default implementation exists solely for
79/// convenience, avoiding the need to specify unprovided components.
80pub(crate) trait ComponentProvider {
81    /// The state type used by the provider, allowing for caching of computed values.
82    type State: Default;
83
84    /// Whether the type can provide date components, indicating that date-related methods can be
85    /// called.
86    const SUPPLIES_DATE: bool = false;
87    /// Whether the type can provide time components, indicating that time-related methods can be
88    /// called.
89    const SUPPLIES_TIME: bool = false;
90    /// Whether the type can provide offset components, indicating that offset-related methods can
91    /// be called.
92    const SUPPLIES_OFFSET: bool = false;
93    /// Whether the type can provide timestamp components, indicating that timestamp-related methods
94    /// can be called.
95    const SUPPLIES_TIMESTAMP: bool = false;
96
97    unimplemented_methods! {
98        /// Obtain the day of the month.
99        ("date") day => u8;
100        /// Obtain the month of the year.
101        ("date") month => Month;
102        /// Obtain the ordinal day of the year.
103        ("date") ordinal => u16;
104        /// Obtain the day of the week.
105        ("date") weekday => Weekday;
106        /// Obtain the ISO week number.
107        ("date") iso_week_number => u8;
108        /// Obtain the Monday-based week number.
109        ("date") monday_based_week => u8;
110        /// Obtain the Sunday-based week number.
111        ("date") sunday_based_week => u8;
112        /// Obtain the calendar year.
113        ("date") calendar_year => i32;
114        /// Obtain the ISO week-based year.
115        ("date") iso_year => i32;
116        /// Obtain the hour within the day.
117        ("time") hour => u8;
118        /// Obtain the minute within the hour.
119        ("time") minute => u8;
120        /// Obtain the period of the day (AM/PM).
121        ("time") period => Period;
122        /// Obtain the second within the minute.
123        ("time") second => u8;
124        /// Obtain the nanosecond within the second.
125        ("time") nanosecond => u32;
126        /// Obtain whether the offset is negative.
127        ("offset") offset_is_negative => bool;
128        /// Obtain whether the offset is UTC.
129        ("offset") offset_is_utc => bool;
130        /// Obtain the hour component of the UTC offset.
131        ("offset") offset_hour => i8;
132        /// Obtain the minute component of the UTC offset.
133        ("offset") offset_minute => i8;
134        /// Obtain the second component of the UTC offset.
135        ("offset") offset_second => i8;
136        /// Obtain the Unix timestamp in seconds.
137        ("timestamp") unix_timestamp_seconds => i64;
138        /// Obtain the Unix timestamp in milliseconds.
139        ("timestamp") unix_timestamp_milliseconds => i64;
140        /// Obtain the Unix timestamp in microseconds.
141        ("timestamp") unix_timestamp_microseconds => i128;
142        /// Obtain the Unix timestamp in nanoseconds.
143        ("timestamp") unix_timestamp_nanoseconds => i128;
144    }
145}
146
147impl ComponentProvider for Time {
148    type State = ();
149
150    const SUPPLIES_TIME: bool = true;
151
152    #[inline]
153    fn hour(&self, _: &mut Self::State) -> u8 {
154        (*self).hour()
155    }
156
157    #[inline]
158    fn minute(&self, _: &mut Self::State) -> u8 {
159        (*self).minute()
160    }
161
162    #[inline]
163    fn period(&self, _: &mut Self::State) -> Period {
164        if (*self).hour() < 12 {
165            Period::Am
166        } else {
167            Period::Pm
168        }
169    }
170
171    #[inline]
172    fn second(&self, _: &mut Self::State) -> u8 {
173        (*self).second()
174    }
175
176    #[inline]
177    fn nanosecond(&self, _: &mut Self::State) -> u32 {
178        (*self).nanosecond()
179    }
180}
181
182impl ComponentProvider for Date {
183    type State = DateState;
184
185    const SUPPLIES_DATE: bool = true;
186
187    #[inline]
188    fn day(&self, state: &mut Self::State) -> u8 {
189        if let Some(day) = state.day {
190            return day.get();
191        }
192
193        let (_, month, day) = (*self).to_calendar_date();
194        state.month = Some(month);
195        // Safety: `day` is guaranteed to be non-zero.
196        state.day = Some(unsafe { NonZero::new_unchecked(day) });
197        day
198    }
199
200    #[inline]
201    fn month(&self, state: &mut Self::State) -> Month {
202        *state.month.get_or_insert_with(|| (*self).month())
203    }
204
205    #[inline]
206    fn ordinal(&self, _: &mut Self::State) -> u16 {
207        (*self).ordinal()
208    }
209
210    #[inline]
211    fn weekday(&self, _: &mut Self::State) -> Weekday {
212        (*self).weekday()
213    }
214
215    #[inline]
216    fn iso_week_number(&self, state: &mut Self::State) -> u8 {
217        if let Some(week) = state.iso_week {
218            return week.get();
219        }
220
221        let (iso_year, iso_week) = (*self).iso_year_week();
222        state.iso_year = MaybeUninit::new(iso_year);
223        state.iso_year_is_initialized = true;
224        // Safety: `iso_week` is guaranteed to be non-zero.
225        state.iso_week = Some(unsafe { NonZero::new_unchecked(iso_week) });
226        iso_week
227    }
228
229    #[inline]
230    fn monday_based_week(&self, _: &mut Self::State) -> u8 {
231        (*self).monday_based_week()
232    }
233
234    #[inline]
235    fn sunday_based_week(&self, _: &mut Self::State) -> u8 {
236        (*self).sunday_based_week()
237    }
238
239    #[inline]
240    fn calendar_year(&self, _: &mut Self::State) -> i32 {
241        (*self).year()
242    }
243
244    #[inline]
245    fn iso_year(&self, state: &mut Self::State) -> i32 {
246        if state.iso_year_is_initialized {
247            // Safety: `iso_year` was declared to be initialized.
248            return unsafe { state.iso_year.assume_init() };
249        }
250
251        let (iso_year, iso_week) = (*self).iso_year_week();
252        state.iso_year = MaybeUninit::new(iso_year);
253        state.iso_year_is_initialized = true;
254        // Safety: `iso_week` is guaranteed to be non-zero.
255        state.iso_week = Some(unsafe { NonZero::new_unchecked(iso_week) });
256        iso_year
257    }
258}
259
260impl ComponentProvider for PrimitiveDateTime {
261    type State = DateState;
262
263    const SUPPLIES_DATE: bool = true;
264    const SUPPLIES_TIME: bool = true;
265
266    delegate_providers!(date {
267        day -> u8
268        month -> Month
269        ordinal -> u16
270        weekday -> Weekday
271        iso_week_number -> u8
272        monday_based_week -> u8
273        sunday_based_week -> u8
274        calendar_year -> i32
275        iso_year -> i32
276    });
277    delegate_providers!(time (&mut ()) {
278        hour -> u8
279        minute -> u8
280        period -> Period
281        second -> u8
282        nanosecond -> u32
283    });
284}
285
286impl ComponentProvider for UtcOffset {
287    type State = ();
288
289    const SUPPLIES_OFFSET: bool = true;
290
291    #[inline]
292    fn offset_is_negative(&self, _: &mut Self::State) -> bool {
293        (*self).is_negative()
294    }
295
296    #[inline]
297    fn offset_is_utc(&self, _state: &mut Self::State) -> bool {
298        (*self).is_utc()
299    }
300
301    #[inline]
302    fn offset_hour(&self, _: &mut Self::State) -> i8 {
303        (*self).whole_hours()
304    }
305
306    #[inline]
307    fn offset_minute(&self, _: &mut Self::State) -> i8 {
308        (*self).minutes_past_hour()
309    }
310
311    #[inline]
312    fn offset_second(&self, _: &mut Self::State) -> i8 {
313        (*self).seconds_past_minute()
314    }
315}
316
317impl ComponentProvider for UtcDateTime {
318    type State = DateState;
319
320    const SUPPLIES_DATE: bool = true;
321    const SUPPLIES_TIME: bool = true;
322    const SUPPLIES_OFFSET: bool = true;
323    const SUPPLIES_TIMESTAMP: bool = true;
324
325    delegate_providers!(date {
326        day -> u8
327        month -> Month
328        ordinal -> u16
329        weekday -> Weekday
330        iso_week_number -> u8
331        monday_based_week -> u8
332        sunday_based_week -> u8
333        calendar_year -> i32
334        iso_year -> i32
335    });
336    delegate_providers!(time (&mut ()) {
337        hour -> u8
338        minute -> u8
339        period -> Period
340        second -> u8
341        nanosecond -> u32
342    });
343
344    #[inline]
345    fn offset_is_negative(&self, _: &mut Self::State) -> bool {
346        false
347    }
348
349    #[inline]
350    fn offset_is_utc(&self, _state: &mut Self::State) -> bool {
351        true
352    }
353
354    #[inline]
355    fn offset_hour(&self, _: &mut Self::State) -> i8 {
356        0
357    }
358
359    #[inline]
360    fn offset_minute(&self, _: &mut Self::State) -> i8 {
361        0
362    }
363
364    #[inline]
365    fn offset_second(&self, _: &mut Self::State) -> i8 {
366        0
367    }
368
369    #[inline]
370    fn unix_timestamp_seconds(&self, _: &mut Self::State) -> i64 {
371        (*self).unix_timestamp()
372    }
373
374    #[inline]
375    fn unix_timestamp_milliseconds(&self, state: &mut Self::State) -> i64 {
376        (ComponentProvider::unix_timestamp_nanoseconds(self, state) / 1_000_000).truncate()
377    }
378
379    #[inline]
380    fn unix_timestamp_microseconds(&self, state: &mut Self::State) -> i128 {
381        ComponentProvider::unix_timestamp_nanoseconds(self, state) / 1_000
382    }
383
384    #[inline]
385    fn unix_timestamp_nanoseconds(&self, _: &mut Self::State) -> i128 {
386        (*self).unix_timestamp_nanos()
387    }
388}
389
390impl ComponentProvider for OffsetDateTime {
391    type State = DateState;
392
393    const SUPPLIES_DATE: bool = true;
394    const SUPPLIES_TIME: bool = true;
395    const SUPPLIES_OFFSET: bool = true;
396    const SUPPLIES_TIMESTAMP: bool = true;
397
398    delegate_providers!(date {
399        day -> u8
400        month -> Month
401        ordinal -> u16
402        weekday -> Weekday
403        iso_week_number -> u8
404        monday_based_week -> u8
405        sunday_based_week -> u8
406        calendar_year -> i32
407        iso_year -> i32
408    });
409    delegate_providers!(time (&mut ()) {
410        hour -> u8
411        minute -> u8
412        period -> Period
413        second -> u8
414        nanosecond -> u32
415    });
416    delegate_providers!(offset (&mut ()) {
417        offset_is_negative -> bool
418        offset_is_utc -> bool
419        offset_hour -> i8
420        offset_minute -> i8
421        offset_second -> i8
422    });
423
424    #[inline]
425    fn unix_timestamp_seconds(&self, _: &mut Self::State) -> i64 {
426        (*self).unix_timestamp()
427    }
428
429    #[inline]
430    fn unix_timestamp_milliseconds(&self, _: &mut Self::State) -> i64 {
431        ((*self).unix_timestamp_nanos() / 1_000_000) as i64
432    }
433
434    #[inline]
435    fn unix_timestamp_microseconds(&self, _: &mut Self::State) -> i128 {
436        (*self).unix_timestamp_nanos() / 1_000
437    }
438
439    #[inline]
440    fn unix_timestamp_nanoseconds(&self, _: &mut Self::State) -> i128 {
441        (*self).unix_timestamp_nanos()
442    }
443}