Skip to content

Commit

Permalink
Create TypedNeoDateTimeFormatter and DateTimePattern types (#4415)
Browse files Browse the repository at this point in the history
  • Loading branch information
sffc authored Dec 30, 2023
1 parent f5f4cf6 commit ecc44ec
Show file tree
Hide file tree
Showing 19 changed files with 2,071 additions and 213 deletions.
4 changes: 4 additions & 0 deletions components/datetime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ required-features = ["compiled_data"]
name = "resolved_components"
required-features = ["experimental", "compiled_data"]

[[test]]
name = "simple_test"
required-features = ["compiled_data"]

[[test]]
name = "skeleton_serialization"
required-features = ["experimental"]
Expand Down
38 changes: 38 additions & 0 deletions components/datetime/benches/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use criterion::{criterion_group, criterion_main, Criterion};
use std::fmt::Write;

use icu_calendar::{DateTime, Gregorian};
use icu_datetime::neo::TypedNeoDateTimeFormatter;
use icu_datetime::TypedDateTimeFormatter;
use icu_datetime::{time_zone::TimeZoneFormatterOptions, TypedZonedDateTimeFormatter};
use icu_locid::Locale;
Expand Down Expand Up @@ -69,6 +70,43 @@ fn datetime_benches(c: &mut Criterion) {
#[cfg(feature = "experimental")]
bench_datetime_with_fixture("components", include_str!("fixtures/tests/components.json"));

#[cfg(feature = "experimental")]
let mut bench_neo_datetime_with_fixture = |name, file| {
let fxs = serde_json::from_str::<fixtures::Fixture>(file).unwrap();
group.bench_function(&format!("neo/datetime_{name}"), |b| {
b.iter(|| {
for fx in &fxs.0 {
let datetimes: Vec<DateTime<Gregorian>> = fx
.values
.iter()
.map(|value| {
mock::parse_gregorian_from_str(value).expect("Failed to parse value.")
})
.collect();
for setup in &fx.setups {
let locale: Locale = setup.locale.parse().expect("Failed to parse locale.");
let options = fixtures::get_options(&setup.options).unwrap();
let dtf = {
TypedNeoDateTimeFormatter::<Gregorian>::try_new(&locale.into(), options)
.expect("Failed to create TypedNeoDateTimeFormatter.")
};

let mut result = String::new();

for dt in &datetimes {
let fdt = dtf.format(dt);
write!(result, "{fdt}").expect("Failed to write to date time format.");
result.clear();
}
}
}
})
});
};

#[cfg(feature = "experimental")]
bench_neo_datetime_with_fixture("lengths", include_str!("fixtures/tests/lengths.json"));

let fxs = serde_json::from_str::<fixtures::Fixture>(include_str!(
"fixtures/tests/lengths_with_zones.json"
))
Expand Down
36 changes: 36 additions & 0 deletions components/datetime/src/calendar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ pub trait CldrCalendar: InternalCldrCalendar {
/// The data marker for loading month symbols for this calendar.
type MonthNamesV1Marker: KeyedDataMarker<Yokeable = MonthNamesV1<'static>>;

#[cfg(feature = "experimental")]
/// The data marker for loading a single date pattern for this calendar.
type DatePatternV1Marker: KeyedDataMarker<Yokeable = DatePatternV1<'static>>;

/// Checks if a given BCP 47 identifier is allowed to be used with this calendar
///
/// By default, just checks against DEFAULT_BCP_47_IDENTIFIER
Expand Down Expand Up @@ -91,6 +95,8 @@ impl CldrCalendar for Buddhist {
type YearNamesV1Marker = BuddhistYearNamesV1Marker;
#[cfg(feature = "experimental")]
type MonthNamesV1Marker = BuddhistMonthNamesV1Marker;
#[cfg(feature = "experimental")]
type DatePatternV1Marker = BuddhistDatePatternV1Marker;
}

impl CldrCalendar for Chinese {
Expand All @@ -101,6 +107,8 @@ impl CldrCalendar for Chinese {
type YearNamesV1Marker = ChineseYearNamesV1Marker;
#[cfg(feature = "experimental")]
type MonthNamesV1Marker = ChineseMonthNamesV1Marker;
#[cfg(feature = "experimental")]
type DatePatternV1Marker = ChineseDatePatternV1Marker;
}

impl CldrCalendar for Coptic {
Expand All @@ -111,6 +119,8 @@ impl CldrCalendar for Coptic {
type YearNamesV1Marker = CopticYearNamesV1Marker;
#[cfg(feature = "experimental")]
type MonthNamesV1Marker = CopticMonthNamesV1Marker;
#[cfg(feature = "experimental")]
type DatePatternV1Marker = CopticDatePatternV1Marker;
}

impl CldrCalendar for Dangi {
Expand All @@ -121,6 +131,8 @@ impl CldrCalendar for Dangi {
type YearNamesV1Marker = DangiYearNamesV1Marker;
#[cfg(feature = "experimental")]
type MonthNamesV1Marker = DangiMonthNamesV1Marker;
#[cfg(feature = "experimental")]
type DatePatternV1Marker = DangiDatePatternV1Marker;
}

impl CldrCalendar for Ethiopian {
Expand All @@ -131,6 +143,8 @@ impl CldrCalendar for Ethiopian {
type YearNamesV1Marker = EthiopianYearNamesV1Marker;
#[cfg(feature = "experimental")]
type MonthNamesV1Marker = EthiopianMonthNamesV1Marker;
#[cfg(feature = "experimental")]
type DatePatternV1Marker = EthiopianDatePatternV1Marker;
fn is_identifier_allowed_for_calendar(value: &Value) -> bool {
*value == value!("ethiopic") || *value == value!("ethioaa")
}
Expand All @@ -144,6 +158,8 @@ impl CldrCalendar for Gregorian {
type YearNamesV1Marker = GregorianYearNamesV1Marker;
#[cfg(feature = "experimental")]
type MonthNamesV1Marker = GregorianMonthNamesV1Marker;
#[cfg(feature = "experimental")]
type DatePatternV1Marker = GregorianDatePatternV1Marker;
}

impl CldrCalendar for Hebrew {
Expand All @@ -154,6 +170,8 @@ impl CldrCalendar for Hebrew {
type YearNamesV1Marker = HebrewYearNamesV1Marker;
#[cfg(feature = "experimental")]
type MonthNamesV1Marker = HebrewMonthNamesV1Marker;
#[cfg(feature = "experimental")]
type DatePatternV1Marker = HebrewDatePatternV1Marker;
}

impl CldrCalendar for Indian {
Expand All @@ -164,6 +182,8 @@ impl CldrCalendar for Indian {
type YearNamesV1Marker = IndianYearNamesV1Marker;
#[cfg(feature = "experimental")]
type MonthNamesV1Marker = IndianMonthNamesV1Marker;
#[cfg(feature = "experimental")]
type DatePatternV1Marker = IndianDatePatternV1Marker;
}

impl CldrCalendar for IslamicCivil {
Expand All @@ -177,6 +197,8 @@ impl CldrCalendar for IslamicCivil {
type YearNamesV1Marker = IslamicYearNamesV1Marker;
#[cfg(feature = "experimental")]
type MonthNamesV1Marker = IslamicMonthNamesV1Marker;
#[cfg(feature = "experimental")]
type DatePatternV1Marker = IslamicDatePatternV1Marker;
fn is_identifier_allowed_for_calendar(value: &Value) -> bool {
*value == value!("islamicc") || is_islamic_subcal(value, tinystr!(8, "civil"))
}
Expand All @@ -190,6 +212,8 @@ impl CldrCalendar for IslamicObservational {
type YearNamesV1Marker = IslamicYearNamesV1Marker;
#[cfg(feature = "experimental")]
type MonthNamesV1Marker = IslamicMonthNamesV1Marker;
#[cfg(feature = "experimental")]
type DatePatternV1Marker = IslamicDatePatternV1Marker;
}

impl CldrCalendar for IslamicTabular {
Expand All @@ -203,6 +227,8 @@ impl CldrCalendar for IslamicTabular {
type YearNamesV1Marker = IslamicYearNamesV1Marker;
#[cfg(feature = "experimental")]
type MonthNamesV1Marker = IslamicMonthNamesV1Marker;
#[cfg(feature = "experimental")]
type DatePatternV1Marker = IslamicDatePatternV1Marker;
fn is_identifier_allowed_for_calendar(value: &Value) -> bool {
is_islamic_subcal(value, tinystr!(8, "tbla"))
}
Expand All @@ -219,6 +245,8 @@ impl CldrCalendar for IslamicUmmAlQura {
type YearNamesV1Marker = IslamicYearNamesV1Marker;
#[cfg(feature = "experimental")]
type MonthNamesV1Marker = IslamicMonthNamesV1Marker;
#[cfg(feature = "experimental")]
type DatePatternV1Marker = IslamicDatePatternV1Marker;
fn is_identifier_allowed_for_calendar(value: &Value) -> bool {
is_islamic_subcal(value, tinystr!(8, "umalqura"))
}
Expand All @@ -232,6 +260,8 @@ impl CldrCalendar for Japanese {
type YearNamesV1Marker = JapaneseYearNamesV1Marker;
#[cfg(feature = "experimental")]
type MonthNamesV1Marker = JapaneseMonthNamesV1Marker;
#[cfg(feature = "experimental")]
type DatePatternV1Marker = JapaneseDatePatternV1Marker;
}

impl CldrCalendar for JapaneseExtended {
Expand All @@ -242,6 +272,8 @@ impl CldrCalendar for JapaneseExtended {
type YearNamesV1Marker = JapaneseExtendedYearNamesV1Marker;
#[cfg(feature = "experimental")]
type MonthNamesV1Marker = JapaneseExtendedMonthNamesV1Marker;
#[cfg(feature = "experimental")]
type DatePatternV1Marker = JapaneseExtendedDatePatternV1Marker;
}

impl CldrCalendar for Persian {
Expand All @@ -252,6 +284,8 @@ impl CldrCalendar for Persian {
type YearNamesV1Marker = PersianYearNamesV1Marker;
#[cfg(feature = "experimental")]
type MonthNamesV1Marker = PersianMonthNamesV1Marker;
#[cfg(feature = "experimental")]
type DatePatternV1Marker = PersianDatePatternV1Marker;
}

impl CldrCalendar for Roc {
Expand All @@ -262,6 +296,8 @@ impl CldrCalendar for Roc {
type YearNamesV1Marker = RocYearNamesV1Marker;
#[cfg(feature = "experimental")]
type MonthNamesV1Marker = RocMonthNamesV1Marker;
#[cfg(feature = "experimental")]
type DatePatternV1Marker = RocDatePatternV1Marker;
}

impl InternalCldrCalendar for Buddhist {}
Expand Down
161 changes: 161 additions & 0 deletions components/datetime/src/external_loaders.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

//! Internal traits and structs for loading data from other crates.
use icu_calendar::provider::WeekDataV2Marker;
use icu_calendar::week::WeekCalculator;
use icu_calendar::CalendarError;
use icu_decimal::options::FixedDecimalFormatterOptions;
use icu_decimal::provider::DecimalSymbolsV1Marker;
use icu_decimal::{DecimalError, FixedDecimalFormatter};
use icu_provider::prelude::*;

/// Trait for loading a FixedDecimalFormatter.
///
/// Implemented on the provider-specific loader types in this module.
pub(crate) trait FixedDecimalFormatterLoader {
fn load(
&self,
locale: &DataLocale,
options: FixedDecimalFormatterOptions,
) -> Result<FixedDecimalFormatter, DecimalError>;
}

/// Trait for loading a WeekCalculator.
///
/// Implemented on the provider-specific loader types in this module.
pub(crate) trait WeekCalculatorLoader {
fn load(&self, locale: &DataLocale) -> Result<WeekCalculator, CalendarError>;
}

/// Helper for type resolution with optional loader arguments
pub(crate) struct PhantomLoader {
_not_constructible: core::convert::Infallible,
}

impl FixedDecimalFormatterLoader for PhantomLoader {
fn load(
&self,
_locale: &DataLocale,
_options: FixedDecimalFormatterOptions,
) -> Result<FixedDecimalFormatter, DecimalError> {
unreachable!() // not constructible
}
}

impl WeekCalculatorLoader for PhantomLoader {
#[inline]
fn load(&self, _locale: &DataLocale) -> Result<WeekCalculator, CalendarError> {
unreachable!() // not constructible
}
}

/// Loader for types from other crates using compiled data.
#[cfg(feature = "compiled_data")]
pub(crate) struct ExternalLoaderCompiledData;

#[cfg(feature = "compiled_data")]
impl FixedDecimalFormatterLoader for ExternalLoaderCompiledData {
#[inline]
fn load(
&self,
locale: &DataLocale,
options: FixedDecimalFormatterOptions,
) -> Result<FixedDecimalFormatter, DecimalError> {
FixedDecimalFormatter::try_new(locale, options)
}
}

#[cfg(feature = "compiled_data")]
impl WeekCalculatorLoader for ExternalLoaderCompiledData {
#[inline]
fn load(&self, locale: &DataLocale) -> Result<WeekCalculator, CalendarError> {
WeekCalculator::try_new(locale)
}
}

/// Loader for types from other crates using [`AnyProvider`].
pub(crate) struct ExternalLoaderAny<'a, P: ?Sized>(pub &'a P);

impl<P> FixedDecimalFormatterLoader for ExternalLoaderAny<'_, P>
where
P: ?Sized + AnyProvider,
{
#[inline]
fn load(
&self,
locale: &DataLocale,
options: FixedDecimalFormatterOptions,
) -> Result<FixedDecimalFormatter, DecimalError> {
FixedDecimalFormatter::try_new_with_any_provider(self.0, locale, options)
}
}

impl<P> WeekCalculatorLoader for ExternalLoaderAny<'_, P>
where
P: ?Sized + AnyProvider,
{
#[inline]
fn load(&self, locale: &DataLocale) -> Result<WeekCalculator, CalendarError> {
WeekCalculator::try_new_with_any_provider(self.0, locale)
}
}

/// Loader for types from other crates using [`BufferProvider`].
#[cfg(feature = "serde")]
pub(crate) struct ExternalLoaderBuffer<'a, P: ?Sized>(pub &'a P);

#[cfg(feature = "serde")]
impl<P> FixedDecimalFormatterLoader for ExternalLoaderBuffer<'_, P>
where
P: ?Sized + BufferProvider,
{
#[inline]
fn load(
&self,
locale: &DataLocale,
options: FixedDecimalFormatterOptions,
) -> Result<FixedDecimalFormatter, DecimalError> {
FixedDecimalFormatter::try_new_with_buffer_provider(self.0, locale, options)
}
}

#[cfg(feature = "serde")]
impl<P> WeekCalculatorLoader for ExternalLoaderBuffer<'_, P>
where
P: ?Sized + BufferProvider,
{
#[inline]
fn load(&self, locale: &DataLocale) -> Result<WeekCalculator, CalendarError> {
WeekCalculator::try_new_with_buffer_provider(self.0, locale)
}
}

/// Loader for types from other crates using [`DataProvider`].
pub(crate) struct ExternalLoaderUnstable<'a, P: ?Sized>(pub &'a P);

impl<P> FixedDecimalFormatterLoader for ExternalLoaderUnstable<'_, P>
where
P: ?Sized + DataProvider<DecimalSymbolsV1Marker>,
{
#[inline]
fn load(
&self,
locale: &DataLocale,
options: FixedDecimalFormatterOptions,
) -> Result<FixedDecimalFormatter, DecimalError> {
FixedDecimalFormatter::try_new_unstable(self.0, locale, options)
}
}

impl<P> WeekCalculatorLoader for ExternalLoaderUnstable<'_, P>
where
P: ?Sized + DataProvider<WeekDataV2Marker>,
{
#[inline]
fn load(&self, locale: &DataLocale) -> Result<WeekCalculator, CalendarError> {
WeekCalculator::try_new_unstable(self.0, locale)
}
}
Loading

0 comments on commit ecc44ec

Please sign in to comment.