usecrate::calendar_arithmetic::ArithmeticDate; usecrate::calendar_arithmetic::DateFieldsResolver; usecrate::error::{DateError, DateFromFieldsError, EcmaReferenceYearError, UnknownEraError}; usecrate::options::DateFromFieldsOptions; usecrate::options::{DateAddOptions, DateDifferenceOptions}; usecrate::types::DateFields; usecrate::{types, Calendar, Date, RangeError}; use calendrical_calculations::rata_die::RataDie; use tinystr::tinystr;
/// The [Indian National (Śaka) Calendar](https://en.wikipedia.org/wiki/Indian_national_calendar) /// /// The Indian National calendar is a solar calendar created by the Indian government. /// /// This implementation extends proleptically for dates before the calendar's creation /// in 1879 Śaka (1957 CE). /// /// This corresponds to the `"indian"` [CLDR calendar](https://unicode.org/reports/tr35/#UnicodeCalendarIdentifier). /// /// # Era codes /// /// This calendar uses a single era code: `shaka`, with Śaka 0 being 78 CE. Dates before this era use negative years. /// /// # Months and days /// /// The 12 months are called Chaitra (`M01`, 30 days), Vaisakha (`M02`, 31 days), /// Jyaishtha (`M03`, 31 days), Ashadha (`M04`, 31 days), Sravana (`M05`, 31 days), /// Bhadra (`M06`, 31 days), Asvina (`M07`, 30 days), Kartika (`M08`, 30 days), /// Agrahayana or Margasirsha (`M09`, 30 days), Pausha (`M10`, 30 days), Magha (`M11`, 30 days), /// Phalguna (`M12`, 30 days). /// /// In leap years (years where the concurrent [`Gregorian`](crate::cal::Gregorian) year (`year + 78`) is leap), /// Chaitra gains a 31st day. /// /// Standard years thus have 365 days, and leap years 366. /// /// # Calendar drift /// /// The Indian calendar has the same year lengths and leap year rules as the Gregorian calendar, /// so it experiences the same drift of 1 day in ~7700 years with respect to the seasons. #[derive(Copy, Clone, Debug, Hash, Default, Eq, PartialEq, PartialOrd, Ord)] #[allow(clippy::exhaustive_structs)] // this type is stable pubstruct Indian;
/// The inner date type used for representing [`Date`]s of [`Indian`]. See [`Date`] and [`Indian`] for more details. #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)] pubstruct IndianDateInner(ArithmeticDate<Indian>);
/// The Śaka era starts on the 81st day of the Gregorian year (March 22 or 21) /// which is an 80 day offset. This number should be subtracted from Gregorian dates const DAY_OFFSET: u16 = 80; /// The Śaka era is 78 years behind Gregorian. This number should be added to Gregorian dates const YEAR_OFFSET: i32 = 78;
impl DateFieldsResolver for Indian { type YearInfo = i32;
#[inline] fn reference_year_from_month_day(
&self,
month_code: types::ValidMonthCode,
day: u8,
) -> Result<Self::YearInfo, EcmaReferenceYearError> { let (ordinal_month, false) = month_code.to_tuple() else { return Err(EcmaReferenceYearError::MonthCodeNotInCalendar);
}; // December 31, 1972 occurs on 10th month, 10th day, 1894 Shaka // Note: 1894 Shaka is also a leap year let shaka_year = if ordinal_month < 10 || (ordinal_month == 10 && day <= 10) { 1894
} else { 1893
};
Ok(shaka_year)
}
}
implcrate::cal::scaffold::UnstableSealed for Indian {} impl Calendar for Indian { type DateInner = IndianDateInner; type Year = types::EraYear; type DifferenceError = core::convert::Infallible;
// Algorithms directly implemented in icu_calendar since they're not from the book fn from_rata_die(&self, rd: RataDie) -> Self::DateInner { let iso_year = calendrical_calculations::gregorian::year_from_fixed(rd)
.unwrap_or_else(|e| e.saturate()); // Get day number in year (1 indexed) let day_of_year_iso =
(rd - calendrical_calculations::gregorian::day_before_year(iso_year)) as u16; // Convert to Śaka year letmut year = iso_year - YEAR_OFFSET; // This is in the previous Indian year let day_of_year_indian = if day_of_year_iso <= DAY_OFFSET {
year -= 1; let n_days = if calendrical_calculations::gregorian::is_leap_year(year + YEAR_OFFSET) { 366
} else { 365
};
// calculate day of year in previous year
n_days + day_of_year_iso - DAY_OFFSET
} else {
day_of_year_iso - DAY_OFFSET
}; letmut month = 1; letmut day = day_of_year_indian as i32; while month <= 12 { let month_days = Self::days_in_provided_month(year, month) as i32; if day <= month_days { break;
} else {
day -= month_days;
month += 1;
}
}
debug_assert!(day <= Self::days_in_provided_month(year, month) as i32); let day = day.try_into().unwrap_or(1);
// Algorithms directly implemented in icu_calendar since they're not from the book fn to_rata_die(&self, date: &Self::DateInner) -> RataDie { let day_of_year_indian = self.day_of_year(date).0; // 1-indexed let days_in_year = self.days_in_year(date);
letmut year_iso = date.0.year + YEAR_OFFSET; // days_in_year is a valid day of the year, so we check > not >= let day_of_year_iso = if day_of_year_indian + DAY_OFFSET > days_in_year {
year_iso += 1; // calculate day of year in next year
day_of_year_indian + DAY_OFFSET - days_in_year
} else {
day_of_year_indian + DAY_OFFSET
};
impl Indian { /// Construct a new Indian Calendar pubfn new() -> Self { Self
}
}
impl Date<Indian> { /// Construct new Indian Date, with year provided in the Śaka era. /// /// ```rust /// use icu::calendar::Date; /// /// let date_indian = Date::try_new_indian(1891, 10, 12) /// .expect("Failed to initialize Indian Date instance."); /// /// assert_eq!(date_indian.era_year().year, 1891); /// assert_eq!(date_indian.month().ordinal, 10); /// assert_eq!(date_indian.day_of_month().0, 12); /// ``` pubfn try_new_indian(year: i32, month: u8, day: u8) -> Result<Date<Indian>, RangeError> {
ArithmeticDate::try_from_ymd(year, month, day)
.map(IndianDateInner)
.map(|inner| Date::from_raw(inner, Indian))
}
}
#[cfg(test)] mod tests { usesuper::*; use calendrical_calculations::rata_die::RataDie; fn assert_roundtrip(y: i32, m: u8, d: u8, iso_y: i32, iso_m: u8, iso_d: u8) { let indian =
Date::try_new_indian(y, m, d).expect("Indian date should construct successfully"); let iso = indian.to_iso();
assert_eq!(
iso.era_year().year,
iso_y, "{y}-{m}-{d}: ISO year did not match"
);
assert_eq!(
iso.month().ordinal,
iso_m, "{y}-{m}-{d}: ISO month did not match"
);
assert_eq!(
iso.day_of_month().0,
iso_d, "{y}-{m}-{d}: ISO day did not match"
);
let roundtrip = iso.to_calendar(Indian);
assert_eq!(
roundtrip.era_year().year,
indian.era_year().year, "{y}-{m}-{d}: roundtrip year did not match"
);
assert_eq!(
roundtrip.month().ordinal,
indian.month().ordinal, "{y}-{m}-{d}: roundtrip month did not match"
);
assert_eq!(
roundtrip.day_of_month(),
indian.day_of_month(), "{y}-{m}-{d}: roundtrip day did not match"
);
}
#[test] fn roundtrip_indian() { // Ultimately the day of the year will always be identical regardless of it // being a leap year or not // Test dates that occur after and before Chaitra 1 (March 22/21), in all years of // a four-year leap cycle, to ensure that all code paths are tested
assert_roundtrip(1944, 6, 7, 2022, 8, 29);
assert_roundtrip(1943, 6, 7, 2021, 8, 29);
assert_roundtrip(1942, 6, 7, 2020, 8, 29);
assert_roundtrip(1941, 6, 7, 2019, 8, 29);
assert_roundtrip(1944, 11, 7, 2023, 1, 27);
assert_roundtrip(1943, 11, 7, 2022, 1, 27);
assert_roundtrip(1942, 11, 7, 2021, 1, 27);
assert_roundtrip(1941, 11, 7, 2020, 1, 27);
}
#[test] fn test_roundtrip_near_rd_zero() { for i in -1000..=1000 { let initial = RataDie::new(i); let result = Date::from_rata_die(initial, Indian).to_rata_die();
assert_eq!(
initial, result, "Roundtrip failed for initial: {initial:?}, result: {result:?}"
);
}
}
#[test] fn test_roundtrip_near_epoch_start() { // Epoch start: RD 28570 for i in27570..=29570 { let initial = RataDie::new(i); let result = Date::from_rata_die(initial, Indian).to_rata_die();
assert_eq!(
initial, result, "Roundtrip failed for initial: {initial:?}, result: {result:?}"
);
}
}
#[test] fn test_directionality_near_rd_zero() { for i in -100..=100 { for j in -100..=100 { let rd_i = RataDie::new(i); let rd_j = RataDie::new(j);
let indian_i = Date::from_rata_die(rd_i, Indian); let indian_j = Date::from_rata_die(rd_j, Indian);
assert_eq!(i.cmp(&j), indian_i.cmp(&indian_j), "Directionality test failed for i: {i}, j: {j}, indian_i: {indian_i:?}, indian_j: {indian_j:?}");
}
}
}
#[test] fn test_directionality_near_epoch_start() { // Epoch start: RD 28570 for i in28470..=28670 { for j in28470..=28670 { let indian_i = Date::from_rata_die(RataDie::new(i), Indian); let indian_j = Date::from_rata_die(RataDie::new(j), Indian);
assert_eq!(i.cmp(&j), indian_i.cmp(&indian_j), "Directionality test failed for i: {i}, j: {j}, indian_i: {indian_i:?}, indian_j: {indian_j:?}");
}
}
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.