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, PlainDateTime, Time, Timestamp, 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    weekday: Option<Weekday>,
25}
26
27/// State used by `Timestamp` to cache computed date and time values.
28///
29/// `Date` and `Time` are cached separately, with the `Date`'s state being stored to allow for
30/// reusing existing methods.
31#[derive(Debug, Default)]
32pub(crate) struct TimestampState {
33    date: Option<Date>,
34    time: Option<Time>,
35    date_state: <Date as ComponentProvider>::State,
36}
37
38macro_rules! unimplemented_methods {
39    ($(
40        $(#[$meta:meta])*
41        ($component:literal) $name:ident => $ret:ty;
42    )*) => {
43        $(
44            $(#[$meta])*
45            #[track_caller]
46            #[expect(unused_variables, reason = "better for auto-generation of method stubs")]
47            fn $name(&self, state: &mut Self::State) -> $ret {
48                unimplemented!(concat!("type does not supply ", $component, " components"))
49            }
50        )*
51    };
52}
53
54macro_rules! delegate_providers {
55    (
56        $target:ident {
57            $($method:ident -> $return:ty)*
58        }
59    ) => {$(
60        #[inline]
61        fn $method(&self, state: &mut Self::State) -> $return {
62            ComponentProvider::$method(&self.$target(), state)
63        }
64    )*};
65    (
66        $target:ident ($state:expr) {
67            $($method:ident -> $return:ty)*
68        }
69    ) => {$(
70        #[inline]
71        fn $method(&self, _: &mut Self::State) -> $return {
72            ComponentProvider::$method(&self.$target(), $state)
73        }
74    )*};
75}
76
77/// A type with the ability to provide date, time, offset, and/or timestamp components on demand.
78///
79/// Note that while all methods have a default body, implementations are expected to override the
80/// body for all components that they provide. The default implementation exists solely for
81/// convenience, avoiding the need to specify unprovided components.
82pub(crate) trait ComponentProvider {
83    /// The state type used by the provider, allowing for caching of computed values.
84    type State: Default;
85
86    /// Whether the type can provide date components, indicating that date-related methods can be
87    /// called.
88    const SUPPLIES_DATE: bool = false;
89    /// Whether the type can provide time components, indicating that time-related methods can be
90    /// called.
91    const SUPPLIES_TIME: bool = false;
92    /// Whether the type can provide offset components, indicating that offset-related methods can
93    /// be called.
94    const SUPPLIES_OFFSET: bool = false;
95    /// Whether the type can provide timestamp components, indicating that timestamp-related methods
96    /// can be called.
97    const SUPPLIES_TIMESTAMP: bool = false;
98
99    unimplemented_methods! {
100        /// Obtain the day of the month.
101        ("date") day => Day;
102        /// Obtain the month of the year.
103        ("date") month => Month;
104        /// Obtain the ordinal day of the year.
105        ("date") ordinal => Ordinal;
106        /// Obtain the day of the week.
107        ("date") weekday => Weekday;
108        /// Obtain the ISO week number.
109        ("date") iso_week_number => IsoWeekNumber;
110        /// Obtain the Monday-based week number.
111        ("date") monday_based_week => MondayBasedWeek;
112        /// Obtain the Sunday-based week number.
113        ("date") sunday_based_week => SundayBasedWeek;
114        /// Obtain the calendar year.
115        ("date") calendar_year => Year;
116        /// Obtain the ISO week-based year.
117        ("date") iso_year => Year;
118        /// Obtain the hour within the day.
119        ("time") hour => Hours;
120        /// Obtain the minute within the hour.
121        ("time") minute => Minutes;
122        /// Obtain the period of the day (AM/PM).
123        ("time") period => Period;
124        /// Obtain the second within the minute.
125        ("time") second => Seconds;
126        /// Obtain the nanosecond within the second.
127        ("time") nanosecond => Nanoseconds;
128        /// Obtain whether the offset is negative.
129        ("offset") offset_is_negative => bool;
130        /// Obtain whether the offset is UTC.
131        ("offset") offset_is_utc => bool;
132        /// Obtain the hour component of the UTC offset.
133        ("offset") offset_hour => OffsetHours;
134        /// Obtain the minute component of the UTC offset.
135        ("offset") offset_minute => OffsetMinutes;
136        /// Obtain the second component of the UTC offset.
137        ("offset") offset_second => OffsetSeconds;
138        /// Obtain the Unix timestamp in seconds.
139        ("timestamp") unix_timestamp_seconds => i64;
140        /// Obtain the Unix timestamp in milliseconds.
141        ("timestamp") unix_timestamp_milliseconds => i64;
142        /// Obtain the Unix timestamp in microseconds.
143        ("timestamp") unix_timestamp_microseconds => i128;
144        /// Obtain the Unix timestamp in nanoseconds.
145        ("timestamp") unix_timestamp_nanoseconds => i128;
146    }
147}
148
149impl ComponentProvider for Time {
150    type State = ();
151
152    const SUPPLIES_TIME: bool = true;
153
154    #[inline]
155    fn hour(&self, _: &mut Self::State) -> Hours {
156        self.as_hms_nano_ranged().0
157    }
158
159    #[inline]
160    fn minute(&self, _: &mut Self::State) -> Minutes {
161        self.as_hms_nano_ranged().1
162    }
163
164    #[inline]
165    fn period(&self, _: &mut Self::State) -> Period {
166        if (*self).hour() < 12 {
167            Period::Am
168        } else {
169            Period::Pm
170        }
171    }
172
173    #[inline]
174    fn second(&self, _: &mut Self::State) -> Seconds {
175        self.as_hms_nano_ranged().2
176    }
177
178    #[inline]
179    fn nanosecond(&self, _: &mut Self::State) -> Nanoseconds {
180        self.as_hms_nano_ranged().3
181    }
182}
183
184impl ComponentProvider for Date {
185    type State = DateState;
186
187    const SUPPLIES_DATE: bool = true;
188
189    #[inline]
190    fn day(&self, state: &mut Self::State) -> Day {
191        if let Some(day) = state.day.get() {
192            return day;
193        }
194
195        let (_, month, day) = self.to_calendar_date();
196        // Safety: `day` is guaranteed to be in range.
197        let day = unsafe { Day::new_unchecked(day) };
198        state.month = Some(month);
199        state.day = OptionDay::Some(day);
200        day
201    }
202
203    #[inline]
204    fn month(&self, state: &mut Self::State) -> Month {
205        if let Some(month) = state.month {
206            return month;
207        }
208
209        let (_, month, day) = self.to_calendar_date();
210        // Safety: `day` is guaranteed to be in range.
211        let day = unsafe { Day::new_unchecked(day) };
212        state.month = Some(month);
213        state.day = OptionDay::Some(day);
214        month
215    }
216
217    #[inline]
218    fn ordinal(&self, _: &mut Self::State) -> Ordinal {
219        // Safety: `self.ordinal()` is guaranteed to be in range.
220        unsafe { Ordinal::new_unchecked((*self).ordinal()) }
221    }
222
223    #[inline]
224    fn weekday(&self, state: &mut Self::State) -> Weekday {
225        if let Some(weekday) = state.weekday {
226            return weekday;
227        }
228
229        let weekday = (*self).weekday();
230        state.weekday = Some(weekday);
231        weekday
232    }
233
234    #[inline]
235    fn iso_week_number(&self, state: &mut Self::State) -> IsoWeekNumber {
236        if let Some(week) = state.iso_week.get() {
237            return week;
238        }
239
240        let weekday = self.weekday(state);
241        let (iso_year, iso_week) = self.iso_year_week(weekday);
242        // Safety: `iso_week` is guaranteed to be non-zero.
243        let iso_week = unsafe { IsoWeekNumber::new_unchecked(iso_week) };
244        // Safety: `iso_year` is guaranteed to be in range.
245        state.iso_year = OptionYear::Some(unsafe { Year::new_unchecked(iso_year) });
246        state.iso_week = OptionIsoWeekNumber::Some(iso_week);
247        iso_week
248    }
249
250    #[inline]
251    fn monday_based_week(&self, _: &mut Self::State) -> MondayBasedWeek {
252        // Safety: `self.monday_based_week()` is guaranteed to be in range.
253        unsafe { MondayBasedWeek::new_unchecked((*self).monday_based_week()) }
254    }
255
256    #[inline]
257    fn sunday_based_week(&self, _: &mut Self::State) -> SundayBasedWeek {
258        // Safety: `self.sunday_based_week()` is guaranteed to be in range.
259        unsafe { SundayBasedWeek::new_unchecked((*self).sunday_based_week()) }
260    }
261
262    #[inline]
263    fn calendar_year(&self, _: &mut Self::State) -> Year {
264        // Safety: `self.year()` is guaranteed to be in range.
265        unsafe { Year::new_unchecked((*self).year()) }
266    }
267
268    #[inline]
269    fn iso_year(&self, state: &mut Self::State) -> Year {
270        if let Some(iso_year) = state.iso_year.get() {
271            return iso_year;
272        }
273
274        let weekday = self.weekday(state);
275        let (iso_year, iso_week) = self.iso_year_week(weekday);
276        // Safety: `iso_year_week` returns a valid ISO year.
277        let iso_year = unsafe { Year::new_unchecked(iso_year) };
278        state.iso_year = OptionYear::Some(iso_year);
279        // Safety: `iso_week` is guaranteed to be non-zero.
280        state.iso_week =
281            OptionIsoWeekNumber::Some(unsafe { IsoWeekNumber::new_unchecked(iso_week) });
282        iso_year
283    }
284}
285
286impl ComponentProvider for PlainDateTime {
287    type State = DateState;
288
289    const SUPPLIES_DATE: bool = true;
290    const SUPPLIES_TIME: bool = true;
291
292    delegate_providers!(date {
293        day -> Day
294        month -> Month
295        ordinal -> Ordinal
296        weekday -> Weekday
297        iso_week_number -> IsoWeekNumber
298        monday_based_week -> MondayBasedWeek
299        sunday_based_week -> SundayBasedWeek
300        calendar_year -> Year
301        iso_year -> Year
302    });
303    delegate_providers!(time (&mut ()) {
304        hour -> Hours
305        minute -> Minutes
306        period -> Period
307        second -> Seconds
308        nanosecond -> Nanoseconds
309    });
310}
311
312impl ComponentProvider for UtcOffset {
313    type State = ();
314
315    const SUPPLIES_OFFSET: bool = true;
316
317    #[inline]
318    fn offset_is_negative(&self, _: &mut Self::State) -> bool {
319        (*self).is_negative()
320    }
321
322    #[inline]
323    fn offset_is_utc(&self, _state: &mut Self::State) -> bool {
324        self.is_utc()
325    }
326
327    #[inline]
328    fn offset_hour(&self, _: &mut Self::State) -> OffsetHours {
329        self.as_hms_ranged().0
330    }
331
332    #[inline]
333    fn offset_minute(&self, _: &mut Self::State) -> OffsetMinutes {
334        self.as_hms_ranged().1
335    }
336
337    #[inline]
338    fn offset_second(&self, _: &mut Self::State) -> OffsetSeconds {
339        self.as_hms_ranged().2
340    }
341}
342
343impl ComponentProvider for UtcDateTime {
344    type State = DateState;
345
346    const SUPPLIES_DATE: bool = true;
347    const SUPPLIES_TIME: bool = true;
348    const SUPPLIES_OFFSET: bool = true;
349    const SUPPLIES_TIMESTAMP: bool = true;
350
351    delegate_providers!(date {
352        day -> Day
353        month -> Month
354        ordinal -> Ordinal
355        weekday -> Weekday
356        iso_week_number -> IsoWeekNumber
357        monday_based_week -> MondayBasedWeek
358        sunday_based_week -> SundayBasedWeek
359        calendar_year -> Year
360        iso_year -> Year
361    });
362    delegate_providers!(time (&mut ()) {
363        hour -> Hours
364        minute -> Minutes
365        period -> Period
366        second -> Seconds
367        nanosecond -> Nanoseconds
368    });
369
370    #[inline]
371    fn offset_is_negative(&self, _: &mut Self::State) -> bool {
372        false
373    }
374
375    #[inline]
376    fn offset_is_utc(&self, _state: &mut Self::State) -> bool {
377        true
378    }
379
380    #[inline]
381    fn offset_hour(&self, _: &mut Self::State) -> OffsetHours {
382        OffsetHours::new_static::<0>()
383    }
384
385    #[inline]
386    fn offset_minute(&self, _: &mut Self::State) -> OffsetMinutes {
387        OffsetMinutes::new_static::<0>()
388    }
389
390    #[inline]
391    fn offset_second(&self, _: &mut Self::State) -> OffsetSeconds {
392        OffsetSeconds::new_static::<0>()
393    }
394
395    #[inline]
396    fn unix_timestamp_seconds(&self, _: &mut Self::State) -> i64 {
397        self.unix_timestamp()
398    }
399
400    #[inline]
401    fn unix_timestamp_milliseconds(&self, state: &mut Self::State) -> i64 {
402        (ComponentProvider::unix_timestamp_nanoseconds(self, state) / 1_000_000).truncate()
403    }
404
405    #[inline]
406    fn unix_timestamp_microseconds(&self, state: &mut Self::State) -> i128 {
407        ComponentProvider::unix_timestamp_nanoseconds(self, state) / 1_000
408    }
409
410    #[inline]
411    fn unix_timestamp_nanoseconds(&self, _: &mut Self::State) -> i128 {
412        self.unix_timestamp_nanos()
413    }
414}
415
416impl ComponentProvider for OffsetDateTime {
417    type State = DateState;
418
419    const SUPPLIES_DATE: bool = true;
420    const SUPPLIES_TIME: bool = true;
421    const SUPPLIES_OFFSET: bool = true;
422    const SUPPLIES_TIMESTAMP: bool = true;
423
424    delegate_providers!(date {
425        day -> Day
426        month -> Month
427        ordinal -> Ordinal
428        weekday -> Weekday
429        iso_week_number -> IsoWeekNumber
430        monday_based_week -> MondayBasedWeek
431        sunday_based_week -> SundayBasedWeek
432        calendar_year -> Year
433        iso_year -> Year
434    });
435    delegate_providers!(time (&mut ()) {
436        hour -> Hours
437        minute -> Minutes
438        period -> Period
439        second -> Seconds
440        nanosecond -> Nanoseconds
441    });
442    delegate_providers!(offset (&mut ()) {
443        offset_is_negative -> bool
444        offset_is_utc -> bool
445        offset_hour -> OffsetHours
446        offset_minute -> OffsetMinutes
447        offset_second -> OffsetSeconds
448    });
449
450    #[inline]
451    fn unix_timestamp_seconds(&self, _: &mut Self::State) -> i64 {
452        self.unix_timestamp()
453    }
454
455    #[inline]
456    fn unix_timestamp_milliseconds(&self, _: &mut Self::State) -> i64 {
457        (self.unix_timestamp_nanos() / 1_000_000) as i64
458    }
459
460    #[inline]
461    fn unix_timestamp_microseconds(&self, _: &mut Self::State) -> i128 {
462        self.unix_timestamp_nanos() / 1_000
463    }
464
465    #[inline]
466    fn unix_timestamp_nanoseconds(&self, _: &mut Self::State) -> i128 {
467        self.unix_timestamp_nanos()
468    }
469}
470
471impl ComponentProvider for Timestamp {
472    type State = TimestampState;
473
474    const SUPPLIES_DATE: bool = true;
475    const SUPPLIES_TIME: bool = true;
476    const SUPPLIES_OFFSET: bool = true;
477    const SUPPLIES_TIMESTAMP: bool = true;
478
479    #[inline]
480    fn day(&self, state: &mut Self::State) -> Day {
481        let date = state.date.get_or_insert_with(|| self.date());
482        ComponentProvider::day(date, &mut state.date_state)
483    }
484
485    #[inline]
486    fn month(&self, state: &mut Self::State) -> Month {
487        let date = state.date.get_or_insert_with(|| self.date());
488        ComponentProvider::month(date, &mut state.date_state)
489    }
490
491    #[inline]
492    fn ordinal(&self, state: &mut Self::State) -> Ordinal {
493        let date = state.date.get_or_insert_with(|| self.date());
494        ComponentProvider::ordinal(date, &mut state.date_state)
495    }
496
497    #[inline]
498    fn weekday(&self, state: &mut Self::State) -> Weekday {
499        let date = state.date.get_or_insert_with(|| self.date());
500        ComponentProvider::weekday(date, &mut state.date_state)
501    }
502
503    #[inline]
504    fn iso_week_number(&self, state: &mut Self::State) -> IsoWeekNumber {
505        let date = state.date.get_or_insert_with(|| self.date());
506        ComponentProvider::iso_week_number(date, &mut state.date_state)
507    }
508
509    #[inline]
510    fn monday_based_week(&self, state: &mut Self::State) -> MondayBasedWeek {
511        let date = state.date.get_or_insert_with(|| self.date());
512        ComponentProvider::monday_based_week(date, &mut state.date_state)
513    }
514
515    #[inline]
516    fn sunday_based_week(&self, state: &mut Self::State) -> SundayBasedWeek {
517        let date = state.date.get_or_insert_with(|| self.date());
518        ComponentProvider::sunday_based_week(date, &mut state.date_state)
519    }
520
521    #[inline]
522    fn calendar_year(&self, state: &mut Self::State) -> Year {
523        let date = state.date.get_or_insert_with(|| self.date());
524        ComponentProvider::calendar_year(date, &mut state.date_state)
525    }
526
527    #[inline]
528    fn iso_year(&self, state: &mut Self::State) -> Year {
529        let date = state.date.get_or_insert_with(|| self.date());
530        ComponentProvider::iso_year(date, &mut state.date_state)
531    }
532
533    #[inline]
534    fn hour(&self, state: &mut Self::State) -> Hours {
535        let time = state.time.get_or_insert_with(|| self.time());
536        ComponentProvider::hour(time, &mut ())
537    }
538
539    #[inline]
540    fn minute(&self, state: &mut Self::State) -> Minutes {
541        let time = state.time.get_or_insert_with(|| self.time());
542        ComponentProvider::minute(time, &mut ())
543    }
544
545    #[inline]
546    fn period(&self, state: &mut Self::State) -> Period {
547        let time = state.time.get_or_insert_with(|| self.time());
548        ComponentProvider::period(time, &mut ())
549    }
550
551    #[inline]
552    fn second(&self, state: &mut Self::State) -> Seconds {
553        let time = state.time.get_or_insert_with(|| self.time());
554        ComponentProvider::second(time, &mut ())
555    }
556
557    #[inline]
558    fn nanosecond(&self, _: &mut Self::State) -> Nanoseconds {
559        // No need to cache time here, as nanosecond is stored separately in `Timestamp` and can be
560        // directly accessed.
561        self.as_parts_ranged().1
562    }
563
564    #[inline]
565    fn offset_is_negative(&self, _: &mut Self::State) -> bool {
566        false
567    }
568
569    #[inline]
570    fn offset_is_utc(&self, _: &mut Self::State) -> bool {
571        true
572    }
573
574    #[inline]
575    fn offset_hour(&self, _: &mut Self::State) -> OffsetHours {
576        OffsetHours::new_static::<0>()
577    }
578
579    #[inline]
580    fn offset_minute(&self, _: &mut Self::State) -> OffsetMinutes {
581        OffsetMinutes::new_static::<0>()
582    }
583
584    #[inline]
585    fn offset_second(&self, _: &mut Self::State) -> OffsetSeconds {
586        OffsetSeconds::new_static::<0>()
587    }
588
589    #[inline]
590    fn unix_timestamp_seconds(&self, _: &mut Self::State) -> i64 {
591        self.as_seconds()
592    }
593
594    #[inline]
595    fn unix_timestamp_milliseconds(&self, _: &mut Self::State) -> i64 {
596        self.as_milliseconds()
597    }
598
599    #[inline]
600    fn unix_timestamp_microseconds(&self, _: &mut Self::State) -> i128 {
601        self.as_microseconds()
602    }
603
604    #[inline]
605    fn unix_timestamp_nanoseconds(&self, _: &mut Self::State) -> i128 {
606        self.as_nanoseconds()
607    }
608}