use crate::cal::abstract_gregorian::{
AbstractGregorian, GregorianYears, impl_with_abstract_gregorian,
};
use crate::cal::gregorian::CeBce;
use crate::calendar_arithmetic::ArithmeticDate;
use crate::error::{DateError, UnknownEraError};
use crate::provider::{CalendarJapaneseModernV1, EraStartDate, JapaneseEras};
use crate::{AsCalendar, Date, types};
use icu_provider::prelude::*;
#[derive(Clone, Debug, Default, Copy)]
pub struct Japanese(JapaneseEras);
impl Japanese {
#[cfg(feature = "compiled_data")]
pub const fn new() -> Self {
const { Self(*crate::provider::Baked::SINGLETON_CALENDAR_JAPANESE_MODERN_V1) }
}
icu_provider::gen_buffer_data_constructors!(() -> error: DataError,
functions: [
new: skip,
try_new_with_buffer_provider,
try_new_unstable,
Self,
]);
#[doc = icu_provider::gen_buffer_unstable_docs!(UNSTABLE, Self::new)]
pub fn try_new_unstable<D: DataProvider<CalendarJapaneseModernV1> + ?Sized>(
provider: &D,
) -> Result<Self, DataError> {
Ok(Self(*provider.load(Default::default())?.payload.get()))
}
}
impl GregorianYears for Japanese {
fn extended_from_era_year(
&self,
era: Option<&[u8]>,
year: i32,
) -> Result<i32, UnknownEraError> {
if let Ok(g) = CeBce.extended_from_era_year(era, year) {
return Ok(g);
}
let (start, ..) = self
.0
.eras()
.rfind(|(_, name, _)| era == Some(name.as_bytes()))
.ok_or(UnknownEraError)?;
Ok(year - 1 + start.year)
}
fn era_year_from_extended(&self, year: i32, month: u8, day: u8) -> types::EraYear {
let date: EraStartDate = EraStartDate { year, month, day };
let ret =
if let Some((start, code, idx)) = self.0.eras().rfind(|&(start, ..)| date >= start) {
types::EraYear {
era: code.resize(),
era_index: Some(idx),
year: year - start.year + 1,
extended_year: year,
ambiguity: types::YearAmbiguity::CenturyRequired,
}
} else {
CeBce.era_year_from_extended(year, month, day)
};
if ret.era == "meiji" && ret.year < 6 {
return CeBce.era_year_from_extended(year, month, day);
}
ret
}
fn debug_name(&self) -> &'static str {
"Japanese"
}
fn calendar_algorithm(&self) -> Option<crate::preferences::CalendarAlgorithm> {
Some(crate::preferences::CalendarAlgorithm::Japanese)
}
}
impl_with_abstract_gregorian!(Japanese, JapaneseDateInner, Japanese, this, *this);
impl Date<Japanese> {
pub fn try_new_japanese_with_calendar<A: AsCalendar<Calendar = Japanese>>(
era: &str,
year: i32,
month: u8,
day: u8,
japanese_calendar: A,
) -> Result<Date<A>, DateError> {
ArithmeticDate::from_era_year_month_day(
era,
year,
month,
day,
&AbstractGregorian(*japanese_calendar.as_calendar()),
)
.map(ArithmeticDate::cast)
.map(JapaneseDateInner)
.map(|i| Date::from_raw(i, japanese_calendar))
}
}
impl Date<Japanese> {
#[deprecated(since = "2.2.0", note = "use `try_new_japanese_with_calendar`")]
pub fn try_new_japanese_extended_with_calendar<A: AsCalendar<Calendar = Japanese>>(
era: &str,
year: i32,
month: u8,
day: u8,
japanese_calendar: A,
) -> Result<Date<A>, DateError> {
Self::try_new_japanese_with_calendar(era, year, month, day, japanese_calendar)
}
}
#[cfg(test)]
mod tests {
use super::*;
use tinystr::tinystr;
const CALENDAR: Japanese = Japanese(
JapaneseEras::with_last_era(
EraStartDate {
year: 2086,
month: 11,
day: 1,
},
tinystr!(8, "fuzu"),
8,
)
.unwrap(),
);
fn single_test_roundtrip(era: &str, year: i32, month: u8, day: u8) {
let date = Date::try_new_japanese_with_calendar(era, year, month, day, CALENDAR)
.unwrap_or_else(|e| {
panic!("Failed to construct date with {era:?}, {year}, {month}, {day}: {e:?}")
});
let reconstructed = Date::from_rata_die(date.to_rata_die(), CALENDAR);
assert_eq!(
date, reconstructed,
"Failed to roundtrip with {era:?}, {year}, {month}, {day}"
);
assert_eq!(reconstructed.era_year().era, era);
assert_eq!(reconstructed.era_year().year, year);
}
fn single_test_era_range_roundtrip(
era: &str,
year: i32,
month: u8,
day: u8,
era2: &str,
year2: i32,
) {
let expected = Date::try_new_japanese_with_calendar(era2, year2, month, day, CALENDAR)
.unwrap_or_else(|e| {
panic!(
"Failed to construct expectation date with {era2:?}, {year2}, {month}, {day}: {e:?}"
)
});
let date = Date::try_new_japanese_with_calendar(era, year, month, day, CALENDAR)
.unwrap_or_else(|e| {
panic!("Failed to construct date with {era:?}, {year}, {month}, {day}: {e:?}")
});
assert_eq!(
expected, date,
"Failed to convert with {era:?}, {year}, {month}, {day}"
)
}
#[test]
fn test_japanese() {
single_test_roundtrip("heisei", 12, 3, 1);
single_test_roundtrip("taisho", 3, 3, 1);
single_test_era_range_roundtrip("heisei", 1, 1, 1, "showa", 64);
single_test_roundtrip("bce", 100, 3, 1);
single_test_roundtrip("bce", 1, 3, 1);
single_test_roundtrip("ce", 1, 3, 1);
single_test_roundtrip("ce", 100, 3, 1);
single_test_roundtrip("ce", 1000, 3, 1);
single_test_era_range_roundtrip("ce", 0, 3, 1, "bce", 1);
single_test_era_range_roundtrip("bce", -1, 3, 1, "ce", 2);
single_test_roundtrip("reiwa", 68, 10, 31);
single_test_era_range_roundtrip("fuzu", 1, 10, 31, "reiwa", 68);
single_test_roundtrip("fuzu", 1, 11, 1);
single_test_era_range_roundtrip("ce", 2024, 3, 1, "reiwa", 6);
}
#[test]
fn test_meiji_6_switchover() {
single_test_roundtrip("ce", 1868, 1, 1);
single_test_roundtrip("ce", 1868, 10, 1);
single_test_roundtrip("ce", 1868, 10, 23);
single_test_roundtrip("ce", 1868, 10, 24);
single_test_roundtrip("ce", 1868, 11, 1);
single_test_roundtrip("ce", 1869, 1, 1);
single_test_roundtrip("ce", 1871, 1, 1);
single_test_era_range_roundtrip("meiji", 1, 1, 1, "ce", 1868);
single_test_era_range_roundtrip("meiji", 1, 10, 1, "ce", 1868);
single_test_era_range_roundtrip("meiji", 1, 10, 23, "ce", 1868);
single_test_era_range_roundtrip("meiji", 1, 10, 24, "ce", 1868);
single_test_era_range_roundtrip("meiji", 1, 11, 1, "ce", 1868);
single_test_era_range_roundtrip("meiji", 1, 11, 1, "ce", 1868);
single_test_era_range_roundtrip("meiji", 2, 1, 1, "ce", 1869);
single_test_era_range_roundtrip("meiji", 5, 1, 1, "ce", 1872);
single_test_roundtrip("meiji", 6, 1, 1);
single_test_roundtrip("meiji", 6, 5, 1);
single_test_roundtrip("meiji", 7, 1, 1);
single_test_era_range_roundtrip("ce", 1873, 1, 1, "meiji", 6);
single_test_era_range_roundtrip("ce", 1873, 5, 1, "meiji", 6);
single_test_era_range_roundtrip("ce", 1874, 1, 1, "meiji", 7);
}
#[test]
fn test_constructor_roundtrip() {
let rds = crate::tests::get_interesting_rds();
for rd in rds {
let date = Date::from_rata_die(rd, Japanese::new());
let reconstructed = Date::try_new_japanese_with_calendar(
date.era_year().era.as_str(),
date.era_year().year,
date.month().ordinal,
date.day_of_month().0,
Japanese::new(),
)
.unwrap();
assert_eq!(
reconstructed.to_rata_die(),
rd,
"Japanese failed for RD {:?}",
rd
);
}
}
}