use super::names::RawDateTimeNamesBorrowed;
use super::pattern::DateTimePatternBorrowed;
use crate::format::datetime::try_write_pattern_items;
use crate::format::DateTimeInputUnchecked;
use crate::pattern::FormattedDateTimePatternError;
use crate::scaffold::*;
use crate::scaffold::{
AllInputMarkers, DateInputMarkers, DateTimeMarkers, InFixedCalendar, TimeMarkers,
TypedDateDataMarkers, ZoneMarkers,
};
use core::fmt;
use core::marker::PhantomData;
use writeable::TryWriteable;
#[derive(Debug, Copy, Clone)]
pub struct DateTimePatternFormatter<'a, C: CldrCalendar, FSet> {
inner: RawDateTimePatternFormatter<'a>,
_calendar: PhantomData<C>,
_marker: PhantomData<FSet>,
}
#[derive(Debug, Copy, Clone)]
pub(crate) struct RawDateTimePatternFormatter<'a> {
pattern: DateTimePatternBorrowed<'a>,
names: RawDateTimeNamesBorrowed<'a>,
}
impl<'a, C: CldrCalendar, FSet> DateTimePatternFormatter<'a, C, FSet> {
pub(crate) fn new(
pattern: DateTimePatternBorrowed<'a>,
names: RawDateTimeNamesBorrowed<'a>,
) -> Self {
Self {
inner: RawDateTimePatternFormatter { pattern, names },
_calendar: PhantomData,
_marker: PhantomData,
}
}
}
impl<'a, C: CldrCalendar, FSet: DateTimeMarkers> DateTimePatternFormatter<'a, C, FSet>
where
FSet::D: TypedDateDataMarkers<C> + DateInputMarkers,
FSet::T: TimeMarkers,
FSet::Z: ZoneMarkers,
{
pub fn format<I>(&self, datetime: &I) -> FormattedDateTimePattern<'a>
where
I: ?Sized + InFixedCalendar<C> + AllInputMarkers<FSet>,
{
FormattedDateTimePattern {
pattern: self.inner.pattern,
input: DateTimeInputUnchecked::extract_from_neo_input::<FSet::D, FSet::T, FSet::Z, I>(
datetime,
),
names: self.inner.names,
}
}
}
#[derive(Debug)]
pub struct FormattedDateTimePattern<'a> {
pattern: DateTimePatternBorrowed<'a>,
input: DateTimeInputUnchecked,
names: RawDateTimeNamesBorrowed<'a>,
}
impl TryWriteable for FormattedDateTimePattern<'_> {
type Error = FormattedDateTimePatternError;
fn try_write_to_parts<S: writeable::PartsWrite + ?Sized>(
&self,
sink: &mut S,
) -> Result<Result<(), Self::Error>, fmt::Error> {
try_write_pattern_items(
self.pattern.0.as_borrowed().metadata,
self.pattern.0.as_borrowed().items.iter(),
&self.input,
&self.names,
self.names.decimal_formatter,
sink,
)
}
}
#[cfg(test)]
#[cfg(feature = "compiled_data")]
mod tests {
use crate::provider::fields::Field;
use super::super::*;
use icu_calendar::{cal::KoreanTraditional, Date, Gregorian};
use icu_locale_core::locale;
use icu_time::{DateTime, Time};
use writeable::assert_try_writeable_eq;
#[test]
fn test_basic_pattern_formatting() {
let locale = locale!("en").into();
let mut names: FixedCalendarDateTimeNames<Gregorian> =
FixedCalendarDateTimeNames::try_new(locale).unwrap();
names
.load_month_names(&crate::provider::Baked, MonthNameLength::Wide)
.unwrap()
.load_weekday_names(&crate::provider::Baked, WeekdayNameLength::Abbreviated)
.unwrap()
.load_year_names(&crate::provider::Baked, YearNameLength::Narrow)
.unwrap()
.load_day_period_names(&crate::provider::Baked, DayPeriodNameLength::Abbreviated)
.unwrap();
let pattern: DateTimePattern = "'It is' E, MMMM d, y GGGGG 'at' hh:mm a'!'"
.parse()
.unwrap();
let datetime = DateTime {
date: Date::try_new_gregorian(2023, 10, 25).unwrap(),
time: Time::try_new(15, 0, 55, 0).unwrap(),
};
let formatted_pattern = names.with_pattern_unchecked(&pattern).format(&datetime);
assert_try_writeable_eq!(
formatted_pattern,
"It is Wed, October 25, 2023 A at 03:00 PM!",
Ok(()),
);
}
#[test]
fn test_era_coverage() {
let locale = locale!("uk").into();
#[derive(Debug)]
struct TestCase {
pattern: &'static str,
length: YearNameLength,
expected: &'static str,
}
let cases = [
TestCase {
pattern: "<G>",
length: YearNameLength::Abbreviated,
expected: "<н. е.>",
},
TestCase {
pattern: "<GG>",
length: YearNameLength::Abbreviated,
expected: "<н. е.>",
},
TestCase {
pattern: "<GGG>",
length: YearNameLength::Abbreviated,
expected: "<н. е.>",
},
TestCase {
pattern: "<GGGG>",
length: YearNameLength::Wide,
expected: "<нашої ери>",
},
TestCase {
pattern: "<GGGGG>",
length: YearNameLength::Narrow,
expected: "<н.е.>",
},
];
for cas in cases {
let TestCase {
pattern,
length,
expected,
} = cas;
let mut names: FixedCalendarDateTimeNames<Gregorian> =
FixedCalendarDateTimeNames::try_new(locale).unwrap();
names
.load_year_names(&crate::provider::Baked, length)
.unwrap();
let pattern: DateTimePattern = pattern.parse().unwrap();
let datetime = DateTime {
date: Date::try_new_gregorian(2023, 11, 17).unwrap(),
time: Time::try_new(13, 41, 28, 0).unwrap(),
};
let formatted_pattern = names.with_pattern_unchecked(&pattern).format(&datetime);
assert_try_writeable_eq!(formatted_pattern, expected, Ok(()), "{cas:?}");
}
}
#[test]
fn test_month_coverage() {
let locale = locale!("uk").into();
#[derive(Debug)]
struct TestCase {
pattern: &'static str,
length: MonthNameLength,
expected: &'static str,
}
let cases = [
TestCase {
pattern: "<MMM>",
length: MonthNameLength::Abbreviated,
expected: "<лист.>",
},
TestCase {
pattern: "<MMMM>",
length: MonthNameLength::Wide,
expected: "<листопада>",
},
TestCase {
pattern: "<MMMMM>",
length: MonthNameLength::Narrow,
expected: "<л>",
},
TestCase {
pattern: "<LLL>",
length: MonthNameLength::StandaloneAbbreviated,
expected: "<лист.>",
},
TestCase {
pattern: "<LLLL>",
length: MonthNameLength::StandaloneWide,
expected: "<листопад>",
},
TestCase {
pattern: "<LLLLL>",
length: MonthNameLength::StandaloneNarrow,
expected: "<Л>",
},
];
for cas in cases {
let TestCase {
pattern,
length,
expected,
} = cas;
let mut names: FixedCalendarDateTimeNames<Gregorian> =
FixedCalendarDateTimeNames::try_new(locale).unwrap();
names
.load_month_names(&crate::provider::Baked, length)
.unwrap();
let pattern: DateTimePattern = pattern.parse().unwrap();
let datetime = DateTime {
date: Date::try_new_gregorian(2023, 11, 17).unwrap(),
time: Time::try_new(13, 41, 28, 0).unwrap(),
};
let formatted_pattern = names.with_pattern_unchecked(&pattern).format(&datetime);
assert_try_writeable_eq!(formatted_pattern, expected, Ok(()), "{cas:?}");
}
}
#[test]
fn test_weekday_coverage() {
let locale = locale!("uk").into();
#[derive(Debug)]
struct TestCase {
pattern: &'static str,
length: WeekdayNameLength,
expected: &'static str,
}
let cases = [
TestCase {
pattern: "<E>",
length: WeekdayNameLength::Abbreviated,
expected: "<пт>",
},
TestCase {
pattern: "<EE>",
length: WeekdayNameLength::Abbreviated,
expected: "<пт>",
},
TestCase {
pattern: "<EEE>",
length: WeekdayNameLength::Abbreviated,
expected: "<пт>",
},
TestCase {
pattern: "<EEEE>",
length: WeekdayNameLength::Wide,
expected: "<пʼятниця>",
},
TestCase {
pattern: "<EEEEE>",
length: WeekdayNameLength::Narrow,
expected: "<П>",
},
TestCase {
pattern: "<EEEEEE>",
length: WeekdayNameLength::Short,
expected: "<пт>",
},
TestCase {
pattern: "<eee>",
length: WeekdayNameLength::Abbreviated,
expected: "<пт>",
},
TestCase {
pattern: "<eeee>",
length: WeekdayNameLength::Wide,
expected: "<пʼятниця>",
},
TestCase {
pattern: "<eeeee>",
length: WeekdayNameLength::Narrow,
expected: "<П>",
},
TestCase {
pattern: "<eeeeee>",
length: WeekdayNameLength::Short,
expected: "<пт>",
},
TestCase {
pattern: "<ccc>",
length: WeekdayNameLength::StandaloneAbbreviated,
expected: "<пт>",
},
TestCase {
pattern: "<cccc>",
length: WeekdayNameLength::StandaloneWide,
expected: "<пʼятниця>",
},
TestCase {
pattern: "<ccccc>",
length: WeekdayNameLength::StandaloneNarrow,
expected: "<П>",
},
TestCase {
pattern: "<cccccc>",
length: WeekdayNameLength::StandaloneShort,
expected: "<пт>",
},
];
for cas in cases {
let TestCase {
pattern,
length,
expected,
} = cas;
let mut names: FixedCalendarDateTimeNames<Gregorian> =
FixedCalendarDateTimeNames::try_new(locale).unwrap();
names
.load_weekday_names(&crate::provider::Baked, length)
.unwrap();
let pattern: DateTimePattern = pattern.parse().unwrap();
let datetime = DateTime {
date: Date::try_new_gregorian(2023, 11, 17).unwrap(),
time: Time::try_new(13, 41, 28, 0).unwrap(),
};
let formatted_pattern = names.with_pattern_unchecked(&pattern).format(&datetime);
assert_try_writeable_eq!(formatted_pattern, expected, Ok(()), "{cas:?}");
}
}
#[test]
fn test_dayperiod_coverage() {
let locale = locale!("th").into();
#[derive(Debug)]
struct TestCase {
pattern: &'static str,
length: DayPeriodNameLength,
expected: &'static str,
}
let cases = [
TestCase {
pattern: "<a>",
length: DayPeriodNameLength::Abbreviated,
expected: "<PM>",
},
TestCase {
pattern: "<aa>",
length: DayPeriodNameLength::Abbreviated,
expected: "<PM>",
},
TestCase {
pattern: "<aaa>",
length: DayPeriodNameLength::Abbreviated,
expected: "<PM>",
},
TestCase {
pattern: "<aaaa>",
length: DayPeriodNameLength::Wide,
expected: "<หลังเที่ยง>",
},
TestCase {
pattern: "<aaaaa>",
length: DayPeriodNameLength::Narrow,
expected: "<p>",
},
TestCase {
pattern: "<b>",
length: DayPeriodNameLength::Abbreviated,
expected: "<PM>",
},
TestCase {
pattern: "<bb>",
length: DayPeriodNameLength::Abbreviated,
expected: "<PM>",
},
TestCase {
pattern: "<bbb>",
length: DayPeriodNameLength::Abbreviated,
expected: "<PM>",
},
TestCase {
pattern: "<bbbb>",
length: DayPeriodNameLength::Wide,
expected: "<หลังเที่ยง>",
},
TestCase {
pattern: "<bbbbb>",
length: DayPeriodNameLength::Narrow,
expected: "<p>",
},
];
for cas in cases {
let TestCase {
pattern,
length,
expected,
} = cas;
let mut names: FixedCalendarDateTimeNames<Gregorian> =
FixedCalendarDateTimeNames::try_new(locale).unwrap();
names
.load_day_period_names(&crate::provider::Baked, length)
.unwrap();
let pattern: DateTimePattern = pattern.parse().unwrap();
let datetime = DateTime {
date: Date::try_new_gregorian(2023, 11, 17).unwrap(),
time: Time::try_new(13, 41, 28, 0).unwrap(),
};
let formatted_pattern = names.with_pattern_unchecked(&pattern).format(&datetime);
assert_try_writeable_eq!(formatted_pattern, expected, Ok(()), "{cas:?}");
}
}
#[test]
fn test_cyclic_year() {
use crate::provider::fields::{FieldLength, FieldSymbol, Year};
let locale = locale!("uk").into();
let pattern: DateTimePattern = "UUUU".parse().unwrap();
let names: FixedCalendarDateTimeNames<Gregorian> =
FixedCalendarDateTimeNames::try_new(locale).unwrap();
let datetime = DateTime {
date: Date::try_new_gregorian(2023, 11, 17).unwrap(),
time: Time::try_new(13, 41, 28, 0).unwrap(),
};
assert_try_writeable_eq!(
names.with_pattern_unchecked(&pattern).format(&datetime),
"2023",
Err(FormattedDateTimePatternError::UnsupportedField(ErrorField(
Field {
symbol: FieldSymbol::Year(Year::Cyclic),
length: FieldLength::Four
}
))),
);
let mut names: FixedCalendarDateTimeNames<KoreanTraditional> =
FixedCalendarDateTimeNames::try_new(locale).unwrap();
names
.load_year_names(&crate::provider::Baked, YearNameLength::Wide)
.unwrap();
let datetime = DateTime {
date: datetime.date.to_calendar(KoreanTraditional::new()),
time: datetime.time,
};
assert_try_writeable_eq!(
names.with_pattern_unchecked(&pattern).format(&datetime),
"gui-mao",
Ok(()),
);
}
}