use crate::error::DateTimeFormatterLoadError;
use crate::external_loaders::*;
use crate::fieldsets::builder::FieldSetBuilder;
use crate::fieldsets::enums::CompositeFieldSet;
use crate::format::datetime::try_write_pattern_items;
use crate::format::DateTimeInputUnchecked;
use crate::pattern::*;
use crate::preferences::{CalendarAlgorithm, HourCycle, NumberingSystem};
use crate::raw::neo::*;
use crate::scaffold::*;
use crate::scaffold::{
AllInputMarkers, ConvertCalendar, DateDataMarkers, DateInputMarkers, DateTimeMarkers, GetField,
InFixedCalendar, InSameCalendar, TimeMarkers, TypedDateDataMarkers, ZoneMarkers,
};
use crate::size_test_macro::size_test;
use crate::MismatchedCalendarError;
use core::fmt;
use core::marker::PhantomData;
use icu_calendar::{preferences::CalendarPreferences, AnyCalendar, IntoAnyCalendar};
use icu_decimal::DecimalFormatterPreferences;
use icu_locale_core::preferences::{define_preferences, prefs_convert};
use icu_provider::prelude::*;
use writeable::{impl_display_with_writeable, Writeable};
define_preferences!(
[Copy]
DateTimeFormatterPreferences,
{
numbering_system: NumberingSystem,
hour_cycle: HourCycle,
calendar_algorithm: CalendarAlgorithm
}
);
#[test]
fn prefs() {
use icu_locale::locale;
assert_eq!(
DateTimeFormatterPreferences::from_locale_strict(&locale!("en-US-u-hc-h23"))
.unwrap()
.hour_cycle,
Some(HourCycle::H23)
);
assert_eq!(
DateTimeFormatterPreferences::from_locale_strict(&locale!("en-US-u-hc-h24"))
.unwrap_err()
.hour_cycle,
None
);
}
prefs_convert!(DateTimeFormatterPreferences, DecimalFormatterPreferences, {
numbering_system
});
prefs_convert!(DateTimeFormatterPreferences, CalendarPreferences, {
calendar_algorithm
});
macro_rules! gen_buffer_constructors_with_external_loader {
(@compiletime_fset, $fset:ident, $compiled_fn:ident, $buffer_fn:ident, $internal_fn:ident) => {
#[doc = icu_provider::gen_buffer_unstable_docs!(BUFFER, Self::$compiled_fn)]
#[cfg(feature = "serde")]
pub fn $buffer_fn<P>(
provider: &P,
prefs: DateTimeFormatterPreferences,
field_set_with_options: $fset,
) -> Result<Self, DateTimeFormatterLoadError>
where
P: BufferProvider + ?Sized,
{
use crate::provider::compat::CompatProvider;
let deser_provider = provider.as_deserializing();
let compat_provider = CompatProvider(&deser_provider, &provider);
Self::$internal_fn(
&compat_provider,
&ExternalLoaderBuffer(provider),
prefs,
field_set_with_options.get_field(),
)
}
};
}
size_test!(FixedCalendarDateTimeFormatter<icu_calendar::Gregorian, crate::fieldsets::YMD>, typed_neo_year_month_day_formatter_size, 328);
#[doc = typed_neo_year_month_day_formatter_size!()]
#[derive(Debug, Clone)]
pub struct FixedCalendarDateTimeFormatter<C: CldrCalendar, FSet: DateTimeNamesMarker> {
pub(crate) selection: DateTimeZonePatternSelectionData,
pub(crate) names: RawDateTimeNames<FSet>,
_calendar: PhantomData<C>,
}
impl<C: CldrCalendar, FSet: DateTimeMarkers> FixedCalendarDateTimeFormatter<C, FSet>
where
FSet::D: TypedDateDataMarkers<C>,
FSet::T: TimeMarkers,
FSet::Z: ZoneMarkers,
FSet: GetField<CompositeFieldSet>,
{
#[cfg(feature = "compiled_data")]
pub fn try_new(
prefs: DateTimeFormatterPreferences,
field_set_with_options: FSet,
) -> Result<Self, DateTimeFormatterLoadError>
where
crate::provider::Baked: AllFixedCalendarFormattingDataMarkers<C, FSet>,
{
Self::try_new_internal(
&crate::provider::Baked,
&ExternalLoaderCompiledData,
prefs,
field_set_with_options.get_field(),
)
}
gen_buffer_constructors_with_external_loader!(
@compiletime_fset,
FSet,
try_new,
try_new_with_buffer_provider,
try_new_internal
);
#[doc = icu_provider::gen_buffer_unstable_docs!(UNSTABLE, Self::try_new)]
pub fn try_new_unstable<P>(
provider: &P,
prefs: DateTimeFormatterPreferences,
field_set_with_options: FSet,
) -> Result<Self, DateTimeFormatterLoadError>
where
P: ?Sized
+ AllFixedCalendarFormattingDataMarkers<C, FSet>
+ AllFixedCalendarExternalDataMarkers,
{
Self::try_new_internal(
provider,
&ExternalLoaderUnstable(provider),
prefs,
field_set_with_options.get_field(),
)
}
}
impl<C: CldrCalendar, FSet: DateTimeMarkers> FixedCalendarDateTimeFormatter<C, FSet>
where
FSet::D: TypedDateDataMarkers<C>,
FSet::T: TimeMarkers,
FSet::Z: ZoneMarkers,
{
fn try_new_internal<P, L>(
provider: &P,
loader: &L,
prefs: DateTimeFormatterPreferences,
field_set_with_options: CompositeFieldSet,
) -> Result<Self, DateTimeFormatterLoadError>
where
P: ?Sized + AllFixedCalendarFormattingDataMarkers<C, FSet>,
L: DecimalFormatterLoader,
{
let names = RawDateTimeNames::new_without_number_formatting();
Self::try_new_internal_with_names(
provider,
provider,
loader,
prefs,
field_set_with_options,
names,
DateTimeNamesMetadata::new_empty(), )
.map_err(|e| e.0)
}
#[expect(clippy::result_large_err)] pub(crate) fn try_new_internal_with_names<P0, P1, L>(
provider_p: &P0,
provider: &P1,
loader: &L,
prefs: DateTimeFormatterPreferences,
field_set_with_options: CompositeFieldSet,
mut names: RawDateTimeNames<FSet>,
mut names_metadata: DateTimeNamesMetadata,
) -> Result<
Self,
(
DateTimeFormatterLoadError,
(RawDateTimeNames<FSet>, DateTimeNamesMetadata),
),
>
where
P0: ?Sized + AllFixedCalendarPatternDataMarkers<C, FSet>,
P1: ?Sized + AllFixedCalendarFormattingDataMarkers<C, FSet>,
L: DecimalFormatterLoader,
{
let selection = DateTimeZonePatternSelectionData::try_new_with_skeleton(
&<FSet::D as TypedDateDataMarkers<C>>::DateSkeletonPatternsV1::bind(provider_p),
&<FSet::T as TimeMarkers>::TimeSkeletonPatternsV1::bind(provider_p),
&FSet::GluePatternV1::bind(provider_p),
prefs,
field_set_with_options,
);
let selection = match selection {
Ok(selection) => selection,
Err(e) => return Err((DateTimeFormatterLoadError::Data(e), (names, names_metadata))),
};
let result = names.load_for_pattern(
&<FSet::D as TypedDateDataMarkers<C>>::YearNamesV1::bind(provider),
&<FSet::D as TypedDateDataMarkers<C>>::MonthNamesV1::bind(provider),
&<FSet::D as TypedDateDataMarkers<C>>::WeekdayNamesV1::bind(provider),
&<FSet::T as TimeMarkers>::DayPeriodNamesV1::bind(provider),
&<FSet::Z as ZoneMarkers>::EssentialsV1::bind(provider),
&<FSet::Z as ZoneMarkers>::LocationsV1::bind(provider),
&<FSet::Z as ZoneMarkers>::LocationsRootV1::bind(provider),
&<FSet::Z as ZoneMarkers>::ExemplarCitiesV1::bind(provider),
&<FSet::Z as ZoneMarkers>::ExemplarCitiesRootV1::bind(provider),
&<FSet::Z as ZoneMarkers>::GenericLongV1::bind(provider),
&<FSet::Z as ZoneMarkers>::GenericShortV1::bind(provider),
&<FSet::Z as ZoneMarkers>::StandardLongV1::bind(provider),
&<FSet::Z as ZoneMarkers>::SpecificLongV1::bind(provider),
&<FSet::Z as ZoneMarkers>::SpecificShortV1::bind(provider),
&<FSet::Z as ZoneMarkers>::MetazonePeriodV1::bind(provider),
loader, prefs,
selection.pattern_items_for_data_loading(),
&mut names_metadata,
);
match result {
Ok(()) => (),
Err(e) => {
return Err((
DateTimeFormatterLoadError::Names(e),
(names, names_metadata),
))
}
};
Ok(Self {
selection,
names,
_calendar: PhantomData,
})
}
}
impl<C: CldrCalendar, FSet: DateTimeMarkers> FixedCalendarDateTimeFormatter<C, FSet>
where
FSet::D: DateInputMarkers,
FSet::T: TimeMarkers,
FSet::Z: ZoneMarkers,
{
pub fn format<I>(&self, input: &I) -> FormattedDateTime<'_>
where
I: ?Sized + InFixedCalendar<C> + AllInputMarkers<FSet>,
{
let input =
DateTimeInputUnchecked::extract_from_neo_input::<FSet::D, FSet::T, FSet::Z, I>(input);
FormattedDateTime {
pattern: self.selection.select(&input),
input,
names: self.names.as_borrowed(),
}
}
}
size_test!(
DateTimeFormatter<crate::fieldsets::YMD>,
neo_year_month_day_formatter_size,
336
);
#[doc = neo_year_month_day_formatter_size!()]
#[derive(Debug, Clone)]
pub struct DateTimeFormatter<FSet: DateTimeNamesMarker> {
pub(crate) selection: DateTimeZonePatternSelectionData,
pub(crate) names: RawDateTimeNames<FSet>,
pub(crate) calendar: FormattableAnyCalendar,
}
impl<FSet: DateTimeMarkers> DateTimeFormatter<FSet>
where
FSet::D: DateDataMarkers,
FSet::T: TimeMarkers,
FSet::Z: ZoneMarkers,
FSet: GetField<CompositeFieldSet>,
{
#[inline(never)]
#[cfg(feature = "compiled_data")]
pub fn try_new(
prefs: DateTimeFormatterPreferences,
field_set_with_options: FSet,
) -> Result<Self, DateTimeFormatterLoadError>
where
crate::provider::Baked: AllAnyCalendarFormattingDataMarkers<FSet>,
{
Self::try_new_internal(
&crate::provider::Baked,
&ExternalLoaderCompiledData,
prefs,
field_set_with_options.get_field(),
)
}
gen_buffer_constructors_with_external_loader!(
@compiletime_fset,
FSet,
try_new,
try_new_with_buffer_provider,
try_new_internal
);
#[doc = icu_provider::gen_buffer_unstable_docs!(UNSTABLE, Self::try_new)]
pub fn try_new_unstable<P>(
provider: &P,
prefs: DateTimeFormatterPreferences,
field_set_with_options: FSet,
) -> Result<Self, DateTimeFormatterLoadError>
where
P: ?Sized + AllAnyCalendarFormattingDataMarkers<FSet> + AllAnyCalendarExternalDataMarkers,
{
Self::try_new_internal(
provider,
&ExternalLoaderUnstable(provider),
prefs,
field_set_with_options.get_field(),
)
}
}
impl<FSet: DateTimeMarkers> DateTimeFormatter<FSet>
where
FSet::D: DateDataMarkers,
FSet::T: TimeMarkers,
FSet::Z: ZoneMarkers,
{
fn try_new_internal<P, L>(
provider: &P,
loader: &L,
prefs: DateTimeFormatterPreferences,
field_set_with_options: CompositeFieldSet,
) -> Result<Self, DateTimeFormatterLoadError>
where
P: ?Sized + AllAnyCalendarFormattingDataMarkers<FSet>,
L: DecimalFormatterLoader + FormattableAnyCalendarLoader,
{
let calendar = FormattableAnyCalendarLoader::load(loader, (&prefs).into())?;
let names = RawDateTimeNames::new_without_number_formatting();
Self::try_new_internal_with_calendar_and_names(
provider,
provider,
loader,
prefs,
field_set_with_options,
calendar,
names,
DateTimeNamesMetadata::new_empty(), )
.map_err(|e| e.0)
}
#[expect(clippy::result_large_err)] #[expect(clippy::too_many_arguments)] #[expect(clippy::type_complexity)] pub(crate) fn try_new_internal_with_calendar_and_names<P0, P1, L>(
provider_p: &P0,
provider: &P1,
loader: &L,
prefs: DateTimeFormatterPreferences,
field_set_with_options: CompositeFieldSet,
calendar: FormattableAnyCalendar,
mut names: RawDateTimeNames<FSet>,
mut names_metadata: DateTimeNamesMetadata,
) -> Result<
Self,
(
DateTimeFormatterLoadError,
(
FormattableAnyCalendar,
RawDateTimeNames<FSet>,
DateTimeNamesMetadata,
),
),
>
where
P0: ?Sized + AllAnyCalendarPatternDataMarkers<FSet>,
P1: ?Sized + AllAnyCalendarFormattingDataMarkers<FSet>,
L: DecimalFormatterLoader,
{
let selection = DateTimeZonePatternSelectionData::try_new_with_skeleton(
&FormattableAnyCalendarNamesLoader::<<FSet::D as DateDataMarkers>::Skel, _>::new(
provider_p, &calendar,
),
&<FSet::T as TimeMarkers>::TimeSkeletonPatternsV1::bind(provider_p),
&FSet::GluePatternV1::bind(provider_p),
prefs,
field_set_with_options,
);
let selection = match selection {
Ok(selection) => selection,
Err(e) => {
return Err((
DateTimeFormatterLoadError::Data(e),
(calendar, names, names_metadata),
))
}
};
let result = names.load_for_pattern(
&FormattableAnyCalendarNamesLoader::<<FSet::D as DateDataMarkers>::Year, _>::new(
provider, &calendar,
),
&FormattableAnyCalendarNamesLoader::<<FSet::D as DateDataMarkers>::Month, _>::new(
provider, &calendar,
),
&<FSet::D as DateDataMarkers>::WeekdayNamesV1::bind(provider),
&<FSet::T as TimeMarkers>::DayPeriodNamesV1::bind(provider),
&<FSet::Z as ZoneMarkers>::EssentialsV1::bind(provider),
&<FSet::Z as ZoneMarkers>::LocationsV1::bind(provider),
&<FSet::Z as ZoneMarkers>::LocationsRootV1::bind(provider),
&<FSet::Z as ZoneMarkers>::ExemplarCitiesRootV1::bind(provider),
&<FSet::Z as ZoneMarkers>::ExemplarCitiesV1::bind(provider),
&<FSet::Z as ZoneMarkers>::GenericLongV1::bind(provider),
&<FSet::Z as ZoneMarkers>::GenericShortV1::bind(provider),
&<FSet::Z as ZoneMarkers>::StandardLongV1::bind(provider),
&<FSet::Z as ZoneMarkers>::SpecificLongV1::bind(provider),
&<FSet::Z as ZoneMarkers>::SpecificShortV1::bind(provider),
&<FSet::Z as ZoneMarkers>::MetazonePeriodV1::bind(provider),
loader, prefs,
selection.pattern_items_for_data_loading(),
&mut names_metadata,
);
match result {
Ok(()) => (),
Err(e) => {
return Err((
DateTimeFormatterLoadError::Names(e),
(calendar, names, names_metadata),
))
}
};
Ok(Self {
selection,
names,
calendar,
})
}
}
impl<FSet: DateTimeMarkers> DateTimeFormatter<FSet>
where
FSet::D: DateInputMarkers,
FSet::T: TimeMarkers,
FSet::Z: ZoneMarkers,
{
pub fn format_same_calendar<I>(
&self,
datetime: &I,
) -> Result<FormattedDateTime<'_>, MismatchedCalendarError>
where
I: ?Sized + InSameCalendar + AllInputMarkers<FSet>,
{
datetime.check_any_calendar_kind(self.calendar.any_calendar().kind())?;
let datetime = DateTimeInputUnchecked::extract_from_neo_input::<FSet::D, FSet::T, FSet::Z, I>(
datetime,
);
Ok(FormattedDateTime {
pattern: self.selection.select(&datetime),
input: datetime,
names: self.names.as_borrowed(),
})
}
pub fn format<'a, I>(&'a self, datetime: &I) -> FormattedDateTime<'a>
where
I: ?Sized + ConvertCalendar,
I::Converted<'a>: Sized + AllInputMarkers<FSet>,
{
let datetime = datetime.to_calendar(self.calendar.any_calendar());
let datetime = DateTimeInputUnchecked::extract_from_neo_input::<
FSet::D,
FSet::T,
FSet::Z,
I::Converted<'a>,
>(&datetime);
FormattedDateTime {
pattern: self.selection.select(&datetime),
input: datetime,
names: self.names.as_borrowed(),
}
}
}
impl<C: CldrCalendar, FSet: DateTimeMarkers> FixedCalendarDateTimeFormatter<C, FSet> {
pub fn into_formatter(self, calendar: C) -> DateTimeFormatter<FSet>
where
C: IntoFormattableAnyCalendar,
{
DateTimeFormatter {
selection: self.selection,
names: self.names,
calendar: FormattableAnyCalendar::from_calendar(calendar),
}
}
pub fn cast_into_fset<FSet2: DateTimeNamesFrom<FSet>>(
self,
) -> FixedCalendarDateTimeFormatter<C, FSet2> {
FixedCalendarDateTimeFormatter {
selection: self.selection,
names: self.names.cast_into_fset(),
_calendar: PhantomData,
}
}
pub fn to_field_set_builder(&self) -> FieldSetBuilder {
self.selection.to_builder()
}
}
impl<FSet: DateTimeMarkers> DateTimeFormatter<FSet> {
pub fn try_into_typed_formatter<C>(
self,
) -> Result<FixedCalendarDateTimeFormatter<C, FSet>, MismatchedCalendarError>
where
C: CldrCalendar + IntoAnyCalendar,
{
if let Err(cal) = C::from_any(self.calendar.take_any_calendar()) {
return Err(MismatchedCalendarError {
this_kind: cal.kind(),
date_kind: None,
});
}
Ok(FixedCalendarDateTimeFormatter {
selection: self.selection,
names: self.names,
_calendar: PhantomData,
})
}
pub fn cast_into_fset<FSet2: DateTimeNamesFrom<FSet>>(self) -> DateTimeFormatter<FSet2> {
DateTimeFormatter {
selection: self.selection,
names: self.names.cast_into_fset(),
calendar: self.calendar,
}
}
pub fn calendar(&self) -> icu_calendar::Ref<'_, AnyCalendar> {
icu_calendar::Ref(self.calendar.any_calendar())
}
pub fn to_field_set_builder(&self) -> FieldSetBuilder {
self.selection.to_builder()
}
}
pub type NoCalendarFormatter<FSet> = FixedCalendarDateTimeFormatter<(), FSet>;
#[derive(Debug)]
pub struct FormattedDateTime<'a> {
pattern: DateTimeZonePatternDataBorrowed<'a>,
input: DateTimeInputUnchecked,
names: RawDateTimeNamesBorrowed<'a>,
}
impl Writeable for FormattedDateTime<'_> {
fn write_to_parts<S: writeable::PartsWrite + ?Sized>(
&self,
sink: &mut S,
) -> Result<(), fmt::Error> {
let result = try_write_pattern_items(
self.pattern.metadata(),
self.pattern.iter_items(),
&self.input,
&self.names,
self.names.decimal_formatter,
sink,
);
match result {
Ok(Ok(())) => Ok(()),
Err(fmt::Error) => Err(fmt::Error),
Ok(Err(e)) => {
debug_assert!(false, "unexpected error in FormattedDateTime: {e:?}");
Ok(())
}
}
}
}
impl_display_with_writeable!(FormattedDateTime<'_>);
impl FormattedDateTime<'_> {
pub fn pattern(&self) -> DateTimePattern {
self.pattern.to_pattern()
}
}