diff --git a/components/calendar/src/cal/abstract_gregorian.rs b/components/calendar/src/cal/abstract_gregorian.rs index 0967e4d1776..0449f1ded54 100644 --- a/components/calendar/src/cal/abstract_gregorian.rs +++ b/components/calendar/src/cal/abstract_gregorian.rs @@ -22,7 +22,8 @@ pub(crate) trait GregorianYears: Clone + core::fmt::Debug { // Positive if after 0 CE const EXTENDED_YEAR_OFFSET: i32 = 0; - fn extended_from_era_year(&self, era: Option<&str>, year: i32) -> Result; + fn extended_from_era_year(&self, era: Option<&[u8]>, year: i32) + -> Result; fn era_year_from_extended(&self, extended_year: i32, month: u8, day: u8) -> EraYear; @@ -90,7 +91,7 @@ impl DateFieldsResolver for AbstractGregorian { #[inline] fn year_info_from_era( &self, - era: &str, + era: &[u8], era_year: i32, ) -> Result { Ok(self.0.extended_from_era_year(Some(era), era_year)? + Y::EXTENDED_YEAR_OFFSET) diff --git a/components/calendar/src/cal/buddhist.rs b/components/calendar/src/cal/buddhist.rs index 70c778a196c..443bddd0d44 100644 --- a/components/calendar/src/cal/buddhist.rs +++ b/components/calendar/src/cal/buddhist.rs @@ -48,9 +48,13 @@ pub(crate) struct BuddhistEra; impl GregorianYears for BuddhistEra { const EXTENDED_YEAR_OFFSET: i32 = -543; - fn extended_from_era_year(&self, era: Option<&str>, year: i32) -> Result { + fn extended_from_era_year( + &self, + era: Option<&[u8]>, + year: i32, + ) -> Result { match era { - Some("be") | None => Ok(year), + Some(b"be") | None => Ok(year), _ => Err(UnknownEraError), } } diff --git a/components/calendar/src/cal/chinese.rs b/components/calendar/src/cal/chinese.rs index a96a324e40c..d464a190222 100644 --- a/components/calendar/src/cal/chinese.rs +++ b/components/calendar/src/cal/chinese.rs @@ -562,7 +562,7 @@ impl DateFieldsResolver for LunarChinese { #[inline] fn year_info_from_era( &self, - _era: &str, + _era: &[u8], _era_year: i32, ) -> Result { // This calendar has no era codes diff --git a/components/calendar/src/cal/coptic.rs b/components/calendar/src/cal/coptic.rs index d218bfe08d3..704e191efbe 100644 --- a/components/calendar/src/cal/coptic.rs +++ b/components/calendar/src/cal/coptic.rs @@ -89,11 +89,11 @@ impl DateFieldsResolver for Coptic { #[inline] fn year_info_from_era( &self, - era: &str, + era: &[u8], era_year: i32, ) -> Result { match era { - "am" => Ok(era_year), + b"am" => Ok(era_year), _ => Err(UnknownEraError), } } diff --git a/components/calendar/src/cal/ethiopian.rs b/components/calendar/src/cal/ethiopian.rs index d012c61c6ac..b74e8bd54c2 100644 --- a/components/calendar/src/cal/ethiopian.rs +++ b/components/calendar/src/cal/ethiopian.rs @@ -79,12 +79,12 @@ impl DateFieldsResolver for Ethiopian { #[inline] fn year_info_from_era( &self, - era: &str, + era: &[u8], era_year: i32, ) -> Result { match (self.era_style(), era) { - (EthiopianEraStyle::AmeteMihret, "am") => Ok(era_year + AMETE_MIHRET_OFFSET), - (_, "aa") => Ok(era_year + AMETE_ALEM_OFFSET), + (EthiopianEraStyle::AmeteMihret, b"am") => Ok(era_year + AMETE_MIHRET_OFFSET), + (_, b"aa") => Ok(era_year + AMETE_ALEM_OFFSET), (_, _) => Err(UnknownEraError), } } diff --git a/components/calendar/src/cal/gregorian.rs b/components/calendar/src/cal/gregorian.rs index c9be5cdcb0c..01b00afda02 100644 --- a/components/calendar/src/cal/gregorian.rs +++ b/components/calendar/src/cal/gregorian.rs @@ -15,11 +15,15 @@ impl_with_abstract_gregorian!(crate::cal::Gregorian, GregorianDateInner, CeBce, pub(crate) struct CeBce; impl GregorianYears for CeBce { - fn extended_from_era_year(&self, era: Option<&str>, year: i32) -> Result { + fn extended_from_era_year( + &self, + era: Option<&[u8]>, + year: i32, + ) -> Result { match era { None => Ok(year), - Some("ad" | "ce") => Ok(year), - Some("bce" | "bc") => Ok(1 - year), + Some(b"ad" | b"ce") => Ok(year), + Some(b"bce" | b"bc") => Ok(1 - year), Some(_) => Err(UnknownEraError), } } diff --git a/components/calendar/src/cal/hebrew.rs b/components/calendar/src/cal/hebrew.rs index c0e7d3e712c..93231c2bf40 100644 --- a/components/calendar/src/cal/hebrew.rs +++ b/components/calendar/src/cal/hebrew.rs @@ -123,11 +123,11 @@ impl DateFieldsResolver for Hebrew { #[inline] fn year_info_from_era( &self, - era: &str, + era: &[u8], era_year: i32, ) -> Result { match era { - "am" => Ok(HebrewYearInfo::compute(era_year)), + b"am" => Ok(HebrewYearInfo::compute(era_year)), _ => Err(UnknownEraError), } } diff --git a/components/calendar/src/cal/hijri.rs b/components/calendar/src/cal/hijri.rs index e75e644378e..9e4133931f0 100644 --- a/components/calendar/src/cal/hijri.rs +++ b/components/calendar/src/cal/hijri.rs @@ -725,12 +725,12 @@ impl DateFieldsResolver for Hijri { #[inline] fn year_info_from_era( &self, - era: &str, + era: &[u8], era_year: i32, ) -> Result { let extended_year = match era { - "ah" => era_year, - "bh" => 1 - era_year, + b"ah" => era_year, + b"bh" => 1 - era_year, _ => return Err(UnknownEraError), }; Ok(self.year_info_from_extended(extended_year)) diff --git a/components/calendar/src/cal/indian.rs b/components/calendar/src/cal/indian.rs index 9cb55bd496f..3cf6e596760 100644 --- a/components/calendar/src/cal/indian.rs +++ b/components/calendar/src/cal/indian.rs @@ -82,11 +82,11 @@ impl DateFieldsResolver for Indian { #[inline] fn year_info_from_era( &self, - era: &str, + era: &[u8], era_year: i32, ) -> Result { match era { - "shaka" => Ok(era_year), + b"shaka" => Ok(era_year), _ => Err(UnknownEraError), } } diff --git a/components/calendar/src/cal/iso.rs b/components/calendar/src/cal/iso.rs index f7ae0181587..1792c7e76e9 100644 --- a/components/calendar/src/cal/iso.rs +++ b/components/calendar/src/cal/iso.rs @@ -31,9 +31,13 @@ impl_with_abstract_gregorian!(crate::cal::Iso, IsoDateInner, IsoEra, _x, IsoEra) pub(crate) struct IsoEra; impl GregorianYears for IsoEra { - fn extended_from_era_year(&self, era: Option<&str>, year: i32) -> Result { + fn extended_from_era_year( + &self, + era: Option<&[u8]>, + year: i32, + ) -> Result { match era { - Some("default") | None => Ok(year), + Some(b"default") | None => Ok(year), Some(_) => Err(UnknownEraError), } } diff --git a/components/calendar/src/cal/japanese.rs b/components/calendar/src/cal/japanese.rs index f9889e22aea..8d8b4301193 100644 --- a/components/calendar/src/cal/japanese.rs +++ b/components/calendar/src/cal/japanese.rs @@ -169,7 +169,11 @@ const REIWA_START: EraStartDate = EraStartDate { }; impl GregorianYears for &'_ Japanese { - fn extended_from_era_year(&self, era: Option<&str>, year: i32) -> Result { + fn extended_from_era_year( + &self, + era: Option<&[u8]>, + year: i32, + ) -> Result { if let Ok(g) = CeBce.extended_from_era_year(era, year) { return Ok(g); } @@ -179,28 +183,32 @@ impl GregorianYears for &'_ Japanese { }; // Avoid linear search by trying well known eras - if era == "reiwa" { + if era == b"reiwa" { return Ok(year - 1 + REIWA_START.year); - } else if era == "heisei" { + } else if era == b"heisei" { return Ok(year - 1 + HEISEI_START.year); - } else if era == "showa" { + } else if era == b"showa" { return Ok(year - 1 + SHOWA_START.year); - } else if era == "taisho" { + } else if era == b"taisho" { return Ok(year - 1 + TAISHO_START.year); - } else if era == "meiji" { + } else if era == b"meiji" { return Ok(year - 1 + MEIJI_START.year); } let data = &self.eras.get().dates_to_eras; // Try to avoid linear search by binary searching for the year suffix - if let Some(start_year) = era.split('-').nth(1).and_then(|y| y.parse::().ok()) { + if let Some(start_year) = era + .split(|x| *x == b'-') + .nth(1) + .and_then(|y| core::str::from_utf8(y).ok()?.parse::().ok()) + { if let Ok(index) = data.binary_search_by(|(d, _)| d.year.cmp(&start_year)) { // There is a slight chance we hit the case where there are two eras in the same year // There are a couple of rare cases of this, but it's not worth writing a range-based binary search // to catch them since this is an optimization #[expect(clippy::unwrap_used)] // binary search - if data.get(index).unwrap().1 == era { + if data.get(index).unwrap().1.as_bytes() == era { return Ok(start_year + year - 1); } } @@ -210,7 +218,7 @@ impl GregorianYears for &'_ Japanese { let era_start = data .iter() .rev() - .find_map(|(s, e)| (e == era).then_some(s)) + .find_map(|(s, e)| (e.as_bytes() == era).then_some(s)) .ok_or(UnknownEraError)?; Ok(era_start.year + year - 1) } @@ -341,7 +349,7 @@ impl Date { ) -> Result, DateError> { let extended = japanese_calendar .as_calendar() - .extended_from_era_year(Some(era), year)?; + .extended_from_era_year(Some(era.as_bytes()), year)?; Ok(Date::from_raw( JapaneseDateInner(ArithmeticDate::new_gregorian::<&Japanese>( extended, month, day, @@ -392,8 +400,8 @@ impl Date { day: u8, japanext_calendar: A, ) -> Result, DateError> { - let extended = - (&japanext_calendar.as_calendar().0).extended_from_era_year(Some(era), year)?; + let extended = (&japanext_calendar.as_calendar().0) + .extended_from_era_year(Some(era.as_bytes()), year)?; Ok(Date::from_raw( JapaneseExtendedDateInner(ArithmeticDate::new_gregorian::<&Japanese>( extended, month, day, diff --git a/components/calendar/src/cal/julian.rs b/components/calendar/src/cal/julian.rs index 74e32ae5909..c4a3ef1b5ab 100644 --- a/components/calendar/src/cal/julian.rs +++ b/components/calendar/src/cal/julian.rs @@ -107,12 +107,12 @@ impl DateFieldsResolver for Julian { #[inline] fn year_info_from_era( &self, - era: &str, + era: &[u8], era_year: i32, ) -> Result { match era { - "ad" | "ce" => Ok(era_year), - "bc" | "bce" => Ok(1 - era_year), + b"ad" | b"ce" => Ok(era_year), + b"bc" | b"bce" => Ok(1 - era_year), _ => Err(UnknownEraError), } } diff --git a/components/calendar/src/cal/persian.rs b/components/calendar/src/cal/persian.rs index ac9b2ec2193..2fa8fd8707d 100644 --- a/components/calendar/src/cal/persian.rs +++ b/components/calendar/src/cal/persian.rs @@ -81,11 +81,11 @@ impl DateFieldsResolver for Persian { #[inline] fn year_info_from_era( &self, - era: &str, + era: &[u8], era_year: i32, ) -> Result { match era { - "ap" | "sh" | "hs" => Ok(era_year), + b"ap" | b"sh" | b"hs" => Ok(era_year), _ => Err(UnknownEraError), } } diff --git a/components/calendar/src/cal/roc.rs b/components/calendar/src/cal/roc.rs index a85eab1954c..e1a6795f0ed 100644 --- a/components/calendar/src/cal/roc.rs +++ b/components/calendar/src/cal/roc.rs @@ -41,11 +41,15 @@ pub(crate) struct RocEra; impl GregorianYears for RocEra { const EXTENDED_YEAR_OFFSET: i32 = 1911; - fn extended_from_era_year(&self, era: Option<&str>, year: i32) -> Result { + fn extended_from_era_year( + &self, + era: Option<&[u8]>, + year: i32, + ) -> Result { match era { None => Ok(year), - Some("roc") => Ok(year), - Some("broc") => Ok(1 - year), + Some(b"roc") => Ok(year), + Some(b"broc") => Ok(1 - year), Some(_) => Err(UnknownEraError), } } diff --git a/components/calendar/src/calendar.rs b/components/calendar/src/calendar.rs index f13e72884f8..527a18833a7 100644 --- a/components/calendar/src/calendar.rs +++ b/components/calendar/src/calendar.rs @@ -49,7 +49,7 @@ pub trait Calendar: crate::cal::scaffold::UnstableSealed { ) -> Result { let mut fields = types::DateFields::default(); if era.is_some() { - fields.era = era; + fields.era = era.map(|e| e.as_bytes()); fields.era_year = Some(year); } else { fields.extended_year = Some(year); diff --git a/components/calendar/src/calendar_arithmetic.rs b/components/calendar/src/calendar_arithmetic.rs index 84ced09ac8b..1b833346952 100644 --- a/components/calendar/src/calendar_arithmetic.rs +++ b/components/calendar/src/calendar_arithmetic.rs @@ -152,7 +152,7 @@ pub(crate) trait DateFieldsResolver: Calendar { /// this should always return an Err result. fn year_info_from_era( &self, - era: &str, + era: &[u8], era_year: i32, ) -> Result; diff --git a/components/calendar/src/error.rs b/components/calendar/src/error.rs index f51e6c8f95d..4a84ba4ea7f 100644 --- a/components/calendar/src/error.rs +++ b/components/calendar/src/error.rs @@ -170,7 +170,7 @@ pub enum DateFromFieldsError { /// use icu_calendar::types::DateFields; /// /// let mut fields = DateFields::default(); - /// fields.era = Some("reiwa"); + /// fields.era = Some(b"reiwa"); /// fields.era_year = Some(6); /// fields.ordinal_month = Some(1); /// fields.day = Some(1); @@ -277,7 +277,7 @@ pub enum DateFromFieldsError { /// .expect_err("era year still needs an era"); /// assert!(matches!(err, DateFromFieldsError::NotEnoughFields)); /// - /// fields.era = Some("am"); + /// fields.era = Some(b"am"); /// /// let date = Date::try_from_fields( /// fields, diff --git a/components/calendar/src/options.rs b/components/calendar/src/options.rs index 9ab1c17e1ad..0122a5f5e83 100644 --- a/components/calendar/src/options.rs +++ b/components/calendar/src/options.rs @@ -459,7 +459,7 @@ mod tests { // A map from field names to a function that sets that field let mut field_fns = BTreeMap::<&str, &dyn Fn(&mut DateFields)>::new(); - field_fns.insert("era", &|fields| fields.era = Some("ad")); + field_fns.insert("era", &|fields| fields.era = Some(b"ad")); field_fns.insert("era_year", &|fields| fields.era_year = Some(2000)); field_fns.insert("extended_year", &|fields| fields.extended_year = Some(2000)); field_fns.insert("month_code", &|fields| { diff --git a/components/calendar/src/tests/not_enough_fields.rs b/components/calendar/src/tests/not_enough_fields.rs index f8df5e74a79..b53a599485a 100644 --- a/components/calendar/src/tests/not_enough_fields.rs +++ b/components/calendar/src/tests/not_enough_fields.rs @@ -34,7 +34,7 @@ fn test_from_fields_not_enough_fields() { assert_eq!( Date::try_from_fields( DateFields { - era: Some("hebrew"), + era: Some(b"hebrew"), era_year: big_i32, extended_year: None, ordinal_month: None, @@ -50,7 +50,7 @@ fn test_from_fields_not_enough_fields() { assert_eq!( Date::try_from_fields( DateFields { - era: Some("hebrew"), + era: Some(b"hebrew"), era_year: small_i32, extended_year: None, ordinal_month: None, @@ -84,7 +84,7 @@ fn test_from_fields_not_enough_fields() { assert_eq!( Date::try_from_fields( DateFields { - era: Some("hebrew"), + era: Some(b"hebrew"), era_year: None, extended_year: None, ordinal_month: big_u8, @@ -100,7 +100,7 @@ fn test_from_fields_not_enough_fields() { assert_eq!( Date::try_from_fields( DateFields { - era: Some("hebrew"), + era: Some(b"hebrew"), era_year: None, extended_year: None, ordinal_month: small_u8, @@ -202,7 +202,7 @@ fn test_from_fields_not_enough_fields() { assert_eq!( Date::try_from_fields( DateFields { - era: Some("hebrew"), + era: Some(b"hebrew"), era_year: big_i32, extended_year: None, ordinal_month: small_u8, @@ -218,7 +218,7 @@ fn test_from_fields_not_enough_fields() { assert_eq!( Date::try_from_fields( DateFields { - era: Some("hebrew"), + era: Some(b"hebrew"), era_year: small_i32, extended_year: None, ordinal_month: big_u8, diff --git a/components/calendar/src/types.rs b/components/calendar/src/types.rs index e79f6d89b20..aa0e003e8b6 100644 --- a/components/calendar/src/types.rs +++ b/components/calendar/src/types.rs @@ -25,7 +25,7 @@ pub struct DateFields<'a> { /// The era code as defined by CLDR. /// /// If set, [`Self::era_year`] must also be set. - pub era: Option<&'a str>, + pub era: Option<&'a [u8]>, /// The numeric year in [`Self::era`]. /// /// If set, [`Self::era`] must also be set. diff --git a/ffi/capi/src/date.rs b/ffi/capi/src/date.rs index d47f3fd88c3..4d757fc702d 100644 --- a/ffi/capi/src/date.rs +++ b/ffi/capi/src/date.rs @@ -494,12 +494,7 @@ impl<'a> TryFrom> for icu_calendar::types::DateFields<'a> { type Error = CalendarDateFromFieldsError; fn try_from(other: ffi::DateFields<'a>) -> Result { let mut fields = Self::default(); - if let Some(era) = other.era.into_option() { - let Ok(s) = core::str::from_utf8(era) else { - return Err(CalendarDateFromFieldsError::UnknownEra); - }; - fields.era = Some(s); - } + fields.era = other.era.into_option(); fields.era_year = other.era_year.into(); fields.extended_year = other.extended_year.into(); if let Some(month_code) = other.month_code.into_option() {