use core::mem::MaybeUninit;
use core::num::NonZero;
use num_conv::prelude::*;
use crate::format_description::Period;
use crate::{
Date, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcDateTime, UtcOffset, Weekday,
};
#[derive(Debug)]
pub(crate) struct DateState {
day: Option<NonZero<u8>>,
month: Option<Month>,
iso_week: Option<NonZero<u8>>,
iso_year_is_initialized: bool,
iso_year: MaybeUninit<i32>,
}
impl Default for DateState {
fn default() -> Self {
Self {
day: Default::default(),
month: Default::default(),
iso_week: Default::default(),
iso_year_is_initialized: Default::default(),
iso_year: MaybeUninit::uninit(),
}
}
}
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 => u8;
("date") month => Month;
("date") ordinal => u16;
("date") weekday => Weekday;
("date") iso_week_number => u8;
("date") monday_based_week => u8;
("date") sunday_based_week => u8;
("date") calendar_year => i32;
("date") iso_year => i32;
("time") hour => u8;
("time") minute => u8;
("time") period => Period;
("time") second => u8;
("time") nanosecond => u32;
("offset") offset_is_negative => bool;
("offset") offset_is_utc => bool;
("offset") offset_hour => i8;
("offset") offset_minute => i8;
("offset") offset_second => i8;
("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) -> u8 {
(*self).hour()
}
#[inline]
fn minute(&self, _: &mut Self::State) -> u8 {
(*self).minute()
}
#[inline]
fn period(&self, _: &mut Self::State) -> Period {
if (*self).hour() < 12 {
Period::Am
} else {
Period::Pm
}
}
#[inline]
fn second(&self, _: &mut Self::State) -> u8 {
(*self).second()
}
#[inline]
fn nanosecond(&self, _: &mut Self::State) -> u32 {
(*self).nanosecond()
}
}
impl ComponentProvider for Date {
type State = DateState;
const SUPPLIES_DATE: bool = true;
#[inline]
fn day(&self, state: &mut Self::State) -> u8 {
if let Some(day) = state.day {
return day.get();
}
let (_, month, day) = (*self).to_calendar_date();
state.month = Some(month);
state.day = Some(unsafe { NonZero::new_unchecked(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) -> u16 {
(*self).ordinal()
}
#[inline]
fn weekday(&self, _: &mut Self::State) -> Weekday {
(*self).weekday()
}
#[inline]
fn iso_week_number(&self, state: &mut Self::State) -> u8 {
if let Some(week) = state.iso_week {
return week.get();
}
let (iso_year, iso_week) = (*self).iso_year_week();
state.iso_year = MaybeUninit::new(iso_year);
state.iso_year_is_initialized = true;
state.iso_week = Some(unsafe { NonZero::new_unchecked(iso_week) });
iso_week
}
#[inline]
fn monday_based_week(&self, _: &mut Self::State) -> u8 {
(*self).monday_based_week()
}
#[inline]
fn sunday_based_week(&self, _: &mut Self::State) -> u8 {
(*self).sunday_based_week()
}
#[inline]
fn calendar_year(&self, _: &mut Self::State) -> i32 {
(*self).year()
}
#[inline]
fn iso_year(&self, state: &mut Self::State) -> i32 {
if state.iso_year_is_initialized {
return unsafe { state.iso_year.assume_init() };
}
let (iso_year, iso_week) = (*self).iso_year_week();
state.iso_year = MaybeUninit::new(iso_year);
state.iso_year_is_initialized = true;
state.iso_week = Some(unsafe { NonZero::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 -> u8
month -> Month
ordinal -> u16
weekday -> Weekday
iso_week_number -> u8
monday_based_week -> u8
sunday_based_week -> u8
calendar_year -> i32
iso_year -> i32
});
delegate_providers!(time (&mut ()) {
hour -> u8
minute -> u8
period -> Period
second -> u8
nanosecond -> u32
});
}
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) -> i8 {
(*self).whole_hours()
}
#[inline]
fn offset_minute(&self, _: &mut Self::State) -> i8 {
(*self).minutes_past_hour()
}
#[inline]
fn offset_second(&self, _: &mut Self::State) -> i8 {
(*self).seconds_past_minute()
}
}
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 -> u8
month -> Month
ordinal -> u16
weekday -> Weekday
iso_week_number -> u8
monday_based_week -> u8
sunday_based_week -> u8
calendar_year -> i32
iso_year -> i32
});
delegate_providers!(time (&mut ()) {
hour -> u8
minute -> u8
period -> Period
second -> u8
nanosecond -> u32
});
#[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) -> i8 {
0
}
#[inline]
fn offset_minute(&self, _: &mut Self::State) -> i8 {
0
}
#[inline]
fn offset_second(&self, _: &mut Self::State) -> i8 {
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 -> u8
month -> Month
ordinal -> u16
weekday -> Weekday
iso_week_number -> u8
monday_based_week -> u8
sunday_based_week -> u8
calendar_year -> i32
iso_year -> i32
});
delegate_providers!(time (&mut ()) {
hour -> u8
minute -> u8
period -> Period
second -> u8
nanosecond -> u32
});
delegate_providers!(offset (&mut ()) {
offset_is_negative -> bool
offset_is_utc -> bool
offset_hour -> i8
offset_minute -> i8
offset_second -> i8
});
#[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()
}
}