1use crate::cal::abstract_gregorian::{impl_with_abstract_gregorian, GregorianYears};
6use crate::cal::gregorian::CeBce;
7use crate::calendar_arithmetic::ArithmeticDate;
8use crate::error::{DateError, UnknownEraError};
9use crate::provider::{CalendarJapaneseExtendedV1, CalendarJapaneseModernV1, EraStartDate};
10use crate::{types, AsCalendar, Date};
11use icu_provider::prelude::*;
12use tinystr::tinystr;
13
14#[derive(Clone, Debug, Default)]
41pub struct Japanese {
42 eras: DataPayload<CalendarJapaneseModernV1>,
43}
44
45#[derive(Clone, Debug, Default)]
74pub struct JapaneseExtended(Japanese);
75
76impl Japanese {
77 #[cfg(feature = "compiled_data")]
83 pub const fn new() -> Self {
84 Self {
85 eras: DataPayload::from_static_ref(
86 crate::provider::Baked::SINGLETON_CALENDAR_JAPANESE_MODERN_V1,
87 ),
88 }
89 }
90
91 icu_provider::gen_buffer_data_constructors!(() -> error: DataError,
92 functions: [
93 new: skip,
94 try_new_with_buffer_provider,
95 try_new_unstable,
96 Self,
97 ]);
98
99 #[doc = icu_provider::gen_buffer_unstable_docs!(UNSTABLE, Self::new)]
100 pub fn try_new_unstable<D: DataProvider<CalendarJapaneseModernV1> + ?Sized>(
101 provider: &D,
102 ) -> Result<Self, DataError> {
103 Ok(Self {
104 eras: provider.load(Default::default())?.payload,
105 })
106 }
107}
108
109impl JapaneseExtended {
110 #[cfg(feature = "compiled_data")]
116 pub const fn new() -> Self {
117 Self(Japanese {
118 eras: DataPayload::from_static_ref(
119 crate::provider::Baked::SINGLETON_CALENDAR_JAPANESE_EXTENDED_V1,
120 ),
121 })
122 }
123
124 icu_provider::gen_buffer_data_constructors!(() -> error: DataError,
125 functions: [
126 new: skip,
127 try_new_with_buffer_provider,
128 try_new_unstable,
129 Self,
130 ]);
131
132 #[doc = icu_provider::gen_buffer_unstable_docs!(UNSTABLE, Self::new)]
133 pub fn try_new_unstable<D: DataProvider<CalendarJapaneseExtendedV1> + ?Sized>(
134 provider: &D,
135 ) -> Result<Self, DataError> {
136 Ok(Self(Japanese {
137 eras: provider.load(Default::default())?.payload.cast(),
138 }))
139 }
140}
141
142const MEIJI_START: EraStartDate = EraStartDate {
143 year: 1868,
144 month: 10,
145 day: 23,
146};
147const TAISHO_START: EraStartDate = EraStartDate {
148 year: 1912,
149 month: 7,
150 day: 30,
151};
152const SHOWA_START: EraStartDate = EraStartDate {
153 year: 1926,
154 month: 12,
155 day: 25,
156};
157const HEISEI_START: EraStartDate = EraStartDate {
158 year: 1989,
159 month: 1,
160 day: 8,
161};
162const REIWA_START: EraStartDate = EraStartDate {
163 year: 2019,
164 month: 5,
165 day: 1,
166};
167
168impl GregorianYears for &'_ Japanese {
169 fn extended_from_era_year(
170 &self,
171 era: Option<&[u8]>,
172 year: i32,
173 ) -> Result<i32, UnknownEraError> {
174 if let Ok(g) = CeBce.extended_from_era_year(era, year) {
175 return Ok(g);
176 }
177 let Some(era) = era else {
178 return Err(UnknownEraError);
180 };
181
182 if era == b"reiwa" {
184 return Ok(year - 1 + REIWA_START.year);
185 } else if era == b"heisei" {
186 return Ok(year - 1 + HEISEI_START.year);
187 } else if era == b"showa" {
188 return Ok(year - 1 + SHOWA_START.year);
189 } else if era == b"taisho" {
190 return Ok(year - 1 + TAISHO_START.year);
191 } else if era == b"meiji" {
192 return Ok(year - 1 + MEIJI_START.year);
193 }
194
195 let data = &self.eras.get().dates_to_eras;
196
197 if let Some(start_year) = era
199 .split(|x| *x == b'-')
200 .nth(1)
201 .and_then(|y| core::str::from_utf8(y).ok()?.parse::<i32>().ok())
202 {
203 if let Ok(index) = data.binary_search_by(|(d, _)| d.year.cmp(&start_year)) {
204 #[expect(clippy::unwrap_used)] if data.get(index).unwrap().1.as_bytes() == era {
209 return Ok(start_year + year - 1);
210 }
211 }
212 }
213
214 let era_start = data
216 .iter()
217 .rev()
218 .find_map(|(s, e)| (e.as_bytes() == era).then_some(s))
219 .ok_or(UnknownEraError)?;
220 Ok(era_start.year + year - 1)
221 }
222
223 fn era_year_from_extended(&self, year: i32, month: u8, day: u8) -> types::EraYear {
224 let date: EraStartDate = EraStartDate { year, month, day };
225
226 let (start, era) = if date >= MEIJI_START
227 && self
228 .eras
229 .get()
230 .dates_to_eras
231 .last()
232 .is_some_and(|(_, e)| e == tinystr!(16, "reiwa"))
233 {
234 if date >= REIWA_START {
238 (REIWA_START, tinystr!(16, "reiwa"))
239 } else if date >= HEISEI_START {
240 (HEISEI_START, tinystr!(16, "heisei"))
241 } else if date >= SHOWA_START {
242 (SHOWA_START, tinystr!(16, "showa"))
243 } else if date >= TAISHO_START {
244 (TAISHO_START, tinystr!(16, "taisho"))
245 } else {
246 (MEIJI_START, tinystr!(16, "meiji"))
247 }
248 } else {
249 let data = &self.eras.get().dates_to_eras;
250 #[allow(clippy::unwrap_used)] match data.binary_search_by(|(d, _)| d.cmp(&date)) {
252 Err(0) => {
253 return types::EraYear {
254 era_index: None,
256 ..CeBce.era_year_from_extended(year, month, day)
257 };
258 }
259 Ok(index) => data.get(index).unwrap(),
260 Err(index) => data.get(index - 1).unwrap(),
261 }
262 };
263
264 types::EraYear {
265 era,
266 era_index: None,
267 year: year - start.year + 1,
268 extended_year: year,
269 ambiguity: types::YearAmbiguity::CenturyRequired,
270 }
271 }
272
273 fn debug_name(&self) -> &'static str {
274 if self.eras.get().dates_to_eras.len() > 10 {
275 "Japanese (historical era data)"
276 } else {
277 "Japanese"
278 }
279 }
280
281 fn calendar_algorithm(&self) -> Option<crate::preferences::CalendarAlgorithm> {
282 if self.eras.get().dates_to_eras.len() > 10 {
283 None
284 } else {
285 Some(crate::preferences::CalendarAlgorithm::Japanese)
286 }
287 }
288}
289
290impl_with_abstract_gregorian!(Japanese, JapaneseDateInner, Japanese, this, this);
291
292impl_with_abstract_gregorian!(
293 JapaneseExtended,
294 JapaneseExtendedDateInner,
295 Japanese,
296 this,
297 &this.0
298);
299
300impl Date<Japanese> {
301 pub fn try_new_japanese_with_calendar<A: AsCalendar<Calendar = Japanese>>(
341 era: &str,
342 year: i32,
343 month: u8,
344 day: u8,
345 japanese_calendar: A,
346 ) -> Result<Date<A>, DateError> {
347 let extended = japanese_calendar
348 .as_calendar()
349 .extended_from_era_year(Some(era.as_bytes()), year)?;
350 Ok(Date::from_raw(
351 JapaneseDateInner(ArithmeticDate::new_gregorian::<&Japanese>(
352 extended, month, day,
353 )?),
354 japanese_calendar,
355 ))
356 }
357}
358
359impl Date<JapaneseExtended> {
360 pub fn try_new_japanese_extended_with_calendar<A: AsCalendar<Calendar = JapaneseExtended>>(
394 era: &str,
395 year: i32,
396 month: u8,
397 day: u8,
398 japanext_calendar: A,
399 ) -> Result<Date<A>, DateError> {
400 let extended = (&japanext_calendar.as_calendar().0)
401 .extended_from_era_year(Some(era.as_bytes()), year)?;
402 Ok(Date::from_raw(
403 JapaneseExtendedDateInner(ArithmeticDate::new_gregorian::<&Japanese>(
404 extended, month, day,
405 )?),
406 japanext_calendar,
407 ))
408 }
409}
410
411#[cfg(test)]
412mod tests {
413 use super::*;
414 use crate::Ref;
415
416 fn single_test_roundtrip(calendar: Ref<Japanese>, era: &str, year: i32, month: u8, day: u8) {
417 let date = Date::try_new_japanese_with_calendar(era, year, month, day, calendar)
418 .unwrap_or_else(|e| {
419 panic!("Failed to construct date with {era:?}, {year}, {month}, {day}: {e:?}")
420 });
421 let iso = date.to_iso();
422 let reconstructed = Date::new_from_iso(iso, calendar);
423 assert_eq!(
424 date, reconstructed,
425 "Failed to roundtrip with {era:?}, {year}, {month}, {day}"
426 );
427
428 assert_eq!(reconstructed.era_year().era, era);
430 assert_eq!(reconstructed.era_year().year, year);
431 }
432
433 fn single_test_roundtrip_ext(
434 calendar: Ref<JapaneseExtended>,
435 era: &str,
436 year: i32,
437 month: u8,
438 day: u8,
439 ) {
440 let date = Date::try_new_japanese_extended_with_calendar(era, year, month, day, calendar)
441 .unwrap_or_else(|e| {
442 panic!("Failed to construct date with {era:?}, {year}, {month}, {day}: {e:?}")
443 });
444 let iso = date.to_iso();
445 let reconstructed = Date::new_from_iso(iso, calendar);
446 assert_eq!(
447 date, reconstructed,
448 "Failed to roundtrip with {era:?}, {year}, {month}, {day}"
449 )
450 }
451
452 fn single_test_era_range_roundtrip(
454 calendar: Ref<Japanese>,
455 era: &str,
456 year: i32,
457 month: u8,
458 day: u8,
459 era2: &str,
460 year2: i32,
461 ) {
462 let expected = Date::try_new_japanese_with_calendar(era2, year2, month, day, calendar)
463 .unwrap_or_else(|e| {
464 panic!(
465 "Failed to construct expectation date with {era2:?}, {year2}, {month}, {day}: {e:?}"
466 )
467 });
468
469 let date = Date::try_new_japanese_with_calendar(era, year, month, day, calendar)
470 .unwrap_or_else(|e| {
471 panic!("Failed to construct date with {era:?}, {year}, {month}, {day}: {e:?}")
472 });
473 let iso = date.to_iso();
474 let reconstructed = Date::new_from_iso(iso, calendar);
475 assert_eq!(
476 expected, reconstructed,
477 "Failed to roundtrip with {era:?}, {year}, {month}, {day} == {era2:?}, {year}"
478 )
479 }
480 fn single_test_era_range_roundtrip_ext(
481 calendar: Ref<JapaneseExtended>,
482 era: &str,
483 year: i32,
484 month: u8,
485 day: u8,
486 era2: &str,
487 year2: i32,
488 ) {
489 let expected = Date::try_new_japanese_extended_with_calendar(era2, year2, month, day, calendar)
490 .unwrap_or_else(|e| {
491 panic!(
492 "Failed to construct expectation date with {era2:?}, {year2}, {month}, {day}: {e:?}"
493 )
494 });
495
496 let date = Date::try_new_japanese_extended_with_calendar(era, year, month, day, calendar)
497 .unwrap_or_else(|e| {
498 panic!("Failed to construct date with {era:?}, {year}, {month}, {day}: {e:?}")
499 });
500 let iso = date.to_iso();
501 let reconstructed = Date::new_from_iso(iso, calendar);
502 assert_eq!(
503 expected, reconstructed,
504 "Failed to roundtrip with {era:?}, {year}, {month}, {day} == {era2:?}, {year}"
505 )
506 }
507
508 fn single_test_error(
509 calendar: Ref<Japanese>,
510 era: &str,
511 year: i32,
512 month: u8,
513 day: u8,
514 error: DateError,
515 ) {
516 let date = Date::try_new_japanese_with_calendar(era, year, month, day, calendar);
517 assert_eq!(
518 date,
519 Err(error),
520 "Construction with {era:?}, {year}, {month}, {day} did not return {error:?}"
521 )
522 }
523
524 #[test]
525 fn test_japanese() {
526 let calendar = Japanese::new();
527 let calendar_ext = JapaneseExtended::new();
528 let calendar = Ref(&calendar);
529 let calendar_ext = Ref(&calendar_ext);
530
531 single_test_roundtrip(calendar, "heisei", 12, 3, 1);
532 single_test_roundtrip(calendar, "taisho", 3, 3, 1);
533 single_test_era_range_roundtrip(calendar, "heisei", 1, 1, 1, "showa", 64);
535
536 single_test_roundtrip_ext(calendar_ext, "heisei", 12, 3, 1);
537 single_test_roundtrip_ext(calendar_ext, "taisho", 3, 3, 1);
538 single_test_era_range_roundtrip_ext(calendar_ext, "heisei", 1, 1, 1, "showa", 64);
539
540 single_test_roundtrip_ext(calendar_ext, "hakuho-672", 4, 3, 1);
541 single_test_error(calendar, "hakuho-672", 4, 3, 1, DateError::UnknownEra);
542
543 single_test_roundtrip(calendar, "bce", 100, 3, 1);
545 single_test_roundtrip(calendar, "bce", 1, 3, 1);
546 single_test_roundtrip(calendar, "ce", 1, 3, 1);
547 single_test_roundtrip(calendar, "ce", 100, 3, 1);
548 single_test_roundtrip_ext(calendar_ext, "ce", 100, 3, 1);
549 single_test_roundtrip(calendar, "ce", 1000, 3, 1);
550 single_test_era_range_roundtrip(calendar, "ce", 0, 3, 1, "bce", 1);
551 single_test_era_range_roundtrip(calendar, "bce", -1, 3, 1, "ce", 2);
552
553 single_test_era_range_roundtrip_ext(calendar_ext, "ce", 1000, 3, 1, "choho-999", 2);
556 single_test_era_range_roundtrip_ext(calendar_ext, "ce", 749, 5, 10, "tenpyokampo-749", 1);
557 single_test_era_range_roundtrip_ext(calendar_ext, "bce", 10, 3, 1, "bce", 10);
558 single_test_era_range_roundtrip_ext(calendar_ext, "ce", -1, 3, 1, "bce", 2);
559
560 single_test_roundtrip_ext(calendar_ext, "tenpyokampo-749", 1, 4, 20);
563 single_test_roundtrip_ext(calendar_ext, "tenpyokampo-749", 1, 4, 14);
564 single_test_roundtrip_ext(calendar_ext, "tenpyokampo-749", 1, 7, 1);
565 single_test_era_range_roundtrip_ext(
566 calendar_ext,
567 "tenpyokampo-749",
568 1,
569 7,
570 5,
571 "tenpyoshoho-749",
572 1,
573 );
574 single_test_era_range_roundtrip_ext(
575 calendar_ext,
576 "tenpyokampo-749",
577 1,
578 4,
579 13,
580 "tenpyoshoho-749",
581 1,
582 );
583 }
584}