use num_conv::prelude::*;
use crate::format_description::Period;
use crate::formatting::{
Day, IsoWeekNumber, MondayBasedWeek, OptionDay, OptionIsoWeekNumber, OptionYear, Ordinal,
SundayBasedWeek, Year,
};
use crate::time::{Hours, Minutes, Nanoseconds, Seconds};
use crate::utc_offset::{Hours as OffsetHours, Minutes as OffsetMinutes, Seconds as OffsetSeconds};
use crate::{
Date, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcDateTime, UtcOffset, Weekday,
};
#[derive(Debug, Default)]
pub(crate) struct DateState {
day: OptionDay,
month: Option<Month>,
iso_week: OptionIsoWeekNumber,
iso_year: OptionYear,
}
macro_rules! unimplemented_methods {
($(
$(#[$meta:meta])*
($component:literal) $name:ident => $ret:ty;
)*) => {
$(
$(#[$meta])*
#[track_caller]
#[expect(unused_variables, reason = "better for auto-generation of method stubs")]
fn $name(&self, state: &mut Self::State) -> $ret {
unimplemented!(concat!("type does not supply ", $component, " components"))
}
)*
};
}
macro_rules! delegate_providers {
(
$target:ident {
$($method:ident -> $return:ty)*
}
) => {$(
#[inline]
fn $method(&self, state: &mut Self::State) -> $return {
ComponentProvider::$method(&self.$target(), state)
}
)*};
(
$target:ident ($state:expr) {
$($method:ident -> $return:ty)*
}
) => {$(
#[inline]
fn $method(&self, _: &mut Self::State) -> $return {
ComponentProvider::$method(&self.$target(), $state)
}
)*};
}
pub(crate) trait ComponentProvider {
type State: Default;
const SUPPLIES_DATE: bool = false;
const SUPPLIES_TIME: bool = false;
const SUPPLIES_OFFSET: bool = false;
const SUPPLIES_TIMESTAMP: bool = false;
unimplemented_methods! {
("date") day => Day;
("date") month => Month;
("date") ordinal => Ordinal;
("date") weekday => Weekday;
("date") iso_week_number => IsoWeekNumber;
("date") monday_based_week => MondayBasedWeek;
("date") sunday_based_week => SundayBasedWeek;
("date") calendar_year => Year;
("date") iso_year => Year;
("time") hour => Hours;
("time") minute => Minutes;
("time") period => Period;
("time") second => Seconds;
("time") nanosecond => Nanoseconds;
("offset") offset_is_negative => bool;
("offset") offset_is_utc => bool;
("offset") offset_hour => OffsetHours;
("offset") offset_minute => OffsetMinutes;
("offset") offset_second => OffsetSeconds;
("timestamp") unix_timestamp_seconds => i64;
("timestamp") unix_timestamp_milliseconds => i64;
("timestamp") unix_timestamp_microseconds => i128;
("timestamp") unix_timestamp_nanoseconds => i128;
}
}
impl ComponentProvider for Time {
type State = ();
const SUPPLIES_TIME: bool = true;
#[inline]
fn hour(&self, _: &mut Self::State) -> Hours {
self.as_hms_nano_ranged().0
}
#[inline]
fn minute(&self, _: &mut Self::State) -> Minutes {
self.as_hms_nano_ranged().1
}
#[inline]
fn period(&self, _: &mut Self::State) -> Period {
if (*self).hour() < 12 {
Period::Am
} else {
Period::Pm
}
}
#[inline]
fn second(&self, _: &mut Self::State) -> Seconds {
self.as_hms_nano_ranged().2
}
#[inline]
fn nanosecond(&self, _: &mut Self::State) -> Nanoseconds {
self.as_hms_nano_ranged().3
}
}
impl ComponentProvider for Date {
type State = DateState;
const SUPPLIES_DATE: bool = true;
#[inline]
fn day(&self, state: &mut Self::State) -> Day {
if let Some(day) = state.day.get() {
return day;
}
let (_, month, day) = (*self).to_calendar_date();
let day = unsafe { Day::new_unchecked(day) };
state.month = Some(month);
state.day = OptionDay::Some(day);
day
}
#[inline]
fn month(&self, state: &mut Self::State) -> Month {
*state.month.get_or_insert_with(|| (*self).month())
}
#[inline]
fn ordinal(&self, _: &mut Self::State) -> Ordinal {
unsafe { Ordinal::new_unchecked((*self).ordinal()) }
}
#[inline]
fn weekday(&self, _: &mut Self::State) -> Weekday {
(*self).weekday()
}
#[inline]
fn iso_week_number(&self, state: &mut Self::State) -> IsoWeekNumber {
if let Some(week) = state.iso_week.get() {
return week;
}
let (iso_year, iso_week) = (*self).iso_year_week();
let iso_week = unsafe { IsoWeekNumber::new_unchecked(iso_week) };
state.iso_year = OptionYear::Some(unsafe { Year::new_unchecked(iso_year) });
state.iso_week = OptionIsoWeekNumber::Some(iso_week);
iso_week
}
#[inline]
fn monday_based_week(&self, _: &mut Self::State) -> MondayBasedWeek {
unsafe { MondayBasedWeek::new_unchecked((*self).monday_based_week()) }
}
#[inline]
fn sunday_based_week(&self, _: &mut Self::State) -> SundayBasedWeek {
unsafe { SundayBasedWeek::new_unchecked((*self).sunday_based_week()) }
}
#[inline]
fn calendar_year(&self, _: &mut Self::State) -> Year {
unsafe { Year::new_unchecked((*self).year()) }
}
#[inline]
fn iso_year(&self, state: &mut Self::State) -> Year {
if let Some(iso_year) = state.iso_year.get() {
return iso_year;
}
let (iso_year, iso_week) = (*self).iso_year_week();
let iso_year = unsafe { Year::new_unchecked(iso_year) };
state.iso_year = OptionYear::Some(iso_year);
state.iso_week =
OptionIsoWeekNumber::Some(unsafe { IsoWeekNumber::new_unchecked(iso_week) });
iso_year
}
}
impl ComponentProvider for PrimitiveDateTime {
type State = DateState;
const SUPPLIES_DATE: bool = true;
const SUPPLIES_TIME: bool = true;
delegate_providers!(date {
day -> Day
month -> Month
ordinal -> Ordinal
weekday -> Weekday
iso_week_number -> IsoWeekNumber
monday_based_week -> MondayBasedWeek
sunday_based_week -> SundayBasedWeek
calendar_year -> Year
iso_year -> Year
});
delegate_providers!(time (&mut ()) {
hour -> Hours
minute -> Minutes
period -> Period
second -> Seconds
nanosecond -> Nanoseconds
});
}
impl ComponentProvider for UtcOffset {
type State = ();
const SUPPLIES_OFFSET: bool = true;
#[inline]
fn offset_is_negative(&self, _: &mut Self::State) -> bool {
(*self).is_negative()
}
#[inline]
fn offset_is_utc(&self, _state: &mut Self::State) -> bool {
(*self).is_utc()
}
#[inline]
fn offset_hour(&self, _: &mut Self::State) -> OffsetHours {
(*self).as_hms_ranged().0
}
#[inline]
fn offset_minute(&self, _: &mut Self::State) -> OffsetMinutes {
(*self).as_hms_ranged().1
}
#[inline]
fn offset_second(&self, _: &mut Self::State) -> OffsetSeconds {
(*self).as_hms_ranged().2
}
}
impl ComponentProvider for UtcDateTime {
type State = DateState;
const SUPPLIES_DATE: bool = true;
const SUPPLIES_TIME: bool = true;
const SUPPLIES_OFFSET: bool = true;
const SUPPLIES_TIMESTAMP: bool = true;
delegate_providers!(date {
day -> Day
month -> Month
ordinal -> Ordinal
weekday -> Weekday
iso_week_number -> IsoWeekNumber
monday_based_week -> MondayBasedWeek
sunday_based_week -> SundayBasedWeek
calendar_year -> Year
iso_year -> Year
});
delegate_providers!(time (&mut ()) {
hour -> Hours
minute -> Minutes
period -> Period
second -> Seconds
nanosecond -> Nanoseconds
});
#[inline]
fn offset_is_negative(&self, _: &mut Self::State) -> bool {
false
}
#[inline]
fn offset_is_utc(&self, _state: &mut Self::State) -> bool {
true
}
#[inline]
fn offset_hour(&self, _: &mut Self::State) -> OffsetHours {
OffsetHours::new_static::<0>()
}
#[inline]
fn offset_minute(&self, _: &mut Self::State) -> OffsetMinutes {
OffsetMinutes::new_static::<0>()
}
#[inline]
fn offset_second(&self, _: &mut Self::State) -> OffsetSeconds {
OffsetSeconds::new_static::<0>()
}
#[inline]
fn unix_timestamp_seconds(&self, _: &mut Self::State) -> i64 {
(*self).unix_timestamp()
}
#[inline]
fn unix_timestamp_milliseconds(&self, state: &mut Self::State) -> i64 {
(ComponentProvider::unix_timestamp_nanoseconds(self, state) / 1_000_000).truncate()
}
#[inline]
fn unix_timestamp_microseconds(&self, state: &mut Self::State) -> i128 {
ComponentProvider::unix_timestamp_nanoseconds(self, state) / 1_000
}
#[inline]
fn unix_timestamp_nanoseconds(&self, _: &mut Self::State) -> i128 {
(*self).unix_timestamp_nanos()
}
}
impl ComponentProvider for OffsetDateTime {
type State = DateState;
const SUPPLIES_DATE: bool = true;
const SUPPLIES_TIME: bool = true;
const SUPPLIES_OFFSET: bool = true;
const SUPPLIES_TIMESTAMP: bool = true;
delegate_providers!(date {
day -> Day
month -> Month
ordinal -> Ordinal
weekday -> Weekday
iso_week_number -> IsoWeekNumber
monday_based_week -> MondayBasedWeek
sunday_based_week -> SundayBasedWeek
calendar_year -> Year
iso_year -> Year
});
delegate_providers!(time (&mut ()) {
hour -> Hours
minute -> Minutes
period -> Period
second -> Seconds
nanosecond -> Nanoseconds
});
delegate_providers!(offset (&mut ()) {
offset_is_negative -> bool
offset_is_utc -> bool
offset_hour -> OffsetHours
offset_minute -> OffsetMinutes
offset_second -> OffsetSeconds
});
#[inline]
fn unix_timestamp_seconds(&self, _: &mut Self::State) -> i64 {
(*self).unix_timestamp()
}
#[inline]
fn unix_timestamp_milliseconds(&self, _: &mut Self::State) -> i64 {
((*self).unix_timestamp_nanos() / 1_000_000) as i64
}
#[inline]
fn unix_timestamp_microseconds(&self, _: &mut Self::State) -> i128 {
(*self).unix_timestamp_nanos() / 1_000
}
#[inline]
fn unix_timestamp_nanoseconds(&self, _: &mut Self::State) -> i128 {
(*self).unix_timestamp_nanos()
}
}