Skip to main content

time/formatting/
component_provider.rs

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