Coverage Report

Created: 2026-11-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/icu_calendar-1.5.2/src/japanese.rs
Line
Count
Source
1
// This file is part of ICU4X. For terms of use, please see the file
2
// called LICENSE at the top level of the ICU4X source tree
3
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4
5
//! This module contains types and implementations for the Japanese calendar.
6
//!
7
//! ```rust
8
//! use icu::calendar::japanese::Japanese;
9
//! use icu::calendar::{types::Era, Date, DateTime};
10
//! use tinystr::tinystr;
11
//!
12
//! let japanese_calendar = Japanese::new();
13
//!
14
//! // `Date` type
15
//! let date_iso = Date::try_new_iso_date(1970, 1, 2)
16
//!     .expect("Failed to initialize ISO Date instance.");
17
//! let date_japanese = Date::new_from_iso(date_iso, japanese_calendar.clone());
18
//!
19
//! // `DateTime` type
20
//! let datetime_iso = DateTime::try_new_iso_datetime(1970, 1, 2, 13, 1, 0)
21
//!     .expect("Failed to initialize ISO DateTime instance.");
22
//! let datetime_japanese =
23
//!     DateTime::new_from_iso(datetime_iso, japanese_calendar.clone());
24
//!
25
//! // `Date` checks
26
//! assert_eq!(date_japanese.year().number, 45);
27
//! assert_eq!(date_japanese.month().ordinal, 1);
28
//! assert_eq!(date_japanese.day_of_month().0, 2);
29
//! assert_eq!(date_japanese.year().era, Era(tinystr!(16, "showa")));
30
//!
31
//! // `DateTime` type
32
//! assert_eq!(datetime_japanese.date.year().number, 45);
33
//! assert_eq!(datetime_japanese.date.month().ordinal, 1);
34
//! assert_eq!(datetime_japanese.date.day_of_month().0, 2);
35
//! assert_eq!(
36
//!     datetime_japanese.date.year().era,
37
//!     Era(tinystr!(16, "showa"))
38
//! );
39
//! assert_eq!(datetime_japanese.time.hour.number(), 13);
40
//! assert_eq!(datetime_japanese.time.minute.number(), 1);
41
//! assert_eq!(datetime_japanese.time.second.number(), 0);
42
//! ```
43
44
use crate::any_calendar::AnyCalendarKind;
45
use crate::iso::{Iso, IsoDateInner};
46
use crate::provider::{EraStartDate, JapaneseErasV1Marker, JapaneseExtendedErasV1Marker};
47
use crate::{
48
    types, AsCalendar, Calendar, CalendarError, Date, DateDuration, DateDurationUnit, DateTime,
49
    Ref, Time,
50
};
51
use icu_provider::prelude::*;
52
use tinystr::{tinystr, TinyStr16};
53
54
/// The [Japanese Calendar] (with modern eras only)
55
///
56
/// The [Japanese calendar] is a solar calendar used in Japan, with twelve months.
57
/// The months and days are identical to that of the Gregorian calendar, however the years are counted
58
/// differently using the Japanese era system.
59
///
60
/// This calendar only contains eras after Meiji, for all historical eras, check out [`JapaneseExtended`].
61
///
62
/// This type can be used with [`Date`] or [`DateTime`] to represent dates in this calendar.
63
///
64
/// [Japanese calendar]: https://en.wikipedia.org/wiki/Japanese_calendar
65
///
66
/// # Era codes
67
///
68
/// This calendar currently supports seven era codes. It supports the five post-Meiji eras
69
/// (`"meiji"`, `"taisho"`, `"showa"`, `"heisei"`, `"reiwa"`), as well as using the Gregorian
70
/// `"bce"` and `"ce"` for dates before the Meiji era.
71
///
72
/// Future eras will also be added to this type when they are decided.
73
///
74
/// These eras are loaded from data, requiring a data provider capable of providing [`JapaneseErasV1Marker`]
75
/// data (`calendar/japanese@1`).
76
///
77
/// # Month codes
78
///
79
/// This calendar supports 12 solar month codes (`"M01" - "M12"`)
80
#[derive(Clone, Debug, Default)]
81
pub struct Japanese {
82
    eras: DataPayload<JapaneseErasV1Marker>,
83
}
84
85
/// The [Japanese Calendar] (with historical eras)
86
///
87
/// The [Japanese calendar] is a solar calendar used in Japan, with twelve months.
88
/// The months and days are identical to that of the Gregorian calendar, however the years are counted
89
/// differently using the Japanese era system.
90
///
91
/// This type can be used with [`Date`] or [`DateTime`] to represent dates in this calendar.
92
///
93
/// [Japanese calendar]: https://en.wikipedia.org/wiki/Japanese_calendar
94
///
95
/// # Era codes
96
///
97
/// This calendar supports a large number of era codes. It supports the five post-Meiji eras
98
/// (`"meiji"`, `"taisho"`, `"showa"`, `"heisei"`, `"reiwa"`). Pre-Meiji eras are represented
99
/// with their names converted to lowercase ascii and followed by their start year. E.g. the "Ten'ō"
100
/// era (781 - 782 CE) has the code `"teno-781"`. The  Gregorian `"bce"` and `"ce"` eras
101
/// are used for dates before the first known era era.
102
///
103
///
104
/// These eras are loaded from data, requiring a data provider capable of providing [`JapaneseExtendedErasV1Marker`]
105
/// data (`calendar/japanext@1`).
106
#[derive(Clone, Debug, Default)]
107
pub struct JapaneseExtended(Japanese);
108
109
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
110
/// The inner date type used for representing [`Date`]s of [`Japanese`]. See [`Date`] and [`Japanese`] for more details.
111
pub struct JapaneseDateInner {
112
    inner: IsoDateInner,
113
    adjusted_year: i32,
114
    era: TinyStr16,
115
}
116
117
impl Japanese {
118
    /// Creates a new [`Japanese`] using only modern eras (post-meiji) from compiled data.
119
    ///
120
    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
121
    ///
122
    /// [📚 Help choosing a constructor](icu_provider::constructors)
123
    #[cfg(feature = "compiled_data")]
124
0
    pub const fn new() -> Self {
125
0
        Self {
126
0
            eras: DataPayload::from_static_ref(
127
0
                crate::provider::Baked::SINGLETON_CALENDAR_JAPANESE_V1,
128
0
            ),
129
0
        }
130
0
    }
131
132
    icu_provider::gen_any_buffer_data_constructors!(locale: skip, options: skip, error: CalendarError,
133
        #[cfg(skip)]
134
        functions: [
135
            new,
136
            try_new_with_any_provider,
137
            try_new_with_buffer_provider,
138
            try_new_unstable,
139
            Self,
140
    ]);
141
142
    #[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::new)]
143
0
    pub fn try_new_unstable<D: DataProvider<JapaneseErasV1Marker> + ?Sized>(
144
0
        provider: &D,
145
0
    ) -> Result<Self, CalendarError> {
146
        Ok(Self {
147
0
            eras: provider.load(Default::default())?.take_payload()?,
148
        })
149
0
    }
Unexecuted instantiation: <icu_calendar::japanese::Japanese>::try_new_unstable::<icu_provider::any::DowncastingAnyProvider<icu_provider_adapters::empty::EmptyDataProvider>>
Unexecuted instantiation: <icu_calendar::japanese::Japanese>::try_new_unstable::<_>
150
151
0
    fn japanese_date_from_codes(
152
0
        &self,
153
0
        era: types::Era,
154
0
        year: i32,
155
0
        month_code: types::MonthCode,
156
0
        day: u8,
157
0
        debug_name: &'static str,
158
0
    ) -> Result<JapaneseDateInner, CalendarError> {
159
0
        let month = month_code.parsed();
160
0
        let month = if let Some((month, false)) = month {
161
0
            month
162
        } else {
163
0
            return Err(CalendarError::UnknownMonthCode(month_code.0, debug_name));
164
        };
165
166
0
        if month > 12 {
167
0
            return Err(CalendarError::UnknownMonthCode(month_code.0, debug_name));
168
0
        }
169
170
0
        self.new_japanese_date_inner(era, year, month, day)
171
0
    }
172
173
    pub(crate) const DEBUG_NAME: &'static str = "Japanese";
174
}
175
176
impl JapaneseExtended {
177
    /// Creates a new [`Japanese`] from using all eras (including pre-meiji) from compiled data.
178
    ///
179
    /// ✨ *Enabled with the `compiled_data` Cargo feature.*
180
    ///
181
    /// [📚 Help choosing a constructor](icu_provider::constructors)
182
    #[cfg(feature = "compiled_data")]
183
0
    pub const fn new() -> Self {
184
0
        Self(Japanese {
185
0
            eras: DataPayload::from_static_ref(
186
0
                crate::provider::Baked::SINGLETON_CALENDAR_JAPANEXT_V1,
187
0
            ),
188
0
        })
189
0
    }
190
191
    icu_provider::gen_any_buffer_data_constructors!(locale: skip, options: skip, error: CalendarError,
192
        #[cfg(skip)]
193
        functions: [
194
            new,
195
            try_new_with_any_provider,
196
            try_new_with_buffer_provider,
197
            try_new_unstable,
198
            Self,
199
    ]);
200
201
    #[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, Self::new)]
202
0
    pub fn try_new_unstable<D: DataProvider<JapaneseExtendedErasV1Marker> + ?Sized>(
203
0
        provider: &D,
204
0
    ) -> Result<Self, CalendarError> {
205
        Ok(Self(Japanese {
206
0
            eras: provider.load(Default::default())?.take_payload()?.cast(),
207
        }))
208
0
    }
Unexecuted instantiation: <icu_calendar::japanese::JapaneseExtended>::try_new_unstable::<icu_provider::any::DowncastingAnyProvider<icu_provider_adapters::empty::EmptyDataProvider>>
Unexecuted instantiation: <icu_calendar::japanese::JapaneseExtended>::try_new_unstable::<_>
209
210
    pub(crate) const DEBUG_NAME: &'static str = "Japanese (historical era data)";
211
}
212
213
impl Calendar for Japanese {
214
    type DateInner = JapaneseDateInner;
215
216
0
    fn date_from_codes(
217
0
        &self,
218
0
        era: types::Era,
219
0
        year: i32,
220
0
        month_code: types::MonthCode,
221
0
        day: u8,
222
0
    ) -> Result<Self::DateInner, CalendarError> {
223
0
        self.japanese_date_from_codes(era, year, month_code, day, self.debug_name())
224
0
    }
225
226
0
    fn date_from_iso(&self, iso: Date<Iso>) -> JapaneseDateInner {
227
0
        let (adjusted_year, era) = self.adjusted_year_for(iso.inner());
228
0
        JapaneseDateInner {
229
0
            inner: *iso.inner(),
230
0
            adjusted_year,
231
0
            era,
232
0
        }
233
0
    }
234
235
0
    fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
236
0
        Date::from_raw(date.inner, Iso)
237
0
    }
238
239
0
    fn months_in_year(&self, date: &Self::DateInner) -> u8 {
240
0
        Iso.months_in_year(&date.inner)
241
0
    }
242
243
0
    fn days_in_year(&self, date: &Self::DateInner) -> u16 {
244
0
        Iso.days_in_year(&date.inner)
245
0
    }
246
247
0
    fn days_in_month(&self, date: &Self::DateInner) -> u8 {
248
0
        Iso.days_in_month(&date.inner)
249
0
    }
250
251
0
    fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
252
0
        Iso.offset_date(&mut date.inner, offset.cast_unit());
253
0
        let (adjusted_year, era) = self.adjusted_year_for(&date.inner);
254
0
        date.adjusted_year = adjusted_year;
255
0
        date.era = era
256
0
    }
257
258
0
    fn until(
259
0
        &self,
260
0
        date1: &Self::DateInner,
261
0
        date2: &Self::DateInner,
262
0
        _calendar2: &Self,
263
0
        largest_unit: DateDurationUnit,
264
0
        smallest_unit: DateDurationUnit,
265
0
    ) -> DateDuration<Self> {
266
0
        Iso.until(
267
0
            &date1.inner,
268
0
            &date2.inner,
269
0
            &Iso,
270
0
            largest_unit,
271
0
            smallest_unit,
272
        )
273
0
        .cast_unit()
274
0
    }
275
276
    /// The calendar-specific year represented by `date`
277
0
    fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
278
0
        types::FormattableYear {
279
0
            era: types::Era(date.era),
280
0
            number: date.adjusted_year,
281
0
            cyclic: None,
282
0
            related_iso: None,
283
0
        }
284
0
    }
285
286
0
    fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
287
0
        Iso.is_in_leap_year(&date.inner)
288
0
    }
289
290
    /// The calendar-specific month represented by `date`
291
0
    fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
292
0
        Iso.month(&date.inner)
293
0
    }
294
295
    /// The calendar-specific day-of-month represented by `date`
296
0
    fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
297
0
        Iso.day_of_month(&date.inner)
298
0
    }
299
300
    /// Information of the day of the year
301
0
    fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
302
0
        let prev_dec_31 = IsoDateInner::dec_31(date.inner.0.year - 1);
303
0
        let next_jan_1 = IsoDateInner::jan_1(date.inner.0.year + 1);
304
305
0
        let prev_dec_31 = self.date_from_iso(Date::from_raw(prev_dec_31, Iso));
306
0
        let next_jan_1 = self.date_from_iso(Date::from_raw(next_jan_1, Iso));
307
0
        types::DayOfYearInfo {
308
0
            day_of_year: Iso::days_in_year_direct(date.inner.0.year),
309
0
            days_in_year: Iso::days_in_year_direct(date.inner.0.year),
310
0
            prev_year: self.year(&prev_dec_31),
311
0
            days_in_prev_year: Iso::days_in_year_direct(prev_dec_31.inner.0.year),
312
0
            next_year: self.year(&next_jan_1),
313
0
        }
314
0
    }
315
316
0
    fn debug_name(&self) -> &'static str {
317
0
        Self::DEBUG_NAME
318
0
    }
319
320
0
    fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
321
0
        Some(AnyCalendarKind::Japanese)
322
0
    }
323
}
324
325
impl Calendar for JapaneseExtended {
326
    type DateInner = JapaneseDateInner;
327
328
0
    fn date_from_codes(
329
0
        &self,
330
0
        era: types::Era,
331
0
        year: i32,
332
0
        month_code: types::MonthCode,
333
0
        day: u8,
334
0
    ) -> Result<Self::DateInner, CalendarError> {
335
0
        self.0
336
0
            .japanese_date_from_codes(era, year, month_code, day, self.debug_name())
337
0
    }
338
339
0
    fn date_from_iso(&self, iso: Date<Iso>) -> JapaneseDateInner {
340
0
        Japanese::date_from_iso(&self.0, iso)
341
0
    }
342
343
0
    fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
344
0
        Japanese::date_to_iso(&self.0, date)
345
0
    }
346
347
0
    fn months_in_year(&self, date: &Self::DateInner) -> u8 {
348
0
        Japanese::months_in_year(&self.0, date)
349
0
    }
350
351
0
    fn days_in_year(&self, date: &Self::DateInner) -> u16 {
352
0
        Japanese::days_in_year(&self.0, date)
353
0
    }
354
355
0
    fn days_in_month(&self, date: &Self::DateInner) -> u8 {
356
0
        Japanese::days_in_month(&self.0, date)
357
0
    }
358
359
0
    fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
360
0
        Japanese::offset_date(&self.0, date, offset.cast_unit())
361
0
    }
362
363
0
    fn until(
364
0
        &self,
365
0
        date1: &Self::DateInner,
366
0
        date2: &Self::DateInner,
367
0
        calendar2: &Self,
368
0
        largest_unit: DateDurationUnit,
369
0
        smallest_unit: DateDurationUnit,
370
0
    ) -> DateDuration<Self> {
371
0
        Japanese::until(
372
0
            &self.0,
373
0
            date1,
374
0
            date2,
375
0
            &calendar2.0,
376
0
            largest_unit,
377
0
            smallest_unit,
378
        )
379
0
        .cast_unit()
380
0
    }
381
382
    /// The calendar-specific year represented by `date`
383
0
    fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
384
0
        Japanese::year(&self.0, date)
385
0
    }
386
387
0
    fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
388
0
        Japanese::is_in_leap_year(&self.0, date)
389
0
    }
390
391
    /// The calendar-specific month represented by `date`
392
0
    fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
393
0
        Japanese::month(&self.0, date)
394
0
    }
395
396
    /// The calendar-specific day-of-month represented by `date`
397
0
    fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
398
0
        Japanese::day_of_month(&self.0, date)
399
0
    }
400
401
    /// Information of the day of the year
402
0
    fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
403
0
        Japanese::day_of_year_info(&self.0, date)
404
0
    }
405
406
0
    fn debug_name(&self) -> &'static str {
407
0
        Self::DEBUG_NAME
408
0
    }
409
410
0
    fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
411
0
        Some(AnyCalendarKind::JapaneseExtended)
412
0
    }
413
}
414
415
impl Date<Japanese> {
416
    /// Construct a new Japanese Date.
417
    ///
418
    /// Years are specified in the era provided, and must be in range for Japanese
419
    /// eras (e.g. dates past April 30 Heisei 31 must be in Reiwa; "Jun 5 Heisei 31" and "Jan 1 Heisei 32"
420
    /// will not be adjusted to being in Reiwa 1 and 2 respectively)
421
    ///
422
    /// However, dates may always be specified in "bce" or "ce" and they will be adjusted as necessary.
423
    ///
424
    /// ```rust
425
    /// use icu::calendar::japanese::Japanese;
426
    /// use icu::calendar::{types, Date, Ref};
427
    /// use tinystr::tinystr;
428
    ///
429
    /// let japanese_calendar = Japanese::new();
430
    /// // for easy sharing
431
    /// let japanese_calendar = Ref(&japanese_calendar);
432
    ///
433
    /// let era = types::Era(tinystr!(16, "heisei"));
434
    ///
435
    /// let date = Date::try_new_japanese_date(era, 14, 1, 2, japanese_calendar)
436
    ///     .expect("Constructing a date should succeed");
437
    ///
438
    /// assert_eq!(date.year().era, era);
439
    /// assert_eq!(date.year().number, 14);
440
    /// assert_eq!(date.month().ordinal, 1);
441
    /// assert_eq!(date.day_of_month().0, 2);
442
    ///
443
    /// // This function will error for eras that are out of bounds:
444
    /// // (Heisei was 32 years long, Heisei 33 is in Reiwa)
445
    /// let oob_date =
446
    ///     Date::try_new_japanese_date(era, 33, 1, 2, japanese_calendar);
447
    /// assert!(oob_date.is_err());
448
    ///
449
    /// // and for unknown eras
450
    /// let fake_era = types::Era(tinystr!(16, "neko")); // 🐱
451
    /// let fake_date =
452
    ///     Date::try_new_japanese_date(fake_era, 10, 1, 2, japanese_calendar);
453
    /// assert!(fake_date.is_err());
454
    /// ```
455
0
    pub fn try_new_japanese_date<A: AsCalendar<Calendar = Japanese>>(
456
0
        era: types::Era,
457
0
        year: i32,
458
0
        month: u8,
459
0
        day: u8,
460
0
        japanese_calendar: A,
461
0
    ) -> Result<Date<A>, CalendarError> {
462
0
        let inner = japanese_calendar
463
0
            .as_calendar()
464
0
            .new_japanese_date_inner(era, year, month, day)?;
465
0
        Ok(Date::from_raw(inner, japanese_calendar))
466
0
    }
467
}
468
469
impl Date<JapaneseExtended> {
470
    /// Construct a new Japanese Date with all eras.
471
    ///
472
    /// Years are specified in the era provided, and must be in range for Japanese
473
    /// eras (e.g. dates past April 30 Heisei 31 must be in Reiwa; "Jun 5 Heisei 31" and "Jan 1 Heisei 32"
474
    /// will not be adjusted to being in Reiwa 1 and 2 respectively)
475
    ///
476
    /// However, dates may always be specified in "bce" or "ce" and they will be adjusted as necessary.
477
    ///
478
    /// ```rust
479
    /// use icu::calendar::japanese::JapaneseExtended;
480
    /// use icu::calendar::{types, Date, Ref};
481
    /// use tinystr::tinystr;
482
    ///
483
    /// let japanext_calendar = JapaneseExtended::new();
484
    /// // for easy sharing
485
    /// let japanext_calendar = Ref(&japanext_calendar);
486
    ///
487
    /// let era = types::Era(tinystr!(16, "kansei-1789"));
488
    ///
489
    /// let date =
490
    ///     Date::try_new_japanese_extended_date(era, 7, 1, 2, japanext_calendar)
491
    ///         .expect("Constructing a date should succeed");
492
    ///
493
    /// assert_eq!(date.year().era, era);
494
    /// assert_eq!(date.year().number, 7);
495
    /// assert_eq!(date.month().ordinal, 1);
496
    /// assert_eq!(date.day_of_month().0, 2);
497
    /// ```
498
0
    pub fn try_new_japanese_extended_date<A: AsCalendar<Calendar = JapaneseExtended>>(
499
0
        era: types::Era,
500
0
        year: i32,
501
0
        month: u8,
502
0
        day: u8,
503
0
        japanext_calendar: A,
504
0
    ) -> Result<Date<A>, CalendarError> {
505
0
        let inner = japanext_calendar
506
0
            .as_calendar()
507
0
            .0
508
0
            .new_japanese_date_inner(era, year, month, day)?;
509
0
        Ok(Date::from_raw(inner, japanext_calendar))
510
0
    }
511
512
    // For testing era fallback in icu_datetime
513
    #[doc(hidden)]
514
0
    pub fn into_japanese_date(self) -> Date<Japanese> {
515
0
        Date::from_raw(self.inner, self.calendar.0)
516
0
    }
517
}
518
519
impl DateTime<Japanese> {
520
    /// Construct a new Japanese datetime from integers.
521
    ///
522
    /// Years are specified in the era provided.
523
    ///
524
    /// ```rust
525
    /// use icu::calendar::japanese::Japanese;
526
    /// use icu::calendar::{types, DateTime};
527
    /// use tinystr::tinystr;
528
    ///
529
    /// let japanese_calendar = Japanese::new();
530
    ///
531
    /// let era = types::Era(tinystr!(16, "heisei"));
532
    ///
533
    /// let datetime = DateTime::try_new_japanese_datetime(
534
    ///     era,
535
    ///     14,
536
    ///     1,
537
    ///     2,
538
    ///     13,
539
    ///     1,
540
    ///     0,
541
    ///     japanese_calendar,
542
    /// )
543
    /// .expect("Constructing a date should succeed");
544
    ///
545
    /// assert_eq!(datetime.date.year().era, era);
546
    /// assert_eq!(datetime.date.year().number, 14);
547
    /// assert_eq!(datetime.date.month().ordinal, 1);
548
    /// assert_eq!(datetime.date.day_of_month().0, 2);
549
    /// assert_eq!(datetime.time.hour.number(), 13);
550
    /// assert_eq!(datetime.time.minute.number(), 1);
551
    /// assert_eq!(datetime.time.second.number(), 0);
552
    /// ```
553
    #[allow(clippy::too_many_arguments)] // it's more convenient to have this many arguments
554
                                         // if people wish to construct this by parts they can use
555
                                         // Date::try_new_japanese_date() + DateTime::new(date, time)
556
0
    pub fn try_new_japanese_datetime<A: AsCalendar<Calendar = Japanese>>(
557
0
        era: types::Era,
558
0
        year: i32,
559
0
        month: u8,
560
0
        day: u8,
561
0
        hour: u8,
562
0
        minute: u8,
563
0
        second: u8,
564
0
        japanese_calendar: A,
565
0
    ) -> Result<DateTime<A>, CalendarError> {
566
        Ok(DateTime {
567
0
            date: Date::try_new_japanese_date(era, year, month, day, japanese_calendar)?,
568
0
            time: Time::try_new(hour, minute, second, 0)?,
569
        })
570
0
    }
571
}
572
573
impl DateTime<JapaneseExtended> {
574
    /// Construct a new Japanese datetime from integers with all eras.
575
    ///
576
    /// Years are specified in the era provided.
577
    ///
578
    /// ```rust
579
    /// use icu::calendar::japanese::JapaneseExtended;
580
    /// use icu::calendar::{types, DateTime};
581
    /// use tinystr::tinystr;
582
    ///
583
    /// let japanext_calendar = JapaneseExtended::new();
584
    ///
585
    /// let era = types::Era(tinystr!(16, "kansei-1789"));
586
    ///
587
    /// let datetime = DateTime::try_new_japanese_extended_datetime(
588
    ///     era,
589
    ///     7,
590
    ///     1,
591
    ///     2,
592
    ///     13,
593
    ///     1,
594
    ///     0,
595
    ///     japanext_calendar,
596
    /// )
597
    /// .expect("Constructing a date should succeed");
598
    ///
599
    /// assert_eq!(datetime.date.year().era, era);
600
    /// assert_eq!(datetime.date.year().number, 7);
601
    /// assert_eq!(datetime.date.month().ordinal, 1);
602
    /// assert_eq!(datetime.date.day_of_month().0, 2);
603
    /// assert_eq!(datetime.time.hour.number(), 13);
604
    /// assert_eq!(datetime.time.minute.number(), 1);
605
    /// assert_eq!(datetime.time.second.number(), 0);
606
    /// ```
607
    #[allow(clippy::too_many_arguments)] // it's more convenient to have this many arguments
608
                                         // if people wish to construct this by parts they can use
609
                                         // Date::try_new_japanese_date() + DateTime::new(date, time)
610
0
    pub fn try_new_japanese_extended_datetime<A: AsCalendar<Calendar = JapaneseExtended>>(
611
0
        era: types::Era,
612
0
        year: i32,
613
0
        month: u8,
614
0
        day: u8,
615
0
        hour: u8,
616
0
        minute: u8,
617
0
        second: u8,
618
0
        japanext_calendar: A,
619
0
    ) -> Result<DateTime<A>, CalendarError> {
620
        Ok(DateTime {
621
0
            date: Date::try_new_japanese_extended_date(era, year, month, day, japanext_calendar)?,
622
0
            time: Time::try_new(hour, minute, second, 0)?,
623
        })
624
0
    }
625
}
626
627
const MEIJI_START: EraStartDate = EraStartDate {
628
    year: 1868,
629
    month: 9,
630
    day: 8,
631
};
632
const TAISHO_START: EraStartDate = EraStartDate {
633
    year: 1912,
634
    month: 7,
635
    day: 30,
636
};
637
const SHOWA_START: EraStartDate = EraStartDate {
638
    year: 1926,
639
    month: 12,
640
    day: 25,
641
};
642
const HEISEI_START: EraStartDate = EraStartDate {
643
    year: 1989,
644
    month: 1,
645
    day: 8,
646
};
647
const REIWA_START: EraStartDate = EraStartDate {
648
    year: 2019,
649
    month: 5,
650
    day: 1,
651
};
652
653
const FALLBACK_ERA: (EraStartDate, TinyStr16) = (REIWA_START, tinystr!(16, "reiwa"));
654
655
impl Japanese {
656
    /// Given an ISO date, give year and era for that date in the Japanese calendar
657
    ///
658
    /// This will also use Gregorian eras for eras that are before the earliest era
659
0
    fn adjusted_year_for(&self, date: &IsoDateInner) -> (i32, TinyStr16) {
660
0
        let date: EraStartDate = date.into();
661
0
        let (start, era) = self.japanese_era_for(date);
662
        // The year in which an era starts is Year 1, and it may be short
663
        // The only time this function will experience dates that are *before*
664
        // the era start date are for the first era (Currently, taika-645
665
        // for japanext, meiji for japanese),
666
        // In such a case, we instead fall back to Gregorian era codes
667
0
        if date < start {
668
0
            if date.year <= 0 {
669
0
                (1 - date.year, tinystr!(16, "bce"))
670
            } else {
671
0
                (date.year, tinystr!(16, "ce"))
672
            }
673
        } else {
674
0
            (date.year - start.year + 1, era)
675
        }
676
0
    }
677
678
    /// Given an date, obtain the era data (not counting spliced gregorian eras)
679
0
    fn japanese_era_for(&self, date: EraStartDate) -> (EraStartDate, TinyStr16) {
680
0
        let era_data = self.eras.get();
681
        // We optimize for the five "modern" post-Meiji eras, which are stored in a smaller
682
        // array and also hardcoded. The hardcoded version is not used if data indicates the
683
        // presence of newer eras.
684
0
        if date >= MEIJI_START
685
0
            && era_data.dates_to_eras.last().map(|x| x.1) == Some(tinystr!(16, "reiwa"))
686
        {
687
            // Fast path in case eras have not changed since this code was written
688
0
            return if date >= REIWA_START {
689
0
                (REIWA_START, tinystr!(16, "reiwa"))
690
0
            } else if date >= HEISEI_START {
691
0
                (HEISEI_START, tinystr!(16, "heisei"))
692
0
            } else if date >= SHOWA_START {
693
0
                (SHOWA_START, tinystr!(16, "showa"))
694
0
            } else if date >= TAISHO_START {
695
0
                (TAISHO_START, tinystr!(16, "taisho"))
696
            } else {
697
0
                (MEIJI_START, tinystr!(16, "meiji"))
698
            };
699
0
        }
700
0
        let data = &era_data.dates_to_eras;
701
0
        match data.binary_search_by(|(d, _)| d.cmp(&date)) {
702
0
            Ok(index) => data.get(index),
703
0
            Err(index) if index == 0 => data.get(index),
704
0
            Err(index) => data.get(index - 1).or_else(|| data.iter().next_back()),
705
        }
706
0
        .unwrap_or(FALLBACK_ERA)
707
0
    }
708
709
    /// Returns the range of dates for a given Japanese era code,
710
    /// not handling "bce" or "ce"
711
    ///
712
    /// Returns (era_start, era_end)
713
0
    fn japanese_era_range_for(
714
0
        &self,
715
0
        era: TinyStr16,
716
0
    ) -> Result<(EraStartDate, Option<EraStartDate>), CalendarError> {
717
        // Avoid linear search by trying well known eras
718
0
        if era == tinystr!(16, "reiwa") {
719
            // Check if we're the last
720
0
            if let Some(last) = self.eras.get().dates_to_eras.last() {
721
0
                if last.1 == era {
722
0
                    return Ok((REIWA_START, None));
723
0
                }
724
0
            }
725
0
        } else if era == tinystr!(16, "heisei") {
726
0
            return Ok((HEISEI_START, Some(REIWA_START)));
727
0
        } else if era == tinystr!(16, "showa") {
728
0
            return Ok((SHOWA_START, Some(HEISEI_START)));
729
0
        } else if era == tinystr!(16, "taisho") {
730
0
            return Ok((TAISHO_START, Some(SHOWA_START)));
731
0
        } else if era == tinystr!(16, "meiji") {
732
0
            return Ok((MEIJI_START, Some(TAISHO_START)));
733
0
        }
734
735
0
        let era_data = self.eras.get();
736
0
        let data = &era_data.dates_to_eras;
737
        // Try to avoid linear search by binary searching for the year suffix
738
0
        if let Some(year) = era.split('-').nth(1) {
739
0
            if let Ok(ref int) = year.parse::<i32>() {
740
0
                if let Ok(index) = data.binary_search_by(|(d, _)| d.year.cmp(int)) {
741
                    #[allow(clippy::expect_used)] // see expect message
742
0
                    let (era_start, code) = data
743
0
                        .get(index)
744
0
                        .expect("Indexing from successful binary search must succeed");
745
                    // There is a slight chance we hit the case where there are two eras in the same year
746
                    // There are a couple of rare cases of this, but it's not worth writing a range-based binary search
747
                    // to catch them since this is an optimization
748
0
                    if code == era {
749
0
                        return Ok((era_start, data.get(index + 1).map(|e| e.0)));
750
0
                    }
751
0
                }
752
0
            }
753
0
        }
754
755
        // Avoidance didn't work. Let's find the era manually, searching back from the present
756
0
        if let Some((index, (start, _))) = data.iter().enumerate().rev().find(|d| d.1 .1 == era) {
757
0
            return Ok((start, data.get(index + 1).map(|e| e.0)));
758
0
        }
759
760
0
        Err(CalendarError::UnknownEra(era, self.debug_name()))
761
0
    }
762
763
0
    fn new_japanese_date_inner(
764
0
        &self,
765
0
        era: types::Era,
766
0
        year: i32,
767
0
        month: u8,
768
0
        day: u8,
769
0
    ) -> Result<JapaneseDateInner, CalendarError> {
770
0
        let cal = Ref(self);
771
0
        if era.0 == tinystr!(16, "bce") {
772
0
            if year <= 0 {
773
0
                return Err(CalendarError::OutOfRange);
774
0
            }
775
0
            return Ok(Date::try_new_iso_date(1 - year, month, day)?
776
0
                .to_calendar(cal)
777
                .inner);
778
0
        } else if era.0 == tinystr!(16, "ce") {
779
0
            if year <= 0 {
780
0
                return Err(CalendarError::OutOfRange);
781
0
            }
782
0
            return Ok(Date::try_new_iso_date(year, month, day)?
783
0
                .to_calendar(cal)
784
                .inner);
785
0
        }
786
787
0
        let (era_start, next_era_start) = self.japanese_era_range_for(era.0)?;
788
789
0
        let date_in_iso = EraStartDate {
790
0
            year: era_start.year + year - 1,
791
0
            month,
792
0
            day,
793
0
        };
794
795
0
        if date_in_iso < era_start {
796
0
            return Err(CalendarError::OutOfRange);
797
0
        } else if let Some(next_era_start) = next_era_start {
798
0
            if date_in_iso >= next_era_start {
799
0
                return Err(CalendarError::OutOfRange);
800
0
            }
801
0
        }
802
803
0
        let iso = Date::try_new_iso_date(date_in_iso.year, date_in_iso.month, date_in_iso.day)?;
804
0
        Ok(JapaneseDateInner {
805
0
            inner: iso.inner,
806
0
            adjusted_year: year,
807
0
            era: era.0,
808
0
        })
809
0
    }
810
}
811
812
#[cfg(test)]
813
mod tests {
814
    use super::*;
815
    use crate::Ref;
816
817
    fn single_test_roundtrip(calendar: Ref<Japanese>, era: &str, year: i32, month: u8, day: u8) {
818
        let era = types::Era(era.parse().expect("era must parse"));
819
820
        let date =
821
            Date::try_new_japanese_date(era, year, month, day, calendar).unwrap_or_else(|e| {
822
                panic!("Failed to construct date with {era:?}, {year}, {month}, {day}: {e}")
823
            });
824
        let iso = date.to_iso();
825
        let reconstructed = Date::new_from_iso(iso, calendar);
826
        assert_eq!(
827
            date, reconstructed,
828
            "Failed to roundtrip with {era:?}, {year}, {month}, {day}"
829
        );
830
831
        // Extra coverage for https://github.com/unicode-org/icu4x/issues/4968
832
        assert_eq!(reconstructed.year().era, era);
833
        assert_eq!(reconstructed.year().number, year);
834
    }
835
836
    fn single_test_roundtrip_ext(
837
        calendar: Ref<JapaneseExtended>,
838
        era: &str,
839
        year: i32,
840
        month: u8,
841
        day: u8,
842
    ) {
843
        let era = types::Era(era.parse().expect("era must parse"));
844
845
        let date = Date::try_new_japanese_extended_date(era, year, month, day, calendar)
846
            .unwrap_or_else(|e| {
847
                panic!("Failed to construct date with {era:?}, {year}, {month}, {day}: {e}")
848
            });
849
        let iso = date.to_iso();
850
        let reconstructed = Date::new_from_iso(iso, calendar);
851
        assert_eq!(
852
            date, reconstructed,
853
            "Failed to roundtrip with {era:?}, {year}, {month}, {day}"
854
        )
855
    }
856
857
    // test that the Gregorian eras roundtrip to Japanese ones
858
    fn single_test_gregorian_roundtrip_ext(
859
        calendar: Ref<JapaneseExtended>,
860
        era: &str,
861
        year: i32,
862
        month: u8,
863
        day: u8,
864
        era2: &str,
865
        year2: i32,
866
    ) {
867
        let era = types::Era(era.parse().expect("era must parse"));
868
        let era2 = types::Era(era2.parse().expect("era must parse"));
869
870
        let expected = Date::try_new_japanese_extended_date(era2, year2, month, day, calendar)
871
            .unwrap_or_else(|e| {
872
                panic!(
873
                    "Failed to construct expectation date with {era2:?}, {year2}, {month}, {day}: {e}"
874
                )
875
            });
876
877
        let date = Date::try_new_japanese_extended_date(era, year, month, day, calendar)
878
            .unwrap_or_else(|e| {
879
                panic!("Failed to construct date with {era:?}, {year}, {month}, {day}: {e}")
880
            });
881
        let iso = date.to_iso();
882
        let reconstructed = Date::new_from_iso(iso, calendar);
883
        assert_eq!(
884
            expected, reconstructed,
885
            "Failed to roundtrip with {era:?}, {year}, {month}, {day} == {era2:?}, {year}"
886
        )
887
    }
888
889
    fn single_test_error(
890
        calendar: Ref<Japanese>,
891
        era: &str,
892
        year: i32,
893
        month: u8,
894
        day: u8,
895
        error: CalendarError,
896
    ) {
897
        let era = types::Era(era.parse().expect("era must parse"));
898
899
        let date = Date::try_new_japanese_date(era, year, month, day, calendar);
900
        assert_eq!(
901
            date,
902
            Err(error),
903
            "Construction with {era:?}, {year}, {month}, {day} did not return {error:?}"
904
        )
905
    }
906
907
    fn single_test_error_ext(
908
        calendar: Ref<JapaneseExtended>,
909
        era: &str,
910
        year: i32,
911
        month: u8,
912
        day: u8,
913
        error: CalendarError,
914
    ) {
915
        let era = types::Era(era.parse().expect("era must parse"));
916
917
        let date = Date::try_new_japanese_extended_date(era, year, month, day, calendar);
918
        assert_eq!(
919
            date,
920
            Err(error),
921
            "Construction with {era:?}, {year}, {month}, {day} did not return {error:?}"
922
        )
923
    }
924
925
    #[test]
926
    fn test_japanese() {
927
        let calendar = Japanese::new();
928
        let calendar_ext = JapaneseExtended::new();
929
        let calendar = Ref(&calendar);
930
        let calendar_ext = Ref(&calendar_ext);
931
932
        single_test_roundtrip(calendar, "heisei", 12, 3, 1);
933
        single_test_roundtrip(calendar, "taisho", 3, 3, 1);
934
        // Heisei did not start until later in the year
935
        single_test_error(calendar, "heisei", 1, 1, 1, CalendarError::OutOfRange);
936
937
        single_test_roundtrip_ext(calendar_ext, "heisei", 12, 3, 1);
938
        single_test_roundtrip_ext(calendar_ext, "taisho", 3, 3, 1);
939
        single_test_error_ext(calendar_ext, "heisei", 1, 1, 1, CalendarError::OutOfRange);
940
941
        single_test_roundtrip_ext(calendar_ext, "hakuho-672", 4, 3, 1);
942
        single_test_error(
943
            calendar,
944
            "hakuho-672",
945
            4,
946
            3,
947
            1,
948
            CalendarError::UnknownEra("hakuho-672".parse().unwrap(), "Japanese"),
949
        );
950
951
        // handle bce/ce
952
        single_test_roundtrip(calendar, "bce", 100, 3, 1);
953
        single_test_roundtrip(calendar, "bce", 1, 3, 1);
954
        single_test_roundtrip(calendar, "ce", 1, 3, 1);
955
        single_test_roundtrip(calendar, "ce", 100, 3, 1);
956
        single_test_roundtrip_ext(calendar_ext, "ce", 100, 3, 1);
957
        single_test_roundtrip(calendar, "ce", 1000, 3, 1);
958
        single_test_error(calendar, "ce", 0, 3, 1, CalendarError::OutOfRange);
959
        single_test_error(calendar, "bce", -1, 3, 1, CalendarError::OutOfRange);
960
961
        // handle the cases where bce/ce get adjusted to different eras
962
        // single_test_gregorian_roundtrip(calendar, "ce", 2021, 3, 1, "reiwa", 3);
963
        single_test_gregorian_roundtrip_ext(calendar_ext, "ce", 1000, 3, 1, "choho-999", 2);
964
        single_test_gregorian_roundtrip_ext(calendar_ext, "ce", 749, 5, 10, "tenpyokampo-749", 1);
965
        single_test_gregorian_roundtrip_ext(calendar_ext, "bce", 10, 3, 1, "bce", 10);
966
967
        // There were multiple eras in this year
968
        // This one is from Apr 14 to July 2
969
        single_test_roundtrip_ext(calendar_ext, "tenpyokampo-749", 1, 4, 20);
970
        single_test_roundtrip_ext(calendar_ext, "tenpyokampo-749", 1, 4, 14);
971
        single_test_roundtrip_ext(calendar_ext, "tenpyokampo-749", 1, 7, 1);
972
        single_test_error_ext(
973
            calendar_ext,
974
            "tenpyokampo-749",
975
            1,
976
            7,
977
            5,
978
            CalendarError::OutOfRange,
979
        );
980
        single_test_error_ext(
981
            calendar_ext,
982
            "tenpyokampo-749",
983
            1,
984
            4,
985
            13,
986
            CalendarError::OutOfRange,
987
        );
988
    }
989
}