use icu_calendar::cal::Hebrew;
use icu_calendar::Date;
use icu_datetime::fieldsets;
use icu_datetime::fieldsets::enums::{
CompositeDateTimeFieldSet, DateAndTimeFieldSet, DateFieldSet,
};
use icu_datetime::{DateTimeFormatterPreferences, FixedCalendarDateTimeFormatter};
use icu_locale_core::{locale, Locale};
use icu_time::{DateTime, Time};
use writeable::assert_writeable_eq;
const EXPECTED_DATETIME: &[&str] = &[
"Friday, December 22, 2023 at 9:22:53\u{202f}PM",
"vendredi 22 décembre 2023 à 21:22:53",
"2023年12月22日星期五 21:22:53",
"श\u{941}क\u{94d}रवार, 22 दिस\u{902}बर 2023 को 9:22:53 pm बजे",
"Friday, December 22, 2023 at 9:22\u{202f}PM",
"vendredi 22 décembre 2023 à 21:22",
"2023年12月22日星期五 21:22",
"श\u{941}क\u{94d}रवार, 22 दिस\u{902}बर 2023 को 9:22 pm बजे",
"December 22, 2023 at 9:22:53\u{202f}PM",
"22 décembre 2023 à 21:22:53",
"2023年12月22日 21:22:53",
"22 दिस\u{902}बर 2023 को 9:22:53 pm बजे",
"December 22, 2023 at 9:22\u{202f}PM",
"22 décembre 2023 à 21:22",
"2023年12月22日 21:22",
"22 दिस\u{902}बर 2023 को 9:22 pm बजे",
"Dec 22, 2023, 9:22:53\u{202f}PM",
"22 déc. 2023, 21:22:53",
"2023年12月22日 21:22:53",
"22 दिस॰ 2023, 9:22:53 pm",
"Dec 22, 2023, 9:22\u{202f}PM",
"22 déc. 2023, 21:22",
"2023年12月22日 21:22",
"22 दिस॰ 2023, 9:22 pm",
"12/22/23, 9:22:53\u{202f}PM",
"22/12/2023 21:22:53",
"2023/12/22 21:22:53",
"22/12/23, 9:22:53 pm",
"12/22/23, 9:22\u{202f}PM",
"22/12/2023 21:22",
"2023/12/22 21:22",
"22/12/23, 9:22 pm",
];
const EXPECTED_DATE: &[&str] = &[
"Friday, December 22, 2023",
"vendredi 22 décembre 2023",
"2023年12月22日星期五",
"शुक्रवार, 22 दिसंबर 2023",
"December 22, 2023",
"22 décembre 2023",
"2023年12月22日",
"22 दिसंबर 2023",
"Dec 22, 2023",
"22 déc. 2023",
"2023年12月22日",
"22 दिस॰ 2023",
"12/22/23",
"22/12/2023",
"2023/12/22",
"22/12/23",
];
#[test]
fn neo_datetime_lengths() {
let datetime = DateTime {
date: Date::try_new_gregorian(2023, 12, 22).unwrap(),
time: Time::try_new(21, 22, 53, 0).unwrap(),
};
let mut expected_iter = EXPECTED_DATETIME.iter();
use icu_datetime::options::TimePrecision::Minute as HM;
for field_set in [
DateAndTimeFieldSet::YMDET(fieldsets::YMDET::long()),
DateAndTimeFieldSet::YMDET(fieldsets::YMDET::long().with_time_precision(HM)),
DateAndTimeFieldSet::YMDT(fieldsets::YMDT::long()),
DateAndTimeFieldSet::YMDT(fieldsets::YMDT::long().with_time_precision(HM)),
DateAndTimeFieldSet::YMDT(fieldsets::YMDT::medium()),
DateAndTimeFieldSet::YMDT(fieldsets::YMDT::medium().with_time_precision(HM)),
DateAndTimeFieldSet::YMDT(fieldsets::YMDT::short()),
DateAndTimeFieldSet::YMDT(fieldsets::YMDT::short().with_time_precision(HM)),
] {
for locale in [locale!("en"), locale!("fr"), locale!("zh"), locale!("hi")] {
let prefs = DateTimeFormatterPreferences::from(&locale);
let skeleton = CompositeDateTimeFieldSet::DateTime(field_set);
let formatter = FixedCalendarDateTimeFormatter::try_new(prefs, skeleton).unwrap();
let formatted = formatter.format(&datetime);
let expected = expected_iter.next().unwrap();
assert_writeable_eq!(formatted, *expected, "{skeleton:?} {locale:?}");
}
}
}
#[test]
fn neo_date_lengths() {
let datetime = DateTime {
date: Date::try_new_gregorian(2023, 12, 22).unwrap(),
time: Time::try_new(21, 22, 53, 0).unwrap(),
};
let mut expected_iter = EXPECTED_DATE.iter();
for field_set in [
DateFieldSet::YMDE(fieldsets::YMDE::long()),
DateFieldSet::YMD(fieldsets::YMD::long()),
DateFieldSet::YMD(fieldsets::YMD::medium()),
DateFieldSet::YMD(fieldsets::YMD::short()),
] {
let date_skeleton = CompositeDateTimeFieldSet::Date(field_set);
for locale in [locale!("en"), locale!("fr"), locale!("zh"), locale!("hi")] {
let prefs = DateTimeFormatterPreferences::from(&locale);
let formatter = FixedCalendarDateTimeFormatter::try_new(prefs, date_skeleton).unwrap();
let formatted = formatter.format(&datetime);
let expected = expected_iter.next().unwrap();
assert_writeable_eq!(formatted, *expected, "{date_skeleton:?} {locale:?}");
}
}
}
#[test]
fn overlap_patterns() {
let datetime = DateTime {
date: Date::try_new_gregorian(2024, 8, 9).unwrap(),
time: Time::try_new(20, 40, 7, 250).unwrap(),
};
struct TestCase {
locale: Locale,
skeleton: CompositeDateTimeFieldSet,
expected: &'static str,
}
let cases = [
TestCase {
locale: locale!("en-US"),
skeleton: CompositeDateTimeFieldSet::DateTime(DateAndTimeFieldSet::ET(
fieldsets::ET::medium(),
)),
expected: "Fri 8:40:07\u{202f}PM",
},
TestCase {
locale: locale!("en-US"),
skeleton: CompositeDateTimeFieldSet::DateTime(DateAndTimeFieldSet::MDET(
fieldsets::MDET::medium(),
)),
expected: "Fri, Aug 9, 8:40:07\u{202f}PM",
},
TestCase {
locale: locale!("ru"),
skeleton: CompositeDateTimeFieldSet::DateTime(DateAndTimeFieldSet::ET(
fieldsets::ET::medium(),
)),
expected: "пт 20:40:07",
},
TestCase {
locale: locale!("ru"),
skeleton: CompositeDateTimeFieldSet::Date(DateFieldSet::E(fieldsets::E::medium())),
expected: "пт",
},
];
for TestCase {
locale,
skeleton,
expected,
} in cases
{
let prefs = DateTimeFormatterPreferences::from(&locale);
let formatter = FixedCalendarDateTimeFormatter::try_new(prefs, skeleton).unwrap();
let formatted = formatter.format(&datetime);
assert_writeable_eq!(formatted, expected, "{locale:?} {skeleton:?}");
}
}
#[test]
fn hebrew_months() {
let formatter =
FixedCalendarDateTimeFormatter::try_new(locale!("en").into(), fieldsets::YMD::medium())
.unwrap();
let formatted_datetime =
formatter.format(&Date::try_new_iso(2011, 3, 4).unwrap().to_calendar(Hebrew));
assert_writeable_eq!(formatted_datetime, "28 Adar I 5771");
let formatted_datetime =
formatter.format(&Date::try_new_iso(2011, 4, 3).unwrap().to_calendar(Hebrew));
assert_writeable_eq!(formatted_datetime, "28 Adar II 5771");
}
#[test]
fn test_5387() {
let datetime = DateTime {
date: Date::try_new_gregorian(2024, 8, 16).unwrap(),
time: Time::try_new(14, 15, 16, 0).unwrap(),
};
let formatter_auto = FixedCalendarDateTimeFormatter::try_new(
locale!("en").into(),
CompositeDateTimeFieldSet::DateTime(DateAndTimeFieldSet::ET(fieldsets::ET::medium())),
)
.unwrap();
let formatter_h12 = FixedCalendarDateTimeFormatter::try_new(
locale!("en-u-hc-h12").into(),
CompositeDateTimeFieldSet::DateTime(DateAndTimeFieldSet::ET(fieldsets::ET::medium())),
)
.unwrap();
let formatter_h24 = FixedCalendarDateTimeFormatter::try_new(
locale!("en-u-hc-h23").into(),
CompositeDateTimeFieldSet::DateTime(DateAndTimeFieldSet::ET(fieldsets::ET::medium())),
)
.unwrap();
assert_writeable_eq!(formatter_auto.format(&datetime), "Fri 2:15:16\u{202f}PM");
assert_writeable_eq!(formatter_h12.format(&datetime), "Fri, 2:15:16\u{202f}PM");
assert_writeable_eq!(formatter_h24.format(&datetime), "Fri, 14:15:16");
}
#[test]
fn test_vancouver_2026() {
use icu_datetime::{fieldsets, DateTimeFormatter};
use icu_time::zone::{TimeZone, UtcOffset};
use icu_time::ZonedDateTime;
let date = Date::try_new_gregorian(2026, 12, 1).unwrap();
let time = Time::try_new(12, 0, 0, 0).unwrap();
let fmt = DateTimeFormatter::try_new(
locale!("en-US").into(),
fieldsets::YMD::long()
.with_time_hm()
.with_zone(fieldsets::zone::SpecificShort),
)
.unwrap();
{
let offset = UtcOffset::from_seconds_unchecked(-8 * 3600);
let zone = TimeZone::from_iana_id("America/Vancouver")
.with_offset(Some(offset))
.at_date_time(DateTime { date, time });
let zdt = ZonedDateTime { date, time, zone };
assert_writeable_eq!(fmt.format(&zdt), "December 1, 2026 at 12:00\u{202f}PM PST");
}
{
let offset = UtcOffset::from_seconds_unchecked(-7 * 3600);
let zone = TimeZone::from_iana_id("America/Vancouver")
.with_offset(Some(offset))
.at_date_time(DateTime { date, time });
let zdt = ZonedDateTime { date, time, zone };
assert_writeable_eq!(fmt.format(&zdt), "December 1, 2026 at 12:00\u{202f}PM PDT");
}
}