Skip to content

Commit 38b9b79

Browse files
Use YearNames::FixedEras for Japanese (#7700)
1 parent a572320 commit 38b9b79

File tree

8 files changed

+485
-438
lines changed

8 files changed

+485
-438
lines changed

components/datetime/src/pattern/names.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3871,6 +3871,7 @@ impl RawDateTimeNamesBorrowed<'_> {
38713871
.ok_or(GetNameForEraError::NotLoaded)?;
38723872

38733873
match year_names {
3874+
#[cfg(feature = "serde")]
38743875
YearNames::VariableEras(era_names) => {
38753876
get_year_name_from_map(era_names, era_year.era.as_str().into())
38763877
.ok_or(GetNameForEraError::InvalidEraCode)

components/datetime/src/provider/names.rs

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@ use crate::size_test_macro::size_test;
88
use alloc::borrow::Cow;
99
use icu_pattern::SinglePlaceholderPattern;
1010
use icu_provider::prelude::*;
11+
#[cfg(feature = "serde")]
1112
use potential_utf::PotentialUtf8;
12-
use zerovec::{ule::tuplevar::Tuple2VarULE, VarZeroCow, VarZeroSlice, VarZeroVec};
13+
use zerovec::VarZeroVec;
14+
#[cfg(feature = "serde")]
15+
use zerovec::{ule::tuplevar::Tuple2VarULE, VarZeroCow, VarZeroSlice};
1316

1417
icu_provider::data_marker!(
1518
/// `DatetimeNamesYearBuddhistV1`
@@ -231,7 +234,7 @@ size_test!(YearNames, year_names_v1_size, 32);
231234
/// to be stable, their Rust representation might not be. Use with caution.
232235
/// </div>
233236
#[derive(Debug, PartialEq, Clone, yoke::Yokeable, zerofrom::ZeroFrom)]
234-
#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
237+
#[cfg_attr(feature = "datagen", derive(databake::Bake))]
235238
#[cfg_attr(feature = "datagen", databake(path = icu_datetime::provider::names))]
236239
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
237240
#[yoke(prove_covariance_manually)]
@@ -242,19 +245,68 @@ pub enum YearNames<'data> {
242245
FixedEras(#[cfg_attr(feature = "serde", serde(borrow))] VarZeroVec<'data, str>),
243246
/// This calendar has a variable set of eras with numeric years, this stores the era names mapped from
244247
/// era code to the name.
248+
#[cfg(feature = "serde")]
245249
VariableEras(#[cfg_attr(feature = "serde", serde(borrow))] YearNamesMap<'data>),
246250
/// This calendar is cyclic (Chinese, Dangi), so it uses cyclic year names without any eras
247251
Cyclic(#[cfg_attr(feature = "serde", serde(borrow))] VarZeroVec<'data, str>),
248252
}
249253

254+
#[cfg(feature = "serde")]
255+
impl serde::Serialize for YearNames<'_> {
256+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
257+
where
258+
S: serde::Serializer,
259+
{
260+
use alloc::vec::Vec;
261+
262+
#[derive(serde::Serialize)]
263+
enum Raw<'a> {
264+
FixedEras(&'a VarZeroVec<'a, str>),
265+
VariableEras(&'a YearNamesMap<'a>),
266+
Cyclic(&'a VarZeroVec<'a, str>),
267+
}
268+
269+
let x: YearNamesMap;
270+
271+
match self {
272+
// Japanese eras are now generated as `FixedEras`, but we want to keep serializing
273+
// them as VariableEras. It's the only calendar with 7 eras.
274+
Self::FixedEras(e) if e.len() == 7 => {
275+
let mut kvs = [
276+
PotentialUtf8::from_str("bce"),
277+
PotentialUtf8::from_str("ce"),
278+
PotentialUtf8::from_str("meiji"),
279+
PotentialUtf8::from_str("taisho"),
280+
PotentialUtf8::from_str("showa"),
281+
PotentialUtf8::from_str("heisei"),
282+
PotentialUtf8::from_str("reiwa"),
283+
]
284+
.into_iter()
285+
.zip(e.iter())
286+
.collect::<Vec<_>>();
287+
kvs.sort_unstable();
288+
let (ks, vs) = kvs.into_iter().unzip::<_, _, Vec<_>, Vec<_>>();
289+
x = VarZeroCow::from_encodeable(&(ks, vs));
290+
Raw::VariableEras(&x)
291+
}
292+
Self::FixedEras(e) => Raw::FixedEras(e),
293+
Self::VariableEras(e) => Raw::VariableEras(e),
294+
Self::Cyclic(c) => Raw::Cyclic(c),
295+
}
296+
.serialize(serializer)
297+
}
298+
}
299+
250300
icu_provider::data_struct!(
251301
YearNames<'_>,
252302
#[cfg(feature = "datagen")]
253303
);
254304

305+
#[cfg(feature = "serde")]
255306
type YearNamesMap<'data> =
256307
VarZeroCow<'data, Tuple2VarULE<VarZeroSlice<PotentialUtf8>, VarZeroSlice<str>>>;
257308

309+
#[cfg(feature = "serde")]
258310
pub(crate) fn get_year_name_from_map<'a>(
259311
map: &'a YearNamesMap<'_>,
260312
year: &PotentialUtf8,

provider/data/datetime/data/datetime_names_year_japanese_v1.rs.data

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

provider/data/datetime/fingerprints.csv

Lines changed: 396 additions & 396 deletions
Large diffs are not rendered by default.

provider/source/src/calendar/eras.rs

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,16 @@ impl SourceDataProvider {
5959
.map(|(key, data)| {
6060
let mut data = data.clone();
6161
let date = data.start.or(data.end).unwrap();
62-
let era_year = Date::try_new_gregorian(date.year, date.month, date.day)
63-
.unwrap()
64-
.to_calendar(icu::calendar::Ref(&any_cal))
65-
.year()
66-
.era()
67-
.unwrap();
62+
let era_year = Date::try_new_gregorian(
63+
date.year + data.offset,
64+
date.month,
65+
date.day,
66+
)
67+
.unwrap()
68+
.to_calendar(icu::calendar::Ref(&any_cal))
69+
.year()
70+
.era()
71+
.unwrap();
6872
if era_year.era != data.code {
6973
log::warn!("mismatched era code {era_year:?} - {data:?}");
7074
}
@@ -90,6 +94,13 @@ fn process_era_dates_map(
9094
.unwrap()
9195
.eras
9296
.retain(|_, era| !era.code.is_empty());
97+
// Meiji needs an offset of 5
98+
data.get_mut(DatagenCalendar::Japanese.cldr_name())
99+
.unwrap()
100+
.eras
101+
.get_mut("232")
102+
.unwrap()
103+
.offset = 5;
93104
data
94105
}
95106

@@ -184,13 +195,8 @@ fn test_calendar_eras() {
184195
for (idx, (_, era)) in data.eras.iter().enumerate() {
185196
let (in_era_iso, not_in_era_iso) = match (era.start, era.end) {
186197
(Some(start), None) => {
187-
let year = if era.code == "meiji" {
188-
// Meiji starts roundtripping only after Meiji 6
189-
start.year + 5
190-
} else {
191-
start.year
192-
};
193-
let start = Date::try_new_iso(year, start.month, start.day).unwrap();
198+
let start =
199+
Date::try_new_iso(start.year + era.offset, start.month, start.day).unwrap();
194200
(start, Date::from_rata_die(start.to_rata_die() - 1, Iso))
195201
}
196202
(None, Some(end)) => {
@@ -236,15 +242,10 @@ fn test_calendar_eras() {
236242
assert_eq!(era_year.era, era.code);
237243
}
238244

239-
// Check that the start/end date uses year 1, and minimal/maximal month/day
240-
let expected_year = if era.code == "meiji" {
241-
// We started at Meiji 6 for Meiji only, above
242-
6
243-
} else {
244-
1
245-
};
245+
// Check that the start/end date uses year 1 + offset, and minimal/maximal month/day
246246
assert_eq!(
247-
era_year.year, expected_year,
247+
era_year.year,
248+
1 + era.offset,
248249
"Didn't get correct year for {in_era:?}"
249250
);
250251

provider/source/src/cldr_serde/eras.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ pub(crate) struct EraData {
4242
pub(crate) code: String,
4343
#[serde(rename = "_aliases", default)]
4444
pub(crate) aliases: String,
45+
/// The offset from the arithmetic start of the era to when it was started
46+
/// to be used. CLDR doesn't track this, we manually set this for Meiji.
47+
#[serde(default)]
48+
pub(crate) offset: i32,
4549
/// `EraYear::era_index`
4650
#[serde(skip)]
4751
pub(crate) icu4x_era_index: Option<u8>,

provider/source/src/datetime/names.rs

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,8 @@ use icu::datetime::provider::semantic_skeletons::marker_attrs::{
1616
use icu::datetime::provider::semantic_skeletons::{DatetimePatternsGlueV1, GluePattern};
1717
use icu_pattern::SinglePlaceholderPattern;
1818
use icu_provider::prelude::*;
19-
use potential_utf::PotentialUtf8;
2019
use std::borrow::Cow;
2120
use std::collections::{BTreeMap, HashSet};
22-
use zerovec::VarZeroCow;
2321

2422
/// Most keys don't have short symbols (except weekdays)
2523
///
@@ -276,21 +274,11 @@ fn years_convert(
276274
.max()
277275
.unwrap_or_default();
278276

279-
if calendar == DatagenCalendar::Japanese {
280-
// The Japanese calendar didn't produce era indices until 2.2.0. To keep
281-
// new-data-old-code working, we need to produce `YearNames::VariableEras`.
282-
let kv = eras
283-
.iter()
284-
.map(|(&(k, _), &v)| (PotentialUtf8::from_str(k), v))
285-
.unzip::<_, _, Vec<_>, Vec<_>>();
286-
Ok(YearNames::VariableEras(VarZeroCow::from_encodeable(&kv)))
287-
} else {
288-
let mut out_eras = vec![""; max_icu4x_era_index];
289-
for ((_, idx), era) in eras {
290-
out_eras[idx] = era;
291-
}
292-
Ok(YearNames::FixedEras((&out_eras).into()))
277+
let mut out_eras = vec![""; max_icu4x_era_index];
278+
for ((_, idx), era) in eras {
279+
out_eras[idx] = era;
293280
}
281+
Ok(YearNames::FixedEras((&out_eras).into()))
294282
} else if let Some(years) = data
295283
.cyclic_name_sets
296284
.as_ref()

provider/source/src/tests/make_testdata.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,9 +226,10 @@ impl DataExporter for ZeroCopyCheckExporter {
226226
fn close(&mut self) -> Result<ExporterCloseMetadata, DataError> {
227227
let rountrip_errors = self.rountrip_errors.get_mut().expect("poison");
228228
// These serialize to a different variant for stability
229-
rountrip_errors.remove(&icu::datetime::provider::names::DatetimeNamesMonthDangiV1::INFO);
230229
rountrip_errors.remove(&icu::datetime::provider::names::DatetimeNamesMonthChineseV1::INFO);
230+
rountrip_errors.remove(&icu::datetime::provider::names::DatetimeNamesMonthDangiV1::INFO);
231231
rountrip_errors.remove(&icu::datetime::provider::names::DatetimeNamesMonthHebrewV1::INFO);
232+
rountrip_errors.remove(&icu::datetime::provider::names::DatetimeNamesYearJapaneseV1::INFO);
232233
assert_eq!(rountrip_errors, &mut BTreeMap::default());
233234

234235
let violations = self

0 commit comments

Comments
 (0)