#[cfg(feature = "experimental")]
use crate::neo_marker::{DateInputMarkers, NeoGetField, TimeMarkers, ZoneMarkers};
use crate::provider::time_zones::{MetazoneId, TimeZoneBcp47Id};
use icu_calendar::any_calendar::AnyCalendarKind;
use icu_calendar::week::{RelativeUnit, WeekCalculator};
use icu_calendar::Calendar;
use icu_calendar::{AsCalendar, Date, DateTime, Iso};
use icu_timezone::{CustomTimeZone, UtcOffset, ZoneVariant};
pub(crate) use icu_calendar::types::{
DayOfMonth, DayOfYearInfo, IsoHour, IsoMinute, IsoSecond, IsoWeekday, MonthInfo, NanoSecond,
Time, WeekOfMonth, WeekOfYear, YearInfo,
};
pub trait DateInput {
type Calendar: Calendar;
fn year(&self) -> Option<YearInfo>;
fn month(&self) -> Option<MonthInfo>;
fn day_of_month(&self) -> Option<DayOfMonth>;
fn iso_weekday(&self) -> Option<IsoWeekday>;
fn day_of_year_info(&self) -> Option<DayOfYearInfo>;
fn any_calendar_kind(&self) -> Option<AnyCalendarKind>;
fn to_iso(&self) -> Date<Iso>;
}
pub trait IsoTimeInput {
fn hour(&self) -> Option<IsoHour>;
fn minute(&self) -> Option<IsoMinute>;
fn second(&self) -> Option<IsoSecond>;
fn nanosecond(&self) -> Option<NanoSecond>;
}
pub trait TimeZoneInput {
fn offset(&self) -> Option<UtcOffset>;
fn time_zone_id(&self) -> Option<TimeZoneBcp47Id>;
fn metazone_id(&self) -> Option<MetazoneId>;
fn zone_variant(&self) -> Option<ZoneVariant>;
}
pub trait DateTimeInput: DateInput + IsoTimeInput {}
impl<T> DateTimeInput for T where T: DateInput + IsoTimeInput {}
#[derive(Default, Debug, Copy, Clone)]
pub(crate) struct ExtractedDateTimeInput {
year: Option<YearInfo>,
month: Option<MonthInfo>,
day_of_month: Option<DayOfMonth>,
iso_weekday: Option<IsoWeekday>,
day_of_year_info: Option<DayOfYearInfo>,
any_calendar_kind: Option<AnyCalendarKind>,
hour: Option<IsoHour>,
minute: Option<IsoMinute>,
second: Option<IsoSecond>,
nanosecond: Option<NanoSecond>,
time_zone: ExtractedTimeZoneInput,
}
#[derive(Debug, Copy, Clone, Default)]
pub(crate) struct ExtractedTimeZoneInput {
pub(crate) offset: Option<UtcOffset>,
pub(crate) time_zone_id: Option<TimeZoneBcp47Id>,
pub(crate) metazone_id: Option<MetazoneId>,
pub(crate) zone_variant: Option<ZoneVariant>,
}
impl ExtractedDateTimeInput {
pub(crate) fn extract_from<T: DateTimeInput>(input: &T) -> Self {
Self {
year: input.year(),
month: input.month(),
day_of_month: input.day_of_month(),
iso_weekday: input.iso_weekday(),
day_of_year_info: input.day_of_year_info(),
any_calendar_kind: input.any_calendar_kind(),
hour: input.hour(),
minute: input.minute(),
second: input.second(),
nanosecond: input.nanosecond(),
..Default::default()
}
}
pub(crate) fn extract_from_date<T: DateInput>(input: &T) -> Self {
Self {
year: input.year(),
month: input.month(),
day_of_month: input.day_of_month(),
iso_weekday: input.iso_weekday(),
day_of_year_info: input.day_of_year_info(),
any_calendar_kind: input.any_calendar_kind(),
..Default::default()
}
}
pub(crate) fn extract_from_time<T: IsoTimeInput>(input: &T) -> Self {
Self {
hour: input.hour(),
minute: input.minute(),
second: input.second(),
nanosecond: input.nanosecond(),
..Default::default()
}
}
#[cfg(feature = "experimental")]
pub(crate) fn extract_from_neo_input<D, T, Z, I>(input: &I) -> Self
where
D: DateInputMarkers,
T: TimeMarkers,
Z: ZoneMarkers,
I: ?Sized
+ NeoGetField<D::YearInput>
+ NeoGetField<D::MonthInput>
+ NeoGetField<D::DayOfMonthInput>
+ NeoGetField<D::DayOfWeekInput>
+ NeoGetField<D::DayOfYearInput>
+ NeoGetField<D::AnyCalendarKindInput>
+ NeoGetField<T::HourInput>
+ NeoGetField<T::MinuteInput>
+ NeoGetField<T::SecondInput>
+ NeoGetField<T::NanoSecondInput>
+ NeoGetField<Z::TimeZoneOffsetInput>
+ NeoGetField<Z::TimeZoneIdInput>
+ NeoGetField<Z::TimeZoneMetazoneInput>
+ NeoGetField<Z::TimeZoneVariantInput>,
{
Self {
year: NeoGetField::<D::YearInput>::get_field(input).into(),
month: NeoGetField::<D::MonthInput>::get_field(input).into(),
day_of_month: NeoGetField::<D::DayOfMonthInput>::get_field(input).into(),
iso_weekday: NeoGetField::<D::DayOfWeekInput>::get_field(input).into(),
day_of_year_info: NeoGetField::<D::DayOfYearInput>::get_field(input).into(),
any_calendar_kind: NeoGetField::<D::AnyCalendarKindInput>::get_field(input).into(),
hour: NeoGetField::<T::HourInput>::get_field(input).into(),
minute: NeoGetField::<T::MinuteInput>::get_field(input).into(),
second: NeoGetField::<T::SecondInput>::get_field(input).into(),
nanosecond: NeoGetField::<T::NanoSecondInput>::get_field(input).into(),
time_zone: ExtractedTimeZoneInput {
offset: NeoGetField::<Z::TimeZoneOffsetInput>::get_field(input).into(),
time_zone_id: NeoGetField::<Z::TimeZoneIdInput>::get_field(input).into(),
metazone_id: NeoGetField::<Z::TimeZoneMetazoneInput>::get_field(input).into(),
zone_variant: NeoGetField::<Z::TimeZoneVariantInput>::get_field(input).into(),
},
}
}
pub(crate) fn time_zone(&self) -> &ExtractedTimeZoneInput {
&self.time_zone
}
#[cfg(feature = "experimental")]
pub(crate) fn should_display_era(&self) -> bool {
match self.any_calendar_kind() {
None => true,
Some(AnyCalendarKind::Buddhist)
| Some(AnyCalendarKind::Coptic)
| Some(AnyCalendarKind::Ethiopian)
| Some(AnyCalendarKind::EthiopianAmeteAlem)
| Some(AnyCalendarKind::Hebrew)
| Some(AnyCalendarKind::Indian)
| Some(AnyCalendarKind::IslamicCivil)
| Some(AnyCalendarKind::IslamicObservational)
| Some(AnyCalendarKind::IslamicTabular)
| Some(AnyCalendarKind::IslamicUmmAlQura)
| Some(AnyCalendarKind::Japanese)
| Some(AnyCalendarKind::JapaneseExtended)
| Some(AnyCalendarKind::Persian)
| Some(AnyCalendarKind::Roc) => true,
Some(AnyCalendarKind::Chinese)
| Some(AnyCalendarKind::Dangi)
| Some(AnyCalendarKind::Iso) => false,
Some(AnyCalendarKind::Gregorian) => match self.year() {
None => true,
Some(year) if year.era_year_or_extended() < 1000 => true,
Some(year)
if year.formatting_era()
!= Some(icu_calendar::types::Era(tinystr::tinystr!(16, "ce"))) =>
{
true
}
Some(_) => false,
},
Some(_) => {
debug_assert!(false, "unknown calendar during era display resolution");
true
}
}
}
}
impl DateInput for ExtractedDateTimeInput {
type Calendar = icu_calendar::any_calendar::AnyCalendar;
fn year(&self) -> Option<YearInfo> {
self.year
}
fn month(&self) -> Option<MonthInfo> {
self.month
}
fn day_of_month(&self) -> Option<DayOfMonth> {
self.day_of_month
}
fn iso_weekday(&self) -> Option<IsoWeekday> {
self.iso_weekday
}
fn day_of_year_info(&self) -> Option<DayOfYearInfo> {
self.day_of_year_info
}
fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
self.any_calendar_kind
}
fn to_iso(&self) -> Date<Iso> {
unreachable!("ExtractedDateTimeInput should never be directly passed to DateTimeFormatter")
}
}
impl IsoTimeInput for ExtractedDateTimeInput {
fn hour(&self) -> Option<IsoHour> {
self.hour
}
fn minute(&self) -> Option<IsoMinute> {
self.minute
}
fn second(&self) -> Option<IsoSecond> {
self.second
}
fn nanosecond(&self) -> Option<NanoSecond> {
self.nanosecond
}
}
impl TimeZoneInput for ExtractedTimeZoneInput {
fn offset(&self) -> Option<UtcOffset> {
self.offset
}
fn time_zone_id(&self) -> Option<TimeZoneBcp47Id> {
self.time_zone_id
}
fn metazone_id(&self) -> Option<MetazoneId> {
self.metazone_id
}
fn zone_variant(&self) -> Option<ZoneVariant> {
self.zone_variant
}
}
impl ExtractedDateTimeInput {
pub(crate) fn week_of_month(
&self,
calculator: &WeekCalculator,
) -> Result<WeekOfMonth, &'static str> {
let day_of_month = self.day_of_month().ok_or("day_of_month")?;
let iso_weekday = self.iso_weekday().ok_or("iso_weekday")?;
Ok(calculator.week_of_month(day_of_month, iso_weekday))
}
pub(crate) fn week_of_year(
&self,
calculator: &WeekCalculator,
) -> Result<(YearInfo, WeekOfYear), &'static str> {
let day_of_year_info = self.day_of_year_info().ok_or("day_of_year_info")?;
let iso_weekday = self.iso_weekday().ok_or("iso_weekday")?;
let week_of = calculator.week_of_year(day_of_year_info, iso_weekday);
let year = match week_of.unit {
RelativeUnit::Previous => day_of_year_info.prev_year,
RelativeUnit::Current => self.year().ok_or("year")?,
RelativeUnit::Next => day_of_year_info.next_year,
};
Ok((year, WeekOfYear(week_of.week as u32)))
}
}
impl<C: Calendar, A: AsCalendar<Calendar = C>> DateInput for Date<A> {
type Calendar = C;
fn year(&self) -> Option<YearInfo> {
Some(self.year())
}
fn month(&self) -> Option<MonthInfo> {
Some(self.month())
}
fn day_of_month(&self) -> Option<DayOfMonth> {
Some(self.day_of_month())
}
fn iso_weekday(&self) -> Option<IsoWeekday> {
Some(self.day_of_week())
}
fn day_of_year_info(&self) -> Option<DayOfYearInfo> {
Some(self.day_of_year_info())
}
fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
self.calendar().any_calendar_kind()
}
fn to_iso(&self) -> Date<Iso> {
Date::to_iso(self)
}
}
impl<C: Calendar, A: AsCalendar<Calendar = C>> DateInput for DateTime<A> {
type Calendar = C;
fn year(&self) -> Option<YearInfo> {
Some(self.date.year())
}
fn month(&self) -> Option<MonthInfo> {
Some(self.date.month())
}
fn day_of_month(&self) -> Option<DayOfMonth> {
Some(self.date.day_of_month())
}
fn iso_weekday(&self) -> Option<IsoWeekday> {
Some(self.date.day_of_week())
}
fn day_of_year_info(&self) -> Option<DayOfYearInfo> {
Some(self.date.day_of_year_info())
}
fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
self.date.calendar().any_calendar_kind()
}
fn to_iso(&self) -> Date<Iso> {
Date::to_iso(&self.date)
}
}
impl<A: AsCalendar> IsoTimeInput for DateTime<A> {
fn hour(&self) -> Option<IsoHour> {
Some(self.time.hour)
}
fn minute(&self) -> Option<IsoMinute> {
Some(self.time.minute)
}
fn second(&self) -> Option<IsoSecond> {
Some(self.time.second)
}
fn nanosecond(&self) -> Option<NanoSecond> {
Some(self.time.nanosecond)
}
}
impl TimeZoneInput for CustomTimeZone {
fn offset(&self) -> Option<UtcOffset> {
self.offset
}
fn time_zone_id(&self) -> Option<TimeZoneBcp47Id> {
self.time_zone_id
}
fn metazone_id(&self) -> Option<MetazoneId> {
self.metazone_id
}
fn zone_variant(&self) -> Option<ZoneVariant> {
self.zone_variant
}
}
impl IsoTimeInput for Time {
fn hour(&self) -> Option<IsoHour> {
Some(self.hour)
}
fn minute(&self) -> Option<IsoMinute> {
Some(self.minute)
}
fn second(&self) -> Option<IsoSecond> {
Some(self.second)
}
fn nanosecond(&self) -> Option<NanoSecond> {
Some(self.nanosecond)
}
}