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, Timestamp, UtcDateTime, UtcOffset,
12    Weekday,
13};
14
15/// State used by date-providing types to cache computed values.
16///
17/// This is used to avoid redundant computations when multiple date components are almost certainly
18/// going to be requested within the same formatting invocation.
19#[derive(Debug, Default)]
20pub(crate) struct DateState {
21    day: OptionDay,
22    month: Option<Month>,
23    iso_week: OptionIsoWeekNumber,
24    iso_year: OptionYear,
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        *state.month.get_or_insert_with(|| (*self).month())
206    }
207
208    #[inline]
209    fn ordinal(&self, _: &mut Self::State) -> Ordinal {
210        // Safety: `self.ordinal()` is guaranteed to be in range.
211        unsafe { Ordinal::new_unchecked((*self).ordinal()) }
212    }
213
214    #[inline]
215    fn weekday(&self, _: &mut Self::State) -> Weekday {
216        (*self).weekday()
217    }
218
219    #[inline]
220    fn iso_week_number(&self, state: &mut Self::State) -> IsoWeekNumber {
221        if let Some(week) = state.iso_week.get() {
222            return week;
223        }
224
225        let (iso_year, iso_week) = (*self).iso_year_week();
226        // Safety: `iso_week` is guaranteed to be non-zero.
227        let iso_week = unsafe { IsoWeekNumber::new_unchecked(iso_week) };
228        // Safety: `iso_year` is guaranteed to be in range.
229        state.iso_year = OptionYear::Some(unsafe { Year::new_unchecked(iso_year) });
230        state.iso_week = OptionIsoWeekNumber::Some(iso_week);
231        iso_week
232    }
233
234    #[inline]
235    fn monday_based_week(&self, _: &mut Self::State) -> MondayBasedWeek {
236        // Safety: `self.monday_based_week()` is guaranteed to be in range.
237        unsafe { MondayBasedWeek::new_unchecked((*self).monday_based_week()) }
238    }
239
240    #[inline]
241    fn sunday_based_week(&self, _: &mut Self::State) -> SundayBasedWeek {
242        // Safety: `self.sunday_based_week()` is guaranteed to be in range.
243        unsafe { SundayBasedWeek::new_unchecked((*self).sunday_based_week()) }
244    }
245
246    #[inline]
247    fn calendar_year(&self, _: &mut Self::State) -> Year {
248        // Safety: `self.year()` is guaranteed to be in range.
249        unsafe { Year::new_unchecked((*self).year()) }
250    }
251
252    #[inline]
253    fn iso_year(&self, state: &mut Self::State) -> Year {
254        if let Some(iso_year) = state.iso_year.get() {
255            return iso_year;
256        }
257
258        let (iso_year, iso_week) = (*self).iso_year_week();
259        // Safety: `iso_year_week` returns a valid ISO year.
260        let iso_year = unsafe { Year::new_unchecked(iso_year) };
261        state.iso_year = OptionYear::Some(iso_year);
262        // Safety: `iso_week` is guaranteed to be non-zero.
263        state.iso_week =
264            OptionIsoWeekNumber::Some(unsafe { IsoWeekNumber::new_unchecked(iso_week) });
265        iso_year
266    }
267}
268
269impl ComponentProvider for PrimitiveDateTime {
270    type State = DateState;
271
272    const SUPPLIES_DATE: bool = true;
273    const SUPPLIES_TIME: bool = true;
274
275    delegate_providers!(date {
276        day -> Day
277        month -> Month
278        ordinal -> Ordinal
279        weekday -> Weekday
280        iso_week_number -> IsoWeekNumber
281        monday_based_week -> MondayBasedWeek
282        sunday_based_week -> SundayBasedWeek
283        calendar_year -> Year
284        iso_year -> Year
285    });
286    delegate_providers!(time (&mut ()) {
287        hour -> Hours
288        minute -> Minutes
289        period -> Period
290        second -> Seconds
291        nanosecond -> Nanoseconds
292    });
293}
294
295impl ComponentProvider for UtcOffset {
296    type State = ();
297
298    const SUPPLIES_OFFSET: bool = true;
299
300    #[inline]
301    fn offset_is_negative(&self, _: &mut Self::State) -> bool {
302        (*self).is_negative()
303    }
304
305    #[inline]
306    fn offset_is_utc(&self, _state: &mut Self::State) -> bool {
307        (*self).is_utc()
308    }
309
310    #[inline]
311    fn offset_hour(&self, _: &mut Self::State) -> OffsetHours {
312        (*self).as_hms_ranged().0
313    }
314
315    #[inline]
316    fn offset_minute(&self, _: &mut Self::State) -> OffsetMinutes {
317        (*self).as_hms_ranged().1
318    }
319
320    #[inline]
321    fn offset_second(&self, _: &mut Self::State) -> OffsetSeconds {
322        (*self).as_hms_ranged().2
323    }
324}
325
326impl ComponentProvider for UtcDateTime {
327    type State = DateState;
328
329    const SUPPLIES_DATE: bool = true;
330    const SUPPLIES_TIME: bool = true;
331    const SUPPLIES_OFFSET: bool = true;
332    const SUPPLIES_TIMESTAMP: bool = true;
333
334    delegate_providers!(date {
335        day -> Day
336        month -> Month
337        ordinal -> Ordinal
338        weekday -> Weekday
339        iso_week_number -> IsoWeekNumber
340        monday_based_week -> MondayBasedWeek
341        sunday_based_week -> SundayBasedWeek
342        calendar_year -> Year
343        iso_year -> Year
344    });
345    delegate_providers!(time (&mut ()) {
346        hour -> Hours
347        minute -> Minutes
348        period -> Period
349        second -> Seconds
350        nanosecond -> Nanoseconds
351    });
352
353    #[inline]
354    fn offset_is_negative(&self, _: &mut Self::State) -> bool {
355        false
356    }
357
358    #[inline]
359    fn offset_is_utc(&self, _state: &mut Self::State) -> bool {
360        true
361    }
362
363    #[inline]
364    fn offset_hour(&self, _: &mut Self::State) -> OffsetHours {
365        OffsetHours::new_static::<0>()
366    }
367
368    #[inline]
369    fn offset_minute(&self, _: &mut Self::State) -> OffsetMinutes {
370        OffsetMinutes::new_static::<0>()
371    }
372
373    #[inline]
374    fn offset_second(&self, _: &mut Self::State) -> OffsetSeconds {
375        OffsetSeconds::new_static::<0>()
376    }
377
378    #[inline]
379    fn unix_timestamp_seconds(&self, _: &mut Self::State) -> i64 {
380        (*self).unix_timestamp()
381    }
382
383    #[inline]
384    fn unix_timestamp_milliseconds(&self, state: &mut Self::State) -> i64 {
385        (ComponentProvider::unix_timestamp_nanoseconds(self, state) / 1_000_000).truncate()
386    }
387
388    #[inline]
389    fn unix_timestamp_microseconds(&self, state: &mut Self::State) -> i128 {
390        ComponentProvider::unix_timestamp_nanoseconds(self, state) / 1_000
391    }
392
393    #[inline]
394    fn unix_timestamp_nanoseconds(&self, _: &mut Self::State) -> i128 {
395        (*self).unix_timestamp_nanos()
396    }
397}
398
399impl ComponentProvider for OffsetDateTime {
400    type State = DateState;
401
402    const SUPPLIES_DATE: bool = true;
403    const SUPPLIES_TIME: bool = true;
404    const SUPPLIES_OFFSET: bool = true;
405    const SUPPLIES_TIMESTAMP: bool = true;
406
407    delegate_providers!(date {
408        day -> Day
409        month -> Month
410        ordinal -> Ordinal
411        weekday -> Weekday
412        iso_week_number -> IsoWeekNumber
413        monday_based_week -> MondayBasedWeek
414        sunday_based_week -> SundayBasedWeek
415        calendar_year -> Year
416        iso_year -> Year
417    });
418    delegate_providers!(time (&mut ()) {
419        hour -> Hours
420        minute -> Minutes
421        period -> Period
422        second -> Seconds
423        nanosecond -> Nanoseconds
424    });
425    delegate_providers!(offset (&mut ()) {
426        offset_is_negative -> bool
427        offset_is_utc -> bool
428        offset_hour -> OffsetHours
429        offset_minute -> OffsetMinutes
430        offset_second -> OffsetSeconds
431    });
432
433    #[inline]
434    fn unix_timestamp_seconds(&self, _: &mut Self::State) -> i64 {
435        (*self).unix_timestamp()
436    }
437
438    #[inline]
439    fn unix_timestamp_milliseconds(&self, _: &mut Self::State) -> i64 {
440        ((*self).unix_timestamp_nanos() / 1_000_000) as i64
441    }
442
443    #[inline]
444    fn unix_timestamp_microseconds(&self, _: &mut Self::State) -> i128 {
445        (*self).unix_timestamp_nanos() / 1_000
446    }
447
448    #[inline]
449    fn unix_timestamp_nanoseconds(&self, _: &mut Self::State) -> i128 {
450        (*self).unix_timestamp_nanos()
451    }
452}
453
454impl ComponentProvider for Timestamp {
455    type State = TimestampState;
456
457    const SUPPLIES_DATE: bool = true;
458    const SUPPLIES_TIME: bool = true;
459    const SUPPLIES_OFFSET: bool = true;
460    const SUPPLIES_TIMESTAMP: bool = true;
461
462    #[inline]
463    fn day(&self, state: &mut Self::State) -> Day {
464        let date = state.date.get_or_insert_with(|| self.date());
465        ComponentProvider::day(date, &mut state.date_state)
466    }
467
468    #[inline]
469    fn month(&self, state: &mut Self::State) -> Month {
470        let date = state.date.get_or_insert_with(|| self.date());
471        ComponentProvider::month(date, &mut state.date_state)
472    }
473
474    #[inline]
475    fn ordinal(&self, state: &mut Self::State) -> Ordinal {
476        let date = state.date.get_or_insert_with(|| self.date());
477        ComponentProvider::ordinal(date, &mut state.date_state)
478    }
479
480    #[inline]
481    fn weekday(&self, state: &mut Self::State) -> Weekday {
482        let date = state.date.get_or_insert_with(|| self.date());
483        ComponentProvider::weekday(date, &mut state.date_state)
484    }
485
486    #[inline]
487    fn iso_week_number(&self, state: &mut Self::State) -> IsoWeekNumber {
488        let date = state.date.get_or_insert_with(|| self.date());
489        ComponentProvider::iso_week_number(date, &mut state.date_state)
490    }
491
492    #[inline]
493    fn monday_based_week(&self, state: &mut Self::State) -> MondayBasedWeek {
494        let date = state.date.get_or_insert_with(|| self.date());
495        ComponentProvider::monday_based_week(date, &mut state.date_state)
496    }
497
498    #[inline]
499    fn sunday_based_week(&self, state: &mut Self::State) -> SundayBasedWeek {
500        let date = state.date.get_or_insert_with(|| self.date());
501        ComponentProvider::sunday_based_week(date, &mut state.date_state)
502    }
503
504    #[inline]
505    fn calendar_year(&self, state: &mut Self::State) -> Year {
506        let date = state.date.get_or_insert_with(|| self.date());
507        ComponentProvider::calendar_year(date, &mut state.date_state)
508    }
509
510    #[inline]
511    fn iso_year(&self, state: &mut Self::State) -> Year {
512        let date = state.date.get_or_insert_with(|| self.date());
513        ComponentProvider::iso_year(date, &mut state.date_state)
514    }
515
516    #[inline]
517    fn hour(&self, state: &mut Self::State) -> Hours {
518        let time = state.time.get_or_insert_with(|| self.time());
519        ComponentProvider::hour(time, &mut ())
520    }
521
522    #[inline]
523    fn minute(&self, state: &mut Self::State) -> Minutes {
524        let time = state.time.get_or_insert_with(|| self.time());
525        ComponentProvider::minute(time, &mut ())
526    }
527
528    #[inline]
529    fn period(&self, state: &mut Self::State) -> Period {
530        let time = state.time.get_or_insert_with(|| self.time());
531        ComponentProvider::period(time, &mut ())
532    }
533
534    #[inline]
535    fn second(&self, state: &mut Self::State) -> Seconds {
536        let time = state.time.get_or_insert_with(|| self.time());
537        ComponentProvider::second(time, &mut ())
538    }
539
540    #[inline]
541    fn nanosecond(&self, _: &mut Self::State) -> Nanoseconds {
542        // No need to cache time here, as nanosecond is stored separately in `Timestamp` and can be
543        // directly accessed.
544        self.as_parts_ranged().1
545    }
546
547    #[inline]
548    fn offset_is_negative(&self, _: &mut Self::State) -> bool {
549        false
550    }
551
552    #[inline]
553    fn offset_is_utc(&self, _: &mut Self::State) -> bool {
554        true
555    }
556
557    #[inline]
558    fn offset_hour(&self, _: &mut Self::State) -> OffsetHours {
559        OffsetHours::new_static::<0>()
560    }
561
562    #[inline]
563    fn offset_minute(&self, _: &mut Self::State) -> OffsetMinutes {
564        OffsetMinutes::new_static::<0>()
565    }
566
567    #[inline]
568    fn offset_second(&self, _: &mut Self::State) -> OffsetSeconds {
569        OffsetSeconds::new_static::<0>()
570    }
571
572    #[inline]
573    fn unix_timestamp_seconds(&self, _: &mut Self::State) -> i64 {
574        self.as_seconds()
575    }
576
577    #[inline]
578    fn unix_timestamp_milliseconds(&self, _: &mut Self::State) -> i64 {
579        self.as_milliseconds()
580    }
581
582    #[inline]
583    fn unix_timestamp_microseconds(&self, _: &mut Self::State) -> i128 {
584        self.as_microseconds()
585    }
586
587    #[inline]
588    fn unix_timestamp_nanoseconds(&self, _: &mut Self::State) -> i128 {
589        self.as_nanoseconds()
590    }
591}