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