use crate::DateTimeFormatterPreferences;
use crate::fieldsets::builder;
use crate::fieldsets::enums::{CompositeFieldSet, TimeFieldSet, ZoneFieldSet};
use crate::format::DateTimeInputUnchecked;
use crate::options::*;
use crate::pattern::DateTimePattern;
use crate::provider::fields::{self, Field, FieldLength, FieldSymbol};
use crate::provider::pattern::CoarseHourCycle;
use crate::provider::pattern::{
GenericPatternItem, PatternItem,
runtime::{self, PatternMetadata},
};
#[cfg(feature = "unstable")]
#[allow(unused_imports)]
use crate::provider::range_patterns::{
DateGreatestDifferenceField, DatetimePatternsRangeGlueV1, ErasedPackedRangePatterns,
RangePatternInfoBorrowed, TimeGreatestDifferenceField,
};
use crate::provider::{
packed_pattern::{ErasedPackedPatterns, PackedSkeletonVariant},
semantic_skeletons::{DatetimePatternsGlueV1, GluePattern, marker_attrs},
};
#[cfg(feature = "unstable")]
use crate::range::difference::Difference;
use icu_calendar::types::YearAmbiguity;
use icu_provider::DataPayloadOr;
use icu_provider::prelude::*;
use marker_attrs::GlueType;
use zerovec::ZeroSlice;
use zerovec::ule::AsULE;
#[derive(Debug, Copy, Clone)]
pub(crate) struct RawOptions {
pub(crate) length: Option<Length>,
pub(crate) date_fields: Option<builder::DateFields>,
pub(crate) alignment: Option<Alignment>,
pub(crate) year_style: Option<YearStyle>,
pub(crate) time_precision: Option<TimePrecision>,
}
impl RawOptions {
#[inline]
pub(crate) fn length(self) -> Length {
self.length.unwrap_or_else(|| {
debug_assert!(false, "length not set in a formatter that needs it");
Default::default()
})
}
}
#[derive(Debug, Copy, Clone, Default)]
pub(crate) struct RawPreferences {
pub(crate) hour_cycle: Option<fields::Hour>,
}
impl RawPreferences {
#[inline]
pub(crate) fn from_prefs(prefs: DateTimeFormatterPreferences) -> Self {
Self {
hour_cycle: fields::Hour::from_prefs(prefs),
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct DatePatternSelectionData {
payload: DataPayloadOr<ErasedPackedPatterns, ()>,
}
#[derive(Debug, Copy, Clone)]
pub(crate) enum DatePatternDataBorrowed<'a> {
Resolved(runtime::PatternBorrowed<'a>, Option<Alignment>),
}
#[derive(Debug, Clone)]
pub(crate) struct TimePatternSelectionData {
payload: DataPayloadOr<ErasedPackedPatterns, ()>,
}
#[derive(Debug, Copy, Clone)]
pub(crate) enum TimePatternDataBorrowed<'a> {
Resolved(
runtime::PatternBorrowed<'a>,
Option<Alignment>,
Option<fields::Hour>,
Option<SubsecondDigits>,
),
}
#[derive(Debug, Clone)]
pub(crate) enum ZonePatternSelectionData {
SinglePatternItem(ZoneFieldSet, <PatternItem as AsULE>::ULE),
}
#[derive(Debug, Copy, Clone)]
pub(crate) enum ZonePatternDataBorrowed<'a> {
SinglePatternItem(&'a <PatternItem as AsULE>::ULE),
}
#[derive(Debug, Copy, Clone, Default)]
pub(crate) struct ItemsAndOptions<'a> {
pub(crate) items: &'a ZeroSlice<PatternItem>,
pub(crate) alignment: Option<Alignment>,
pub(crate) hour_cycle: Option<fields::Hour>,
pub(crate) subsecond_digits: Option<SubsecondDigits>,
}
impl ItemsAndOptions<'_> {
const fn new_empty() -> Self {
Self {
items: ZeroSlice::new_empty(),
alignment: None,
hour_cycle: None,
subsecond_digits: None,
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct DateTimeZonePatternSelectionData {
pub(crate) options: RawOptions,
pub(crate) prefs: RawPreferences,
pub(crate) date: DatePatternSelectionData,
pub(crate) time: TimePatternSelectionData,
pub(crate) zone: Option<ZonePatternSelectionData>,
pub(crate) glue: Option<DataPayload<DatetimePatternsGlueV1>>,
}
#[derive(Debug, Copy, Clone)]
pub(crate) struct DateTimeZonePatternDataBorrowed<'a> {
pub(crate) date: Option<DatePatternDataBorrowed<'a>>,
pub(crate) time: Option<TimePatternDataBorrowed<'a>>,
pub(crate) zone: Option<ZonePatternDataBorrowed<'a>>,
pub(crate) glue: Option<&'a GluePattern<'a>>,
}
impl DatePatternSelectionData {
pub(crate) fn none() -> Self {
Self {
payload: DataPayloadOr::none(),
}
}
pub(crate) fn try_new_with_skeleton(
provider: &(impl BoundDataProvider<ErasedPackedPatterns> + ?Sized),
prefs: DateTimeFormatterPreferences,
attributes: &DataMarkerAttributes,
) -> Result<Self, DataError> {
let locale = provider
.bound_marker()
.make_locale(prefs.locale_preferences);
let payload = provider
.load_bound(DataRequest {
id: DataIdentifierBorrowed::for_marker_attributes_and_locale(attributes, &locale),
..Default::default()
})?
.payload;
Ok(Self {
payload: DataPayloadOr::from_payload(payload),
})
}
#[inline]
pub(crate) fn pattern_items_for_data_loading(
&self,
options: RawOptions,
) -> Option<impl Iterator<Item = PatternItem> + '_> {
let payload = self.payload.get_option()?;
Some(
payload
.get(options.length(), PackedSkeletonVariant::Variant1)
.items
.iter(),
)
}
pub(crate) fn select(
&self,
input: &DateTimeInputUnchecked,
options: RawOptions,
) -> Option<DatePatternDataBorrowed<'_>> {
let payload = self.payload.get_option()?;
let variant = input.resolve_date_variant(options);
Some(DatePatternDataBorrowed::Resolved(
payload.get(options.length(), variant),
options.alignment,
))
}
}
#[derive(Debug, Clone)]
#[cfg(feature = "unstable")]
#[allow(dead_code)]
pub(crate) struct DateRangePatternSelectionData {
pub(crate) payload: DataPayloadOr<ErasedPackedRangePatterns, ()>,
}
#[cfg(feature = "unstable")]
#[allow(dead_code)]
impl DateRangePatternSelectionData {
pub(crate) fn none() -> Self {
Self {
payload: DataPayloadOr::none(),
}
}
pub(crate) fn try_new_with_skeleton(
provider: &(impl BoundDataProvider<ErasedPackedRangePatterns> + ?Sized),
prefs: DateTimeFormatterPreferences,
attributes: &DataMarkerAttributes,
) -> Result<Self, DataError> {
let locale = provider
.bound_marker()
.make_locale(prefs.locale_preferences);
let payload = provider
.load_bound(DataRequest {
id: DataIdentifierBorrowed::for_marker_attributes_and_locale(attributes, &locale),
..Default::default()
})?
.payload;
Ok(Self {
payload: DataPayloadOr::from_payload(payload),
})
}
pub(crate) fn select<'a>(
&'a self,
input: &DateTimeInputUnchecked,
options: RawOptions,
diff: Difference,
) -> Option<RangePatternInfoBorrowed<'a>> {
let payload = self.payload.get_option()?;
let variant = input.resolve_date_variant(options);
let ule = payload.get_element(options.length(), variant)?;
let field = match diff {
Difference::Era => DateGreatestDifferenceField::Era,
Difference::Year => DateGreatestDifferenceField::Year,
Difference::Month => DateGreatestDifferenceField::Month,
Difference::Day => DateGreatestDifferenceField::Day,
_ => return None,
};
ule.get_date_pattern(field)
}
}
impl DateTimeInputUnchecked {
fn resolve_date_variant(&self, options: RawOptions) -> PackedSkeletonVariant {
let year_style = options.year_style.unwrap_or_default();
let ambiguity = self
.year
.as_ref()
.map(|y| {
y.era()
.map(|e| e.ambiguity)
.unwrap_or(YearAmbiguity::EraRequired)
})
.unwrap_or(YearAmbiguity::EraAndCenturyRequired);
match (year_style, ambiguity) {
(YearStyle::WithEra, _) => PackedSkeletonVariant::Variant1,
(
YearStyle::Full,
YearAmbiguity::EraAndCenturyRequired | YearAmbiguity::EraRequired,
) => PackedSkeletonVariant::Variant1,
(YearStyle::Full, YearAmbiguity::CenturyRequired | YearAmbiguity::Unambiguous) => {
PackedSkeletonVariant::Variant0
}
(
YearStyle::Auto | YearStyle::NoEra,
YearAmbiguity::Unambiguous | YearAmbiguity::EraRequired,
) => PackedSkeletonVariant::Standard,
(YearStyle::Auto, YearAmbiguity::CenturyRequired) => PackedSkeletonVariant::Variant0,
(YearStyle::Auto, YearAmbiguity::EraAndCenturyRequired) => {
PackedSkeletonVariant::Variant1
}
(
YearStyle::NoEra,
YearAmbiguity::CenturyRequired | YearAmbiguity::EraAndCenturyRequired,
) => PackedSkeletonVariant::Variant0,
}
}
fn resolve_time_precision(
&self,
time_precision: TimePrecision,
coarse_hour_cycle: Option<CoarseHourCycle>,
range_second_input: Option<&Self>,
) -> (PackedSkeletonVariant, Option<SubsecondDigits>) {
match time_precision {
TimePrecision::Hour => (PackedSkeletonVariant::Standard, None),
TimePrecision::Minute => (PackedSkeletonVariant::Variant0, None),
TimePrecision::Second => (PackedSkeletonVariant::Variant1, None),
TimePrecision::Subsecond(f) => (PackedSkeletonVariant::Variant1, Some(f)),
TimePrecision::MinuteOptional => {
let minute = self.minute.unwrap_or_default();
let second_range_minute_is_zero = range_second_input
.map(|input2| input2.minute.unwrap_or_default().is_zero())
.unwrap_or(true);
if minute.is_zero()
&& second_range_minute_is_zero
&& matches!(coarse_hour_cycle, Some(CoarseHourCycle::H11H12))
{
(PackedSkeletonVariant::Standard, None)
} else {
(PackedSkeletonVariant::Variant0, None)
}
}
}
}
}
impl<'a> DatePatternDataBorrowed<'a> {
pub(crate) fn items_and_options(self) -> ItemsAndOptions<'a> {
let Self::Resolved(pattern, alignment) = self;
ItemsAndOptions {
items: pattern.items,
alignment,
..Default::default()
}
}
}
impl TimePatternSelectionData {
pub(crate) fn none() -> Self {
Self {
payload: DataPayloadOr::none(),
}
}
pub(crate) fn try_new_with_skeleton(
provider: &(impl BoundDataProvider<ErasedPackedPatterns> + ?Sized),
prefs: DateTimeFormatterPreferences,
components: TimeFieldSet,
) -> Result<Self, DataError> {
let locale = provider
.bound_marker()
.make_locale(prefs.locale_preferences);
let prefs = RawPreferences::from_prefs(prefs);
let mut maybe_payload = None;
if let Some(hour_cycle) = prefs.hour_cycle {
maybe_payload = provider
.load_bound(DataRequest {
id: DataIdentifierBorrowed::for_marker_attributes_and_locale(
components.id_str_for_hour_cycle(Some(hour_cycle)),
&locale,
),
..Default::default()
})
.allow_identifier_not_found()?
.map(|r| r.payload);
}
let payload = match maybe_payload {
Some(payload) => payload,
None => {
provider
.load_bound(DataRequest {
id: DataIdentifierBorrowed::for_marker_attributes_and_locale(
components.id_str_for_hour_cycle(None),
&locale,
),
..Default::default()
})?
.payload
}
};
Ok(Self {
payload: DataPayloadOr::from_payload(payload),
})
}
pub(crate) fn try_new_overlap_with_skeleton(
provider: &(impl BoundDataProvider<ErasedPackedPatterns> + ?Sized),
prefs: DateTimeFormatterPreferences,
attributes: &DataMarkerAttributes,
options: RawOptions,
) -> Result<Self, DataError> {
debug_assert!(options.year_style.is_none());
let locale = provider
.bound_marker()
.make_locale(prefs.locale_preferences);
let payload = provider
.load_bound(DataRequest {
id: DataIdentifierBorrowed::for_marker_attributes_and_locale(attributes, &locale),
..Default::default()
})?
.payload;
Ok(Self {
payload: DataPayloadOr::from_payload(payload),
})
}
#[inline]
pub(crate) fn pattern_items_for_data_loading(
&self,
options: RawOptions,
) -> Option<impl Iterator<Item = PatternItem> + '_> {
let payload = self.payload.get_option()?;
Some(
payload
.get(options.length(), PackedSkeletonVariant::Variant1)
.items
.iter(),
)
}
pub(crate) fn select(
&self,
input: &DateTimeInputUnchecked,
options: RawOptions,
prefs: RawPreferences,
) -> Option<TimePatternDataBorrowed<'_>> {
let payload = self.payload.get_option()?;
let time_precision = options.time_precision.unwrap_or_default();
let coarse_hour_cycle = payload
.get(options.length(), PackedSkeletonVariant::Standard)
.metadata
.time_granularity()
.coarse_hour_cycle();
let (variant, subsecond_digits) =
input.resolve_time_precision(time_precision, coarse_hour_cycle, None);
Some(TimePatternDataBorrowed::Resolved(
payload.get(options.length(), variant),
options.alignment,
prefs.hour_cycle,
subsecond_digits,
))
}
}
impl<'a> TimePatternDataBorrowed<'a> {
pub(crate) fn items_and_options(self) -> ItemsAndOptions<'a> {
let Self::Resolved(pattern, alignment, hour_cycle, subsecond_digits) = self;
ItemsAndOptions {
items: pattern.items,
alignment,
hour_cycle,
subsecond_digits,
}
}
}
#[derive(Debug, Clone)]
#[cfg(feature = "unstable")]
#[allow(dead_code)]
pub(crate) struct TimeRangePatternSelectionData {
pub(crate) payload: DataPayloadOr<ErasedPackedRangePatterns, ()>,
}
#[cfg(feature = "unstable")]
#[allow(dead_code)]
impl TimeRangePatternSelectionData {
pub(crate) fn none() -> Self {
Self {
payload: DataPayloadOr::none(),
}
}
pub(crate) fn try_new_with_skeleton(
provider: &(impl BoundDataProvider<ErasedPackedRangePatterns> + ?Sized),
prefs: DateTimeFormatterPreferences,
components: TimeFieldSet,
) -> Result<Self, DataError> {
let locale = provider
.bound_marker()
.make_locale(prefs.locale_preferences);
let prefs = RawPreferences::from_prefs(prefs);
let mut maybe_payload = None;
if let Some(hour_cycle) = prefs.hour_cycle {
maybe_payload = provider
.load_bound(DataRequest {
id: DataIdentifierBorrowed::for_marker_attributes_and_locale(
components.id_str_for_hour_cycle(Some(hour_cycle)),
&locale,
),
..Default::default()
})
.allow_identifier_not_found()?
.map(|r| r.payload);
}
let payload = match maybe_payload {
Some(payload) => payload,
None => {
provider
.load_bound(DataRequest {
id: DataIdentifierBorrowed::for_marker_attributes_and_locale(
components.id_str_for_hour_cycle(None),
&locale,
),
..Default::default()
})?
.payload
}
};
Ok(Self {
payload: DataPayloadOr::from_payload(payload),
})
}
pub(crate) fn select<'a>(
&'a self,
input: &DateTimeInputUnchecked,
input2: &DateTimeInputUnchecked,
options: RawOptions,
diff: Difference,
) -> Option<RangePatternInfoBorrowed<'a>> {
let payload = self.payload.get_option()?;
let field = match diff {
Difference::DayPeriodB => TimeGreatestDifferenceField::DayPeriodB,
Difference::DayPeriodA => TimeGreatestDifferenceField::DayPeriodA,
Difference::Hour => TimeGreatestDifferenceField::Hour,
Difference::Minute => TimeGreatestDifferenceField::Minute,
_ => return None,
};
let coarse_hour_cycle = payload.get_coarse_hour_cycle(options.length());
let time_precision = options.time_precision.unwrap_or_default();
let (variant, _) =
input.resolve_time_precision(time_precision, coarse_hour_cycle, Some(input2));
let ule = payload.get_element(options.length(), variant)?;
ule.get_time_pattern(field)
}
}
impl ZonePatternSelectionData {
pub(crate) fn new_with_skeleton(field_set: ZoneFieldSet) -> Self {
let (symbol, length) = field_set.to_field();
let pattern_item = PatternItem::Field(Field {
symbol: FieldSymbol::TimeZone(symbol),
length,
});
Self::SinglePatternItem(field_set, pattern_item.to_unaligned())
}
#[inline]
pub(crate) fn pattern_items_for_data_loading(&self) -> impl Iterator<Item = PatternItem> + '_ {
let Self::SinglePatternItem(_, pattern_item) = self;
let pattern_item = PatternItem::from_unaligned(*pattern_item);
[pattern_item].into_iter()
}
pub(crate) fn select(&self, _input: &DateTimeInputUnchecked) -> ZonePatternDataBorrowed<'_> {
let Self::SinglePatternItem(_, pattern_item) = self;
ZonePatternDataBorrowed::SinglePatternItem(pattern_item)
}
}
impl<'a> ZonePatternDataBorrowed<'a> {
pub(crate) fn items_and_options(self) -> ItemsAndOptions<'a> {
let Self::SinglePatternItem(item) = self;
ItemsAndOptions {
items: ZeroSlice::from_ule_slice(core::slice::from_ref(item)),
..Default::default()
}
}
}
#[inline]
fn map_composite_skeleton<DateRes, TimeRes, E>(
skeleton: CompositeFieldSet,
mut load_date: impl FnMut(&DataMarkerAttributes) -> Result<DateRes, E>,
mut load_time: impl FnMut(TimeFieldSet) -> Result<TimeRes, E>,
) -> Result<(Option<DateRes>, Option<TimeRes>), E> {
let mut date = None;
let mut time = None;
match skeleton {
CompositeFieldSet::Date(field_set) => {
date = Some(load_date(field_set.id_str())?);
}
CompositeFieldSet::CalendarPeriod(field_set) => {
date = Some(load_date(field_set.id_str())?);
}
CompositeFieldSet::Time(field_set) => {
time = Some(load_time(field_set)?);
}
CompositeFieldSet::DateTime(field_set) => {
date = Some(load_date(field_set.to_date_field_set().id_str())?);
time = Some(load_time(field_set.to_time_field_set())?);
}
CompositeFieldSet::DateZone(combo) => {
date = Some(load_date(combo.dt().id_str())?);
}
CompositeFieldSet::DateTimeZone(combo) => {
date = Some(load_date(combo.dt().to_date_field_set().id_str())?);
time = Some(load_time(combo.dt().to_time_field_set())?);
}
CompositeFieldSet::TimeZone(combo) => {
time = Some(load_time(combo.dt())?);
}
CompositeFieldSet::Zone(_) => {}
}
Ok((date, time))
}
impl DateTimeZonePatternSelectionData {
pub(crate) fn try_new_with_skeleton(
date_provider: &(impl BoundDataProvider<ErasedPackedPatterns> + ?Sized),
time_provider: &(impl BoundDataProvider<ErasedPackedPatterns> + ?Sized),
glue_provider: &(impl BoundDataProvider<DatetimePatternsGlueV1> + ?Sized),
prefs: DateTimeFormatterPreferences,
skeleton: CompositeFieldSet,
) -> Result<Self, DataError> {
if let CompositeFieldSet::DateTime(field_set) = skeleton {
let options = field_set.to_raw_options();
if let (Some(attributes), None) = (field_set.id_str(), prefs.hour_cycle)
&& let Some(overlap) = TimePatternSelectionData::try_new_overlap_with_skeleton(
date_provider,
prefs,
attributes,
options,
)
.allow_identifier_not_found()?
{
let prefs = RawPreferences::from_prefs(prefs);
return Ok(Self {
options,
prefs,
date: DatePatternSelectionData::none(),
time: overlap,
zone: None,
glue: None,
});
}
}
let options = skeleton.to_raw_options();
let zone = skeleton
.to_zone()
.map(ZonePatternSelectionData::new_with_skeleton);
let glue_type = match skeleton {
CompositeFieldSet::DateTime(_) => Some(GlueType::DateTime),
CompositeFieldSet::DateZone(_) => Some(GlueType::DateZone),
CompositeFieldSet::TimeZone(_) => Some(GlueType::TimeZone),
CompositeFieldSet::DateTimeZone(_) => Some(GlueType::DateTimeZone),
_ => None,
};
let glue = if let Some(gt) = glue_type {
Some(Self::load_glue(glue_provider, prefs, options, gt)?)
} else {
None
};
let (date_opt, time_opt) = map_composite_skeleton(
skeleton,
|d_attrs| {
DatePatternSelectionData::try_new_with_skeleton(date_provider, prefs, d_attrs)
},
|t_field_set| {
TimePatternSelectionData::try_new_with_skeleton(time_provider, prefs, t_field_set)
},
)?;
let has_time = time_opt.is_some();
let date = date_opt.unwrap_or_else(DatePatternSelectionData::none);
let time = time_opt.unwrap_or_else(TimePatternSelectionData::none);
let prefs = if has_time {
RawPreferences::from_prefs(prefs)
} else {
Default::default()
};
Ok(Self {
options,
prefs,
date,
time,
zone,
glue,
})
}
fn load_glue(
provider: &(impl BoundDataProvider<DatetimePatternsGlueV1> + ?Sized),
prefs: DateTimeFormatterPreferences,
options: RawOptions,
glue_type: GlueType,
) -> Result<DataPayload<DatetimePatternsGlueV1>, DataError> {
let locale = provider
.bound_marker()
.make_locale(prefs.locale_preferences);
provider
.load_bound(DataRequest {
id: DataIdentifierBorrowed::for_marker_attributes_and_locale(
marker_attrs::pattern_marker_attr_for_glue(
match options.length() {
Length::Long => marker_attrs::PatternLength::Long,
Length::Medium => marker_attrs::PatternLength::Medium,
Length::Short => marker_attrs::PatternLength::Short,
},
glue_type,
),
&locale,
),
..Default::default()
})
.map(|r| r.payload)
}
#[inline]
pub(crate) fn pattern_items_for_data_loading(&self) -> impl Iterator<Item = PatternItem> + '_ {
let Self {
date, time, zone, ..
} = self;
let date_items = date
.pattern_items_for_data_loading(self.options)
.into_iter()
.flatten();
let time_items = time
.pattern_items_for_data_loading(self.options)
.into_iter()
.flatten();
let zone_items = zone.iter().flat_map(|x| x.pattern_items_for_data_loading());
date_items.chain(time_items).chain(zone_items)
}
pub(crate) fn select(
&self,
input: &DateTimeInputUnchecked,
) -> DateTimeZonePatternDataBorrowed<'_> {
DateTimeZonePatternDataBorrowed {
date: self.date.select(input, self.options),
time: self.time.select(input, self.options, self.prefs),
zone: self.zone.as_ref().map(|zone| zone.select(input)),
glue: self.glue.as_ref().map(|glue| glue.get()),
}
}
pub(crate) fn to_builder(&self) -> builder::FieldSetBuilder {
let time_precision = if self.time.payload.is_payload() {
Some(self.options.time_precision.unwrap_or_default())
} else {
None
};
let zone_style = self.zone.as_ref().map(|zone| {
let ZonePatternSelectionData::SinglePatternItem(field_set, _) = zone;
field_set.to_zone_style()
});
builder::FieldSetBuilder {
length: self.options.length,
date_fields: self.options.date_fields,
time_precision,
zone_style,
alignment: self.options.alignment,
year_style: self.options.year_style,
}
}
}
#[derive(Debug, Clone)]
#[cfg(feature = "unstable")]
#[allow(dead_code)]
pub(crate) struct DateTimeZoneRangePatternSelectionData {
pub(crate) date_range: DateRangePatternSelectionData,
pub(crate) time_range: TimeRangePatternSelectionData,
pub(crate) range_glue: DataPayload<DatetimePatternsRangeGlueV1>,
}
#[cfg(feature = "unstable")]
#[allow(dead_code)]
impl DateTimeZoneRangePatternSelectionData {
pub(crate) fn try_new_with_skeleton(
date_range_provider: &(impl BoundDataProvider<ErasedPackedRangePatterns> + ?Sized),
time_range_provider: &(impl BoundDataProvider<ErasedPackedRangePatterns> + ?Sized),
range_glue_provider: &(impl BoundDataProvider<DatetimePatternsRangeGlueV1> + ?Sized),
prefs: DateTimeFormatterPreferences,
skeleton: CompositeFieldSet,
) -> Result<Self, DataError> {
let locale = range_glue_provider
.bound_marker()
.make_locale(prefs.locale_preferences);
let range_glue = range_glue_provider
.load_bound(DataRequest {
id: DataIdentifierBorrowed::for_marker_attributes_and_locale(
DataMarkerAttributes::empty(),
&locale,
),
..Default::default()
})?
.payload;
let (date_opt, time_opt) = map_composite_skeleton(
skeleton,
|d_attrs| {
DateRangePatternSelectionData::try_new_with_skeleton(
date_range_provider,
prefs,
d_attrs,
)
},
|t_field_set| {
TimeRangePatternSelectionData::try_new_with_skeleton(
time_range_provider,
prefs,
t_field_set,
)
},
)?;
let date_range = date_opt.unwrap_or_else(DateRangePatternSelectionData::none);
let time_range = time_opt.unwrap_or_else(TimeRangePatternSelectionData::none);
Ok(Self {
date_range,
time_range,
range_glue,
})
}
}
impl<'a> DateTimeZonePatternDataBorrowed<'a> {
#[inline]
fn date_pattern(self) -> Option<DatePatternDataBorrowed<'a>> {
self.date
}
#[inline]
fn time_pattern(self) -> Option<TimePatternDataBorrowed<'a>> {
self.time
}
#[inline]
fn zone_pattern(self) -> Option<ZonePatternDataBorrowed<'a>> {
self.zone
}
#[inline]
fn glue_pattern(self) -> Option<&'a ZeroSlice<GenericPatternItem>> {
self.glue.map(|glue| glue.pattern.items.as_slice())
}
#[inline]
pub(crate) fn metadata(self) -> PatternMetadata {
match (self.date, self.time) {
(Some(DatePatternDataBorrowed::Resolved(pb, _)), None) => pb.metadata,
(None, Some(TimePatternDataBorrowed::Resolved(pb, _, _, _))) => pb.metadata,
(
Some(DatePatternDataBorrowed::Resolved(date, _)),
Some(TimePatternDataBorrowed::Resolved(time, _, _, _)),
) => PatternMetadata::merge_date_and_time_metadata(date.metadata, time.metadata),
(None, None) => Default::default(),
}
}
pub(crate) fn iter_items(self) -> impl Iterator<Item = PatternItem> + 'a {
let glue_pattern_slice = match self.glue_pattern() {
Some(glue) => glue.as_ule_slice(),
None => runtime::ZERO_ONE_TWO_SLICE.as_ule_slice(),
};
glue_pattern_slice
.iter()
.map(
move |generic_item_ule| match generic_item_ule.as_pattern_item_ule() {
Ok(pattern_item_ule) => ItemsAndOptions {
items: ZeroSlice::from_ule_slice(core::slice::from_ref(pattern_item_ule)),
..Default::default()
},
Err(1) => self
.date_pattern()
.map(|p| p.items_and_options())
.unwrap_or(const { ItemsAndOptions::new_empty() }),
Err(0) => self
.time_pattern()
.map(|p| p.items_and_options())
.unwrap_or(const { ItemsAndOptions::new_empty() }),
Err(2) => self
.zone_pattern()
.map(|p| p.items_and_options())
.unwrap_or(const { ItemsAndOptions::new_empty() }),
_ => ItemsAndOptions::new_empty(),
},
)
.flat_map(|items_and_options| items_and_options.iter_items())
}
pub(crate) fn to_pattern(self) -> DateTimePattern {
let pattern = self.iter_items().collect::<runtime::Pattern>();
DateTimePattern::from(pattern)
}
}
impl<'a> ItemsAndOptions<'a> {
pub(crate) fn iter_items(self) -> impl Iterator<Item = PatternItem> + 'a {
self.items.iter().map(move |mut pattern_item| {
#[expect(clippy::single_match)] match &mut pattern_item {
PatternItem::Field(field) => {
let alignment = self.alignment.unwrap_or_default();
if matches!(alignment, Alignment::Column)
&& field.length == FieldLength::One
&& matches!(
field.symbol,
FieldSymbol::Month(_)
| FieldSymbol::Day(_)
| FieldSymbol::Week(_)
| FieldSymbol::Hour(_)
)
{
field.length = FieldLength::Two;
}
if let Some(hour_cycle) = self.hour_cycle
&& let FieldSymbol::Hour(_) = field.symbol
{
field.symbol = FieldSymbol::Hour(hour_cycle);
}
if let Some(subsecond_digits) = self.subsecond_digits
&& matches!(
field.symbol,
FieldSymbol::Second(fields::Second::Second)
| FieldSymbol::DecimalSecond(_)
)
{
field.symbol = FieldSymbol::from_subsecond_digits(subsecond_digits);
}
}
_ => (),
}
pattern_item
})
}
}