use crate::provider::date_time::PatternForLengthError;
#[cfg(feature = "experimental")]
use crate::provider::date_time::UnsupportedOptionsOrDataOrPatternError;
use crate::{calendar, options::DateTimeFormatterOptions, raw};
use alloc::string::String;
use icu_provider::prelude::*;
use crate::helpers::size_test;
use crate::input::{DateTimeInput, ExtractedDateTimeInput, TimeZoneInput};
use crate::provider::{self, calendar::*, date_time::PatternSelector};
use crate::time_zone::TimeZoneFormatterOptions;
use crate::{DateTimeError, FormattedZonedDateTime};
use icu_calendar::any_calendar::{AnyCalendar, AnyCalendarKind};
use icu_calendar::provider::{
ChineseCacheV1Marker, DangiCacheV1Marker, IslamicObservationalCacheV1Marker,
IslamicUmmAlQuraCacheV1Marker, JapaneseErasV1Marker, JapaneseExtendedErasV1Marker,
WeekDataV1Marker,
};
use icu_calendar::{DateTime, Time};
use icu_decimal::provider::DecimalSymbolsV1Marker;
use icu_plurals::provider::OrdinalV1Marker;
use writeable::Writeable;
size_test!(ZonedDateTimeFormatter, zoned_date_time_formatter_size, 6248);
#[doc = zoned_date_time_formatter_size!()]
#[derive(Debug)]
pub struct ZonedDateTimeFormatter(raw::ZonedDateTimeFormatter, AnyCalendar);
impl ZonedDateTimeFormatter {
#[cfg(feature = "experimental")]
#[cfg(feature = "compiled_data")]
pub fn try_new_experimental(
locale: &DataLocale,
date_time_format_options: DateTimeFormatterOptions,
time_zone_format_options: TimeZoneFormatterOptions,
) -> Result<Self, DateTimeError> {
let calendar = AnyCalendar::new_for_locale(locale);
let kind = calendar.kind();
let patterns = PatternSelector::for_options_experimental(
&crate::provider::Baked,
calendar::load_lengths_for_any_calendar_kind(&crate::provider::Baked, locale, kind)?,
locale,
&kind.as_bcp47_value(),
&date_time_format_options,
)
.map_err(|e| match e {
UnsupportedOptionsOrDataOrPatternError::UnsupportedOptions => {
DateTimeError::UnsupportedOptions
}
UnsupportedOptionsOrDataOrPatternError::Data(e) => DateTimeError::Data(e),
UnsupportedOptionsOrDataOrPatternError::Pattern(e) => DateTimeError::Pattern(e),
})?;
Ok(Self(
raw::ZonedDateTimeFormatter::try_new(
patterns,
|| {
calendar::load_symbols_for_any_calendar_kind(
&crate::provider::Baked,
locale,
kind,
)
},
locale,
time_zone_format_options,
)?,
calendar,
))
}
#[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::try_new_experimental)]
#[cfg(feature = "experimental")]
#[inline]
#[allow(clippy::too_many_arguments)]
pub fn try_new_experimental_unstable<P>(
provider: &P,
locale: &DataLocale,
date_time_format_options: DateTimeFormatterOptions,
time_zone_format_options: TimeZoneFormatterOptions,
) -> Result<Self, DateTimeError>
where
P: DataProvider<TimeSymbolsV1Marker>
+ DataProvider<TimeLengthsV1Marker>
+ DataProvider<crate::provider::calendar::DateSkeletonPatternsV1Marker>
+ DataProvider<WeekDataV1Marker>
+ DataProvider<provider::time_zones::TimeZoneFormatsV1Marker>
+ DataProvider<provider::time_zones::ExemplarCitiesV1Marker>
+ DataProvider<provider::time_zones::MetazoneGenericNamesLongV1Marker>
+ DataProvider<provider::time_zones::MetazoneGenericNamesShortV1Marker>
+ DataProvider<provider::time_zones::MetazoneSpecificNamesLongV1Marker>
+ DataProvider<provider::time_zones::MetazoneSpecificNamesShortV1Marker>
+ DataProvider<OrdinalV1Marker>
+ DataProvider<DecimalSymbolsV1Marker>
+ DataProvider<BuddhistDateLengthsV1Marker>
+ DataProvider<BuddhistDateSymbolsV1Marker>
+ DataProvider<ChineseCacheV1Marker>
+ DataProvider<ChineseDateLengthsV1Marker>
+ DataProvider<ChineseDateSymbolsV1Marker>
+ DataProvider<CopticDateLengthsV1Marker>
+ DataProvider<CopticDateSymbolsV1Marker>
+ DataProvider<DangiCacheV1Marker>
+ DataProvider<DangiDateLengthsV1Marker>
+ DataProvider<DangiDateSymbolsV1Marker>
+ DataProvider<EthiopianDateLengthsV1Marker>
+ DataProvider<EthiopianDateSymbolsV1Marker>
+ DataProvider<GregorianDateLengthsV1Marker>
+ DataProvider<GregorianDateSymbolsV1Marker>
+ DataProvider<HebrewDateLengthsV1Marker>
+ DataProvider<HebrewDateSymbolsV1Marker>
+ DataProvider<IndianDateLengthsV1Marker>
+ DataProvider<IndianDateSymbolsV1Marker>
+ DataProvider<IslamicDateLengthsV1Marker>
+ DataProvider<IslamicDateSymbolsV1Marker>
+ DataProvider<IslamicObservationalCacheV1Marker>
+ DataProvider<IslamicUmmAlQuraCacheV1Marker>
+ DataProvider<JapaneseDateLengthsV1Marker>
+ DataProvider<JapaneseDateSymbolsV1Marker>
+ DataProvider<JapaneseErasV1Marker>
+ DataProvider<JapaneseExtendedDateLengthsV1Marker>
+ DataProvider<JapaneseExtendedDateSymbolsV1Marker>
+ DataProvider<JapaneseExtendedErasV1Marker>
+ DataProvider<PersianDateLengthsV1Marker>
+ DataProvider<PersianDateSymbolsV1Marker>
+ DataProvider<RocDateLengthsV1Marker>
+ DataProvider<RocDateSymbolsV1Marker>
+ ?Sized,
{
let calendar = AnyCalendar::try_new_for_locale_unstable(provider, locale)?;
let kind = calendar.kind();
let patterns = PatternSelector::for_options_experimental(
provider,
calendar::load_lengths_for_any_calendar_kind(provider, locale, kind)?,
locale,
&kind.as_bcp47_value(),
&date_time_format_options,
)
.map_err(|e| match e {
UnsupportedOptionsOrDataOrPatternError::UnsupportedOptions => {
DateTimeError::UnsupportedOptions
}
UnsupportedOptionsOrDataOrPatternError::Data(e) => DateTimeError::Data(e),
UnsupportedOptionsOrDataOrPatternError::Pattern(e) => DateTimeError::Pattern(e),
})?;
Ok(Self(
raw::ZonedDateTimeFormatter::try_new_unstable(
provider,
patterns,
|| calendar::load_symbols_for_any_calendar_kind(provider, locale, kind),
locale,
time_zone_format_options,
)?,
calendar,
))
}
#[cfg(feature = "compiled_data")]
pub fn try_new(
locale: &DataLocale,
date_time_format_options: DateTimeFormatterOptions,
time_zone_format_options: TimeZoneFormatterOptions,
) -> Result<Self, DateTimeError> {
let calendar = AnyCalendar::new_for_locale(locale);
let kind = calendar.kind();
let patterns = PatternSelector::for_options(
&crate::provider::Baked,
calendar::load_lengths_for_any_calendar_kind(&crate::provider::Baked, locale, kind)?,
locale,
&date_time_format_options,
)
.map_err(|e| match e {
PatternForLengthError::Data(e) => DateTimeError::Data(e),
PatternForLengthError::Pattern(e) => DateTimeError::Pattern(e),
})?;
Ok(Self(
raw::ZonedDateTimeFormatter::try_new(
patterns,
|| {
calendar::load_symbols_for_any_calendar_kind(
&crate::provider::Baked,
locale,
kind,
)
},
locale,
time_zone_format_options,
)?,
calendar,
))
}
#[inline]
#[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::try_new)]
#[allow(clippy::too_many_arguments)]
pub fn try_new_unstable<P>(
provider: &P,
locale: &DataLocale,
date_time_format_options: DateTimeFormatterOptions,
time_zone_format_options: TimeZoneFormatterOptions,
) -> Result<Self, DateTimeError>
where
P: DataProvider<TimeSymbolsV1Marker>
+ DataProvider<TimeLengthsV1Marker>
+ DataProvider<WeekDataV1Marker>
+ DataProvider<provider::time_zones::TimeZoneFormatsV1Marker>
+ DataProvider<provider::time_zones::ExemplarCitiesV1Marker>
+ DataProvider<provider::time_zones::MetazoneGenericNamesLongV1Marker>
+ DataProvider<provider::time_zones::MetazoneGenericNamesShortV1Marker>
+ DataProvider<provider::time_zones::MetazoneSpecificNamesLongV1Marker>
+ DataProvider<provider::time_zones::MetazoneSpecificNamesShortV1Marker>
+ DataProvider<OrdinalV1Marker>
+ DataProvider<DecimalSymbolsV1Marker>
+ DataProvider<BuddhistDateLengthsV1Marker>
+ DataProvider<BuddhistDateSymbolsV1Marker>
+ DataProvider<ChineseCacheV1Marker>
+ DataProvider<ChineseDateLengthsV1Marker>
+ DataProvider<ChineseDateSymbolsV1Marker>
+ DataProvider<CopticDateLengthsV1Marker>
+ DataProvider<CopticDateSymbolsV1Marker>
+ DataProvider<DangiCacheV1Marker>
+ DataProvider<DangiDateLengthsV1Marker>
+ DataProvider<DangiDateSymbolsV1Marker>
+ DataProvider<EthiopianDateLengthsV1Marker>
+ DataProvider<EthiopianDateSymbolsV1Marker>
+ DataProvider<GregorianDateLengthsV1Marker>
+ DataProvider<GregorianDateSymbolsV1Marker>
+ DataProvider<HebrewDateLengthsV1Marker>
+ DataProvider<HebrewDateSymbolsV1Marker>
+ DataProvider<IndianDateLengthsV1Marker>
+ DataProvider<IndianDateSymbolsV1Marker>
+ DataProvider<IslamicDateLengthsV1Marker>
+ DataProvider<IslamicDateSymbolsV1Marker>
+ DataProvider<IslamicObservationalCacheV1Marker>
+ DataProvider<IslamicUmmAlQuraCacheV1Marker>
+ DataProvider<JapaneseDateLengthsV1Marker>
+ DataProvider<JapaneseDateSymbolsV1Marker>
+ DataProvider<JapaneseErasV1Marker>
+ DataProvider<JapaneseExtendedDateLengthsV1Marker>
+ DataProvider<JapaneseExtendedDateSymbolsV1Marker>
+ DataProvider<JapaneseExtendedErasV1Marker>
+ DataProvider<PersianDateLengthsV1Marker>
+ DataProvider<PersianDateSymbolsV1Marker>
+ DataProvider<RocDateLengthsV1Marker>
+ DataProvider<RocDateSymbolsV1Marker>
+ ?Sized,
{
let calendar = AnyCalendar::try_new_for_locale_unstable(provider, locale)?;
let kind = calendar.kind();
let patterns = PatternSelector::for_options(
provider,
calendar::load_lengths_for_any_calendar_kind(provider, locale, kind)?,
locale,
&date_time_format_options,
)
.map_err(|e| match e {
PatternForLengthError::Data(e) => DateTimeError::Data(e),
PatternForLengthError::Pattern(e) => DateTimeError::Pattern(e),
})?;
Ok(Self(
raw::ZonedDateTimeFormatter::try_new_unstable(
provider,
patterns,
|| calendar::load_symbols_for_any_calendar_kind(provider, locale, kind),
locale,
time_zone_format_options,
)?,
calendar,
))
}
#[doc = icu_provider::gen_any_buffer_unstable_docs!(ANY, Self::try_new)]
#[inline]
pub fn try_new_with_any_provider(
provider: &impl AnyProvider,
locale: &DataLocale,
options: DateTimeFormatterOptions,
time_zone_format_options: TimeZoneFormatterOptions,
) -> Result<Self, DateTimeError> {
let downcasting = provider.as_downcasting();
Self::try_new_unstable(&downcasting, locale, options, time_zone_format_options)
}
#[doc = icu_provider::gen_any_buffer_unstable_docs!(BUFFER, Self::try_new)]
#[inline]
#[cfg(feature = "serde")]
pub fn try_new_with_buffer_provider(
provider: &impl BufferProvider,
locale: &DataLocale,
options: DateTimeFormatterOptions,
time_zone_format_options: TimeZoneFormatterOptions,
) -> Result<Self, DateTimeError> {
let deserializing = provider.as_deserializing();
Self::try_new_unstable(&deserializing, locale, options, time_zone_format_options)
}
#[inline]
pub fn format<'l>(
&'l self,
date: &impl DateTimeInput<Calendar = AnyCalendar>,
time_zone: &impl TimeZoneInput,
) -> Result<FormattedZonedDateTime<'l>, DateTimeError> {
if let Some(converted) = self.convert_if_necessary(date)? {
Ok(self.0.format(&converted, time_zone))
} else {
Ok(self.0.format(date, time_zone))
}
}
#[inline]
pub fn format_to_string(
&self,
date: &impl DateTimeInput<Calendar = AnyCalendar>,
time_zone: &impl TimeZoneInput,
) -> Result<String, DateTimeError> {
Ok(self.format(date, time_zone)?.write_to_string().into_owned())
}
fn convert_if_necessary(
&self,
value: &impl DateTimeInput<Calendar = AnyCalendar>,
) -> Result<Option<ExtractedDateTimeInput>, DateTimeError> {
let this_calendar = self.1.kind();
let date_calendar = value.any_calendar_kind();
if Some(this_calendar) != date_calendar {
if date_calendar != Some(AnyCalendarKind::Iso) {
return Err(DateTimeError::MismatchedAnyCalendar(
this_calendar,
date_calendar,
));
}
let date = value.to_iso();
let time = Time::new(
value.hour().unwrap_or_default(),
value.minute().unwrap_or_default(),
value.second().unwrap_or_default(),
value.nanosecond().unwrap_or_default(),
);
let datetime = DateTime::new(date, time).to_any();
let converted = self.1.convert_any_datetime(&datetime);
let converted = ExtractedDateTimeInput::extract_from(&converted);
Ok(Some(converted))
} else {
Ok(None)
}
}
}
#[test]
#[cfg(feature = "serde")]
fn buffer_constructor() {
#![allow(clippy::zero_prefixed_literal)]
use icu::calendar::DateTime;
use icu::datetime::options::length;
use icu::datetime::ZonedDateTimeFormatter;
use icu::locid::locale;
use icu::timezone::CustomTimeZone;
use std::str::FromStr;
use writeable::assert_writeable_eq;
let provider = icu_provider_blob::BlobDataProvider::try_new_from_static_blob(include_bytes!(
"../../tests/data/blob.postcard"
))
.unwrap();
let zdtf = ZonedDateTimeFormatter::try_new_with_buffer_provider(
&provider,
&locale!("en").into(),
length::Bag::from_date_time_style(length::Date::Medium, length::Time::Long).into(),
Default::default(),
)
.unwrap();
assert_writeable_eq!(
zdtf.format(
&DateTime::try_new_iso_datetime(2021, 04, 08, 16, 12, 37)
.unwrap()
.to_any(),
&CustomTimeZone::from_str("-07:00").unwrap()
)
.unwrap(),
"Apr 8, 2021, 4:12:37 PM GMT-07:00"
);
}