use crate::format::datetime::FormattedDateTime;
use crate::{
format::datetime,
input::{DateInput, DateTimeInput, ExtractedDateTimeInput, IsoTimeInput},
options::{length, preferences},
pattern::runtime::PatternPlurals,
provider::{
self,
calendar::{
patterns::GenericPatternV1Marker, patterns::PatternPluralsFromPatternsV1Marker,
ErasedDateLengthsV1Marker, ErasedDateSymbolsV1Marker, TimeLengthsV1Marker,
TimeSymbolsV1Marker,
},
},
DateTimeError,
};
use icu_calendar::provider::WeekDataV1Marker;
use icu_calendar::week::WeekCalculator;
use icu_decimal::{
options::{FixedDecimalFormatterOptions, GroupingStrategy},
provider::DecimalSymbolsV1Marker,
FixedDecimalFormatter,
};
use icu_plurals::{provider::OrdinalV1Marker, PluralRules};
use icu_provider::prelude::*;
#[derive(Debug)]
pub(crate) struct TimeFormatter {
pub patterns: DataPayload<PatternPluralsFromPatternsV1Marker>,
pub symbols: Option<DataPayload<TimeSymbolsV1Marker>>,
pub fixed_decimal_format: FixedDecimalFormatter,
}
impl TimeFormatter {
#[inline(never)]
#[cfg(feature = "compiled_data")]
pub fn try_new(
locale: &DataLocale,
length: length::Time,
preferences: Option<preferences::Bag>,
) -> Result<Self, DateTimeError> {
let patterns = provider::date_time::pattern_for_time_length(
&crate::provider::Baked,
locale,
length,
preferences,
)?;
let required = datetime::analyze_patterns(&patterns.get().0, false)
.map_err(|field| DateTimeError::UnsupportedField(field.symbol))?;
let symbols_data = if required.time_symbols_data {
Some(
crate::provider::Baked
.load(DataRequest {
locale,
metadata: Default::default(),
})?
.take_payload()?,
)
} else {
None
};
let mut fixed_decimal_format_options = FixedDecimalFormatterOptions::default();
fixed_decimal_format_options.grouping_strategy = GroupingStrategy::Never;
let fixed_decimal_format =
FixedDecimalFormatter::try_new(locale, fixed_decimal_format_options)?;
Ok(Self::new(patterns, symbols_data, fixed_decimal_format))
}
#[inline(never)]
pub fn try_new_unstable<D>(
provider: &D,
locale: &DataLocale,
length: length::Time,
preferences: Option<preferences::Bag>,
) -> Result<Self, DateTimeError>
where
D: DataProvider<TimeLengthsV1Marker>
+ DataProvider<TimeSymbolsV1Marker>
+ DataProvider<DecimalSymbolsV1Marker>
+ ?Sized,
{
let patterns =
provider::date_time::pattern_for_time_length(provider, locale, length, preferences)?;
let required = datetime::analyze_patterns(&patterns.get().0, false)
.map_err(|field| DateTimeError::UnsupportedField(field.symbol))?;
let symbols_data = if required.time_symbols_data {
Some(
provider
.load(DataRequest {
locale,
metadata: Default::default(),
})?
.take_payload()?,
)
} else {
None
};
let mut fixed_decimal_format_options = FixedDecimalFormatterOptions::default();
fixed_decimal_format_options.grouping_strategy = GroupingStrategy::Never;
let fixed_decimal_format = FixedDecimalFormatter::try_new_unstable(
provider,
locale,
fixed_decimal_format_options,
)?;
Ok(Self::new(patterns, symbols_data, fixed_decimal_format))
}
pub fn new(
patterns: DataPayload<PatternPluralsFromPatternsV1Marker>,
symbols: Option<DataPayload<TimeSymbolsV1Marker>>,
fixed_decimal_format: FixedDecimalFormatter,
) -> Self {
Self {
patterns,
symbols,
fixed_decimal_format,
}
}
#[inline]
pub fn format<'l, T>(&'l self, value: &T) -> FormattedDateTime<'l>
where
T: IsoTimeInput,
{
FormattedDateTime {
patterns: &self.patterns,
date_symbols: None,
time_symbols: self.symbols.as_ref().map(|s| s.get()),
datetime: ExtractedDateTimeInput::extract_from_time(value),
week_data: None,
ordinal_rules: None,
fixed_decimal_format: &self.fixed_decimal_format,
}
}
}
#[derive(Debug)]
pub(crate) struct DateFormatter {
pub generic_pattern: DataPayload<GenericPatternV1Marker>,
pub patterns: DataPayload<PatternPluralsFromPatternsV1Marker>,
pub symbols: Option<DataPayload<ErasedDateSymbolsV1Marker>>,
pub week_data: Option<WeekCalculator>,
pub ordinal_rules: Option<PluralRules>,
pub fixed_decimal_format: FixedDecimalFormatter,
}
impl DateFormatter {
#[cfg(feature = "compiled_data")]
#[inline(never)]
pub fn try_new(
patterns_data: DataPayload<ErasedDateLengthsV1Marker>,
symbols_data_fn: impl FnOnce() -> Result<DataPayload<ErasedDateSymbolsV1Marker>, DataError>,
locale: &DataLocale,
length: length::Date,
) -> Result<Self, DateTimeError> {
let patterns = provider::date_time::pattern_for_date_length(length, patterns_data.clone());
let generic_pattern =
provider::date_time::generic_pattern_for_date_length(length, patterns_data);
let required = datetime::analyze_patterns(&patterns.get().0, false)
.map_err(|field| DateTimeError::UnsupportedField(field.symbol))?;
let week_data = if required.week_data {
Some(WeekCalculator::try_new(locale)?)
} else {
None
};
let ordinal_rules = if let PatternPlurals::MultipleVariants(_) = &patterns.get().0 {
Some(PluralRules::try_new_ordinal(locale)?)
} else {
None
};
let symbols_data = if required.date_symbols_data {
Some(symbols_data_fn()?)
} else {
None
};
let mut fixed_decimal_format_options = FixedDecimalFormatterOptions::default();
fixed_decimal_format_options.grouping_strategy = GroupingStrategy::Never;
let fixed_decimal_format =
FixedDecimalFormatter::try_new(locale, fixed_decimal_format_options)?;
Ok(Self::new(
generic_pattern,
patterns,
symbols_data,
week_data,
ordinal_rules,
fixed_decimal_format,
))
}
#[inline(never)]
pub fn try_new_unstable<D>(
provider: &D,
patterns_data: DataPayload<ErasedDateLengthsV1Marker>,
symbols_data_fn: impl FnOnce() -> Result<DataPayload<ErasedDateSymbolsV1Marker>, DataError>,
locale: &DataLocale,
length: length::Date,
) -> Result<Self, DateTimeError>
where
D: DataProvider<DecimalSymbolsV1Marker>
+ DataProvider<OrdinalV1Marker>
+ DataProvider<WeekDataV1Marker>
+ ?Sized,
{
let patterns = provider::date_time::pattern_for_date_length(length, patterns_data.clone());
let generic_pattern =
provider::date_time::generic_pattern_for_date_length(length, patterns_data);
let required = datetime::analyze_patterns(&patterns.get().0, false)
.map_err(|field| DateTimeError::UnsupportedField(field.symbol))?;
let week_data = if required.week_data {
Some(
(*DataProvider::<WeekDataV1Marker>::load(
provider,
DataRequest {
locale,
metadata: Default::default(),
},
)?
.take_payload()?
.get())
.into(),
)
} else {
None
};
let ordinal_rules = if let PatternPlurals::MultipleVariants(_) = &patterns.get().0 {
Some(PluralRules::try_new_ordinal_unstable(provider, locale)?)
} else {
None
};
let symbols_data = if required.date_symbols_data {
Some(symbols_data_fn()?)
} else {
None
};
let mut fixed_decimal_format_options = FixedDecimalFormatterOptions::default();
fixed_decimal_format_options.grouping_strategy = GroupingStrategy::Never;
let fixed_decimal_format = FixedDecimalFormatter::try_new_unstable(
provider,
locale,
fixed_decimal_format_options,
)?;
Ok(Self::new(
generic_pattern,
patterns,
symbols_data,
week_data,
ordinal_rules,
fixed_decimal_format,
))
}
pub fn new(
generic_pattern: DataPayload<GenericPatternV1Marker>,
patterns: DataPayload<PatternPluralsFromPatternsV1Marker>,
symbols: Option<DataPayload<ErasedDateSymbolsV1Marker>>,
week_data: Option<WeekCalculator>,
ordinal_rules: Option<PluralRules>,
fixed_decimal_format: FixedDecimalFormatter,
) -> Self {
Self {
generic_pattern,
patterns,
symbols,
week_data,
ordinal_rules,
fixed_decimal_format,
}
}
#[inline]
pub fn format<'l, T>(&'l self, value: &T) -> FormattedDateTime<'l>
where
T: DateInput,
{
FormattedDateTime {
patterns: &self.patterns,
date_symbols: self.symbols.as_ref().map(|s| s.get()),
time_symbols: None,
datetime: ExtractedDateTimeInput::extract_from_date(value),
week_data: None,
ordinal_rules: None,
fixed_decimal_format: &self.fixed_decimal_format,
}
}
}
#[derive(Debug)]
pub(crate) struct DateTimeFormatter {
pub patterns: DataPayload<PatternPluralsFromPatternsV1Marker>,
pub date_symbols: Option<DataPayload<ErasedDateSymbolsV1Marker>>,
pub time_symbols: Option<DataPayload<TimeSymbolsV1Marker>>,
pub week_data: Option<WeekCalculator>,
pub ordinal_rules: Option<PluralRules>,
pub fixed_decimal_format: FixedDecimalFormatter,
}
impl DateTimeFormatter {
#[inline(never)]
pub fn try_from_date_and_time(
date: DateFormatter,
time: TimeFormatter,
) -> Result<Self, DateTimeError> {
let generic_pattern = &date.generic_pattern;
let time_patterns = &time.patterns;
let patterns = date
.patterns
.try_map_project::<PatternPluralsFromPatternsV1Marker, _, DateTimeError>(
|data, _| {
let date_pattern = data.0.expect_pattern("Lengths are single patterns");
let time_pattern: crate::pattern::runtime::Pattern = time_patterns
.get()
.clone()
.0
.expect_pattern("Lengths are single patterns");
Ok(PatternPlurals::from(
generic_pattern
.get()
.clone()
.0
.combined(date_pattern, time_pattern)?,
)
.into())
},
)?;
Ok(Self {
patterns,
date_symbols: date.symbols,
time_symbols: time.symbols,
week_data: date.week_data,
ordinal_rules: date.ordinal_rules,
fixed_decimal_format: date.fixed_decimal_format,
})
}
#[inline(never)]
#[cfg(feature = "compiled_data")]
pub fn try_new(
patterns: DataPayload<PatternPluralsFromPatternsV1Marker>,
symbols_data_fn: impl FnOnce() -> Result<DataPayload<ErasedDateSymbolsV1Marker>, DataError>,
locale: &DataLocale,
) -> Result<Self, DateTimeError> {
let required = datetime::analyze_patterns(&patterns.get().0, false)
.map_err(|field| DateTimeError::UnsupportedField(field.symbol))?;
let req = DataRequest {
locale,
metadata: Default::default(),
};
let week_data = if required.week_data {
Some(WeekCalculator::try_new(locale)?)
} else {
None
};
let ordinal_rules = if let PatternPlurals::MultipleVariants(_) = &patterns.get().0 {
Some(PluralRules::try_new_ordinal(locale)?)
} else {
None
};
let date_symbols_data = if required.date_symbols_data {
Some(symbols_data_fn()?)
} else {
None
};
let time_symbols_data = if required.time_symbols_data {
Some(crate::provider::Baked.load(req)?.take_payload()?)
} else {
None
};
let mut fixed_decimal_format_options = FixedDecimalFormatterOptions::default();
fixed_decimal_format_options.grouping_strategy = GroupingStrategy::Never;
let fixed_decimal_format =
FixedDecimalFormatter::try_new(locale, fixed_decimal_format_options)?;
Ok(Self::new(
patterns,
date_symbols_data,
time_symbols_data,
week_data,
ordinal_rules,
fixed_decimal_format,
))
}
#[inline(never)]
pub fn try_new_unstable<D>(
provider: &D,
patterns: DataPayload<PatternPluralsFromPatternsV1Marker>,
symbols_data_fn: impl FnOnce() -> Result<DataPayload<ErasedDateSymbolsV1Marker>, DataError>,
locale: &DataLocale,
) -> Result<Self, DateTimeError>
where
D: DataProvider<TimeSymbolsV1Marker>
+ DataProvider<TimeLengthsV1Marker>
+ DataProvider<DecimalSymbolsV1Marker>
+ DataProvider<OrdinalV1Marker>
+ DataProvider<WeekDataV1Marker>
+ ?Sized,
{
let required = datetime::analyze_patterns(&patterns.get().0, false)
.map_err(|field| DateTimeError::UnsupportedField(field.symbol))?;
let req = DataRequest {
locale,
metadata: Default::default(),
};
let week_data = if required.week_data {
Some(
(*DataProvider::<WeekDataV1Marker>::load(
provider,
DataRequest {
locale,
metadata: Default::default(),
},
)?
.take_payload()?
.get())
.into(),
)
} else {
None
};
let ordinal_rules = if let PatternPlurals::MultipleVariants(_) = &patterns.get().0 {
Some(PluralRules::try_new_ordinal_unstable(provider, locale)?)
} else {
None
};
let date_symbols_data = if required.date_symbols_data {
Some(symbols_data_fn()?)
} else {
None
};
let time_symbols_data = if required.time_symbols_data {
Some(provider.load(req)?.take_payload()?)
} else {
None
};
let mut fixed_decimal_format_options = FixedDecimalFormatterOptions::default();
fixed_decimal_format_options.grouping_strategy = GroupingStrategy::Never;
let fixed_decimal_format = FixedDecimalFormatter::try_new_unstable(
provider,
locale,
fixed_decimal_format_options,
)?;
Ok(Self::new(
patterns,
date_symbols_data,
time_symbols_data,
week_data,
ordinal_rules,
fixed_decimal_format,
))
}
pub fn new(
patterns: DataPayload<PatternPluralsFromPatternsV1Marker>,
date_symbols: Option<DataPayload<ErasedDateSymbolsV1Marker>>,
time_symbols: Option<DataPayload<TimeSymbolsV1Marker>>,
week_data: Option<WeekCalculator>,
ordinal_rules: Option<PluralRules>,
fixed_decimal_format: FixedDecimalFormatter,
) -> Self {
Self {
patterns,
date_symbols,
time_symbols,
week_data,
ordinal_rules,
fixed_decimal_format,
}
}
#[inline]
pub fn format<'l, T>(&'l self, value: &T) -> FormattedDateTime<'l>
where
T: DateTimeInput,
{
FormattedDateTime {
patterns: &self.patterns,
date_symbols: self.date_symbols.as_ref().map(|s| s.get()),
time_symbols: self.time_symbols.as_ref().map(|s| s.get()),
datetime: ExtractedDateTimeInput::extract_from(value),
week_data: self.week_data.as_ref(),
ordinal_rules: self.ordinal_rules.as_ref(),
fixed_decimal_format: &self.fixed_decimal_format,
}
}
#[cfg(feature = "experimental")]
pub fn resolve_components(&self) -> crate::options::components::Bag {
crate::options::components::Bag::from(&self.patterns.get().0)
}
}