Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions components/datetime/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ pub enum DateFields {
/// The weekday alone, as in
/// “Saturday”.
E,
/// Weekday and hour, as in “Saturday 3 PM”.
Eh,
/// A standalone month, as in
/// “January”.
M,
Expand All @@ -150,6 +152,7 @@ impl DateFields {
Self::MDE,
Self::YMDE,
Self::E,
Self::Eh,
Self::M,
Self::YM,
Self::Y,
Expand All @@ -165,6 +168,7 @@ impl DateFields {
DateFields::MDE => false,
DateFields::YMDE => false,
DateFields::E => false,
DateFields::Eh => false,
DateFields::M => true,
DateFields::YM => true,
DateFields::Y => true,
Expand Down Expand Up @@ -492,6 +496,9 @@ impl FieldSetBuilder {
Date(DateFieldSet::YMDE(fieldsets::YMDE::take_from_builder(self)))
}
Some(DateFields::E) => Date(DateFieldSet::E(fieldsets::E::take_from_builder(self))),
Some(DateFields::Eh) => {
Date(DateFieldSet::Eh(fieldsets::Eh::take_from_builder(self)))
}
Some(DateFields::M) => CalendarPeriod(CalendarPeriodFieldSet::M(
fieldsets::M::take_from_builder(self),
)),
Expand Down Expand Up @@ -754,6 +761,7 @@ impl FieldSetBuilder {
DateAndTimeFieldSet::YMDET(fieldsets::YMDET::take_from_builder(&mut self))
}
DateFields::E => DateAndTimeFieldSet::ET(fieldsets::ET::take_from_builder(&mut self)),
DateFields::Eh => DateAndTimeFieldSet::EH(fieldsets::EH::take_from_builder(&mut self)),
DateFields::M | DateFields::YM | DateFields::Y => {
return Err(BuilderError::InvalidDateFields)
}
Expand Down Expand Up @@ -1092,6 +1100,7 @@ mod tests {
DateFields::MDE,
DateFields::YMDE,
DateFields::E,
DateFields::Eh,
];

static CALENDAR_PERIOD_FIELD_SETS: &[DateFields] =
Expand Down
36 changes: 30 additions & 6 deletions components/datetime/src/dynamic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ pub enum DateFieldSet {
/// The weekday alone, as in
/// “Saturday”.
E(fieldsets::E),
/// Weekday and hour, as in “Saturday 3 PM”.
Eh(fieldsets::Eh),
}

/// An enumeration over all possible calendar period field sets.
Expand Down Expand Up @@ -142,6 +144,8 @@ pub enum CalendarPeriodFieldSet {
pub enum TimeFieldSet {
/// A time of day.
T(fieldsets::T),
/// A time of day with generic non-location timezone.
Tv(fieldsets::Tv),
}

/// An enumeration over all possible zone field sets.
Expand Down Expand Up @@ -211,6 +215,9 @@ pub enum DateAndTimeFieldSet {
/// The weekday alone with time of day, as in
/// “Saturday at 10:31 AM”.
ET(fieldsets::ET),
/// The weekday and hour, as in
/// “Saturday 3 PM”.
EH(fieldsets::EH),
}

/// An enum supporting date, calendar period, time, and date+time field sets
Expand Down Expand Up @@ -343,7 +350,7 @@ macro_rules! impl_attrs {
/// ```
pub const ALL_DATA_MARKER_ATTRIBUTES: &'static [&'static DataMarkerAttributes] = &[
$(
Self::$attr_var,
&Self::$attr_var,
)+
];
}
Expand Down Expand Up @@ -399,7 +406,14 @@ macro_rules! impl_attrs {
};
(@time, $type:path, [$(($attr_var:ident, $str_var:ident, $value:literal)),+,]) => {
impl_attrs! { @attrs, $type, [$(($attr_var, $str_var, $value)),+,] }
impl_attrs! { @to_raw_options, $type, [T,] }
impl $type {
pub(crate) fn to_raw_options(self) -> RawOptions {
match self {
Self::T(variant) => variant.to_raw_options(),
Self::Tv(variant) => variant.to_raw_options(),
}
}
}
impl_attrs! { @composite, $type, Time }
};
(@zone, $type:path, [$($variant:ident),+,]) => {
Expand Down Expand Up @@ -459,6 +473,7 @@ impl_attrs! {
(MDE, ATTR_MDE, STR_MDE, "m0de"),
(YMDE, ATTR_YMDE, STR_YMDE, "ym0de"),
(E, ATTR_E, STR_E, "e"),
(Eh, ATTR_EH, STR_EH, "eh"),
]
}

Expand All @@ -479,6 +494,9 @@ impl_attrs! {
(ATTR_T, STR_T, "j"),
(ATTR_T12, STR_T12, "h"),
(ATTR_T24, STR_T24, "h0"),
(ATTR_TV, STR_TV, "jv"),
(ATTR_TV12, STR_TV12, "hv"),
(ATTR_TV24, STR_TV24, "h0v"),
]
}

Expand All @@ -488,10 +506,13 @@ impl TimeFieldSet {
hour_cycle: Option<provider::fields::Hour>,
) -> &'static DataMarkerAttributes {
use provider::fields::Hour::*;
match hour_cycle {
None => Self::ATTR_T,
Some(H11 | H12) => Self::ATTR_T12,
Some(H23) => Self::ATTR_T24,
match (self, hour_cycle) {
(Self::T(_), None) => Self::ATTR_T,
(Self::T(_), Some(H11 | H12)) => Self::ATTR_T12,
(Self::T(_), Some(H23)) => Self::ATTR_T24,
(Self::Tv(_), None) => Self::ATTR_TV,
(Self::Tv(_), Some(H11 | H12)) => Self::ATTR_TV12,
(Self::Tv(_), Some(H23)) => Self::ATTR_TV24,
}
}
}
Expand All @@ -516,6 +537,7 @@ impl_attrs! {
DateAndTimeFieldSet,
[
(ATTR_ET, STR_ET, "ej"),
(ATTR_EH, STR_EH, "eh"),
]
}

Expand All @@ -530,13 +552,15 @@ impl_attrs! {
(MDE, MDET),
(YMDE, YMDET),
(E, ET),
(Eh, EH),
]
}

impl DateAndTimeFieldSet {
pub(crate) const fn id_str(self) -> Option<&'static DataMarkerAttributes> {
match self {
DateAndTimeFieldSet::ET(_) => Some(Self::ATTR_ET),
DateAndTimeFieldSet::EH(_) => Some(Self::ATTR_EH),
_ => None,
}
}
Expand Down
25 changes: 25 additions & 0 deletions components/datetime/src/fieldsets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1052,6 +1052,18 @@ impl_date_marker!(
input_weekday = yes,
);

impl_date_marker!(
/// Weekday and hour, as in "Saturday 3 PM".
Eh,
EH,
description = "weekday and hour",
sample_length = long,
sample = "Friday",
sample_time = "Friday 3 PM",
weekdays = yes,
input_weekday = yes,
);

impl_date_marker!(
/// This format may use ordinal formatting, such as "Friday the 17th",
/// in the future. See CLDR-18040.
Expand Down Expand Up @@ -1322,6 +1334,19 @@ impl_time_marker!(
input_subsecond = yes,
);

impl_time_marker!(
/// Time with generic non-location timezone.
Tv,
description = "time with generic non-location timezone",
sample_length = medium,
sample = "3:47:50 PM PT",
dayperiods = yes,
input_hour = yes,
input_minute = yes,
input_second = yes,
input_subsecond = yes,
);

/// Time zone field sets
pub mod zone {
use super::*;
Expand Down
30 changes: 24 additions & 6 deletions provider/source/src/datetime/semantic_skeletons.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,19 @@ impl SourceDataProvider {
while let Err(e) = names
.load_for_pattern(&DebugProvider, &DateTimePattern::from(pattern.clone()))
{
let PatternLoadError::ConflictingField {
field: requested_field,
previous_field,
} = e
else {
panic!("only know how to fix ConflictingField, but got: {e:?}")
let (requested_field, previous_field) = match e {
PatternLoadError::ConflictingField {
field: requested_field,
previous_field,
} => (requested_field, previous_field),
PatternLoadError::FormatterTooSpecific(field) => {
// This can happen if the selected pattern has a field that
// doesn't exist in the components bag. For example, 'v' vs 'V'.
// For now, we just skip it.
log::warn!("FormatterTooSpecific: {:?}", field);
break;
}
_ => panic!("only know how to fix ConflictingField, but got: {e:?}"),
};
log_fn(previous_field, requested_field, variant.distance);
let requested_field = Field::from(requested_field);
Expand Down Expand Up @@ -477,6 +484,9 @@ fn gen_time_components(
if check_for_field(attributes, "h0") {
filtered_components.hour_cycle = Some(HourCycle::H23);
}
if check_for_field(attributes, "v") {
filtered_components.time_zone_name = Some(components::TimeZoneName::ShortGeneric);
}
filtered_components
}

Expand Down Expand Up @@ -536,6 +546,14 @@ fn gen_date_components(
if check_for_field(attributes, "j") {
filtered_components.hour = Some(components::Numeric::Numeric);
}
if check_for_field(attributes, "h") {
filtered_components.hour = Some(components::Numeric::Numeric);
filtered_components.hour_cycle = Some(HourCycle::H12);
}
if check_for_field(attributes, "h0") {
filtered_components.hour = Some(components::Numeric::Numeric);
filtered_components.hour_cycle = Some(HourCycle::H23);
}
filtered_components
}

Expand Down
80 changes: 80 additions & 0 deletions provider/source/src/datetime/semantic_skeletons/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,86 @@ fn test_en_overlap_patterns() {
);
}

#[test]
fn test_en_tv_patterns() {
use icu::locale::locale;

let provider = SourceDataProvider::new_testing();
let payload: DataPayload<DatetimePatternsTimeV1> = provider
.load(DataRequest {
id: DataIdentifierBorrowed::for_marker_attributes_and_locale(
DataMarkerAttributes::from_str_or_panic("jv"),
&locale!("en").into(),
),
metadata: Default::default(),
})
.unwrap()
.payload;

let json_str = serde_json::to_string_pretty(payload.get()).unwrap();

assert_eq!(
json_str,
r#"{
"variant_pattern_indices": [
2,
2,
2,
3,
3,
3
],
"elements": [
"h a v",
"h:m a",
"h:m:s a"
]
}"#
);
}

#[test]
fn test_en_eh_patterns() {
use icu::locale::locale;

let provider = SourceDataProvider::new_testing();
let payload: DataPayload<DatetimePatternsDateGregorianV1> = provider
.load(DataRequest {
id: DataIdentifierBorrowed::for_marker_attributes_and_locale(
DataMarkerAttributes::from_str_or_panic("eh"),
&locale!("en").into(),
),
metadata: Default::default(),
})
.unwrap()
.payload;

let json_str = serde_json::to_string_pretty(payload.get()).unwrap();

assert_eq!(
json_str,
r#"{
"has_explicit_medium": true,
"variant_pattern_indices": [
3,
4,
4,
5,
6,
6
],
"elements": [
"EEEE h a",
"E h a",
"EEEE h:m a",
"E h:mm a",
"EEEE h:m:s a",
"E h:mm:ss a"
]
}"#
);
}

/// This is a test that should eventually be moved to CLDR.
///
/// See: <https://unicode-org.atlassian.net/browse/CLDR-14993>
Expand Down
Loading