Revision control
Copy as Markdown
Other Tools
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
//! This module contains types and implementations for the Buddhist calendar.
//!
//! ```rust
//! use icu::calendar::{buddhist::Buddhist, Date, DateTime};
//!
//! // `Date` type
//! let date_iso = Date::try_new_iso_date(1970, 1, 2)
//! .expect("Failed to initialize ISO Date instance.");
//! let date_buddhist = Date::new_from_iso(date_iso, Buddhist);
//!
//! // `DateTime` type
//! let datetime_iso = DateTime::try_new_iso_datetime(1970, 1, 2, 13, 1, 0)
//! .expect("Failed to initialize ISO DateTime instance.");
//! let datetime_buddhist = DateTime::new_from_iso(datetime_iso, Buddhist);
//!
//! // `Date` checks
//! assert_eq!(date_buddhist.year().number, 2513);
//! assert_eq!(date_buddhist.month().ordinal, 1);
//! assert_eq!(date_buddhist.day_of_month().0, 2);
//!
//! // `DateTime` type
//! assert_eq!(datetime_buddhist.date.year().number, 2513);
//! assert_eq!(datetime_buddhist.date.month().ordinal, 1);
//! assert_eq!(datetime_buddhist.date.day_of_month().0, 2);
//! assert_eq!(datetime_buddhist.time.hour.number(), 13);
//! assert_eq!(datetime_buddhist.time.minute.number(), 1);
//! assert_eq!(datetime_buddhist.time.second.number(), 0);
//! ```
use crate::any_calendar::AnyCalendarKind;
use crate::calendar_arithmetic::ArithmeticDate;
use crate::iso::{Iso, IsoDateInner};
use crate::{types, Calendar, CalendarError, Date, DateDuration, DateDurationUnit, DateTime, Time};
use tinystr::tinystr;
/// The number of years the Buddhist Era is ahead of C.E. by
///
/// (1 AD = 544 BE)
const BUDDHIST_ERA_OFFSET: i32 = 543;
#[derive(Copy, Clone, Debug, Default)]
/// The [Thai Solar Buddhist Calendar][cal]
///
/// The [Thai Solar Buddhist Calendar][cal] is a solar calendar used in Thailand, with twelve months.
/// The months and days are identical to that of the Gregorian calendar, however the years are counted
/// differently using the Buddhist Era.
///
/// This type can be used with [`Date`] or [`DateTime`] to represent dates in this calendar.
///
///
/// # Era codes
///
/// This calendar supports one era, `"be"`, with 1 B.E. being 543 BCE.
///
/// # Month codes
///
/// This calendar supports 12 solar month codes (`"M01" - "M12"`)
#[allow(clippy::exhaustive_structs)] // this type is stable
pub struct Buddhist;
impl Calendar for Buddhist {
type DateInner = IsoDateInner;
fn date_from_codes(
&self,
era: types::Era,
year: i32,
month_code: types::MonthCode,
day: u8,
) -> Result<Self::DateInner, CalendarError> {
if era.0 != tinystr!(16, "be") {
return Err(CalendarError::UnknownEra(era.0, self.debug_name()));
}
let year = year - BUDDHIST_ERA_OFFSET;
ArithmeticDate::new_from_codes(self, year, month_code, day).map(IsoDateInner)
}
fn date_from_iso(&self, iso: Date<Iso>) -> IsoDateInner {
*iso.inner()
}
fn date_to_iso(&self, date: &Self::DateInner) -> Date<Iso> {
Date::from_raw(*date, Iso)
}
fn months_in_year(&self, date: &Self::DateInner) -> u8 {
Iso.months_in_year(date)
}
fn days_in_year(&self, date: &Self::DateInner) -> u16 {
Iso.days_in_year(date)
}
fn days_in_month(&self, date: &Self::DateInner) -> u8 {
Iso.days_in_month(date)
}
fn offset_date(&self, date: &mut Self::DateInner, offset: DateDuration<Self>) {
Iso.offset_date(date, offset.cast_unit())
}
#[allow(clippy::field_reassign_with_default)] // it's more clear this way
fn until(
&self,
date1: &Self::DateInner,
date2: &Self::DateInner,
_calendar2: &Self,
largest_unit: DateDurationUnit,
smallest_unit: DateDurationUnit,
) -> DateDuration<Self> {
Iso.until(date1, date2, &Iso, largest_unit, smallest_unit)
.cast_unit()
}
/// The calendar-specific year represented by `date`
fn year(&self, date: &Self::DateInner) -> types::FormattableYear {
iso_year_as_buddhist(date.0.year)
}
fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
Iso.is_in_leap_year(date)
}
/// The calendar-specific month represented by `date`
fn month(&self, date: &Self::DateInner) -> types::FormattableMonth {
Iso.month(date)
}
/// The calendar-specific day-of-month represented by `date`
fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
Iso.day_of_month(date)
}
/// Information of the day of the year
fn day_of_year_info(&self, date: &Self::DateInner) -> types::DayOfYearInfo {
let prev_year = date.0.year - 1;
let next_year = date.0.year + 1;
types::DayOfYearInfo {
day_of_year: Iso::day_of_year(*date),
days_in_year: Iso::days_in_year_direct(date.0.year),
prev_year: iso_year_as_buddhist(prev_year),
days_in_prev_year: Iso::days_in_year_direct(prev_year),
next_year: iso_year_as_buddhist(next_year),
}
}
fn debug_name(&self) -> &'static str {
"Buddhist"
}
fn any_calendar_kind(&self) -> Option<AnyCalendarKind> {
Some(AnyCalendarKind::Buddhist)
}
}
impl Date<Buddhist> {
/// Construct a new Buddhist Date.
///
/// Years are specified as BE years.
///
/// ```rust
/// use icu::calendar::Date;
///
/// let date_buddhist = Date::try_new_buddhist_date(1970, 1, 2)
/// .expect("Failed to initialize Buddhist Date instance.");
///
/// assert_eq!(date_buddhist.year().number, 1970);
/// assert_eq!(date_buddhist.month().ordinal, 1);
/// assert_eq!(date_buddhist.day_of_month().0, 2);
/// ```
pub fn try_new_buddhist_date(
year: i32,
month: u8,
day: u8,
) -> Result<Date<Buddhist>, CalendarError> {
Date::try_new_iso_date(year - BUDDHIST_ERA_OFFSET, month, day)
.map(|d| Date::new_from_iso(d, Buddhist))
}
}
impl DateTime<Buddhist> {
/// Construct a new Buddhist datetime from integers.
///
/// Years are specified as BE years.
///
/// ```rust
/// use icu::calendar::DateTime;
///
/// let datetime_buddhist =
/// DateTime::try_new_buddhist_datetime(1970, 1, 2, 13, 1, 0)
/// .expect("Failed to initialize Buddhist DateTime instance.");
///
/// assert_eq!(datetime_buddhist.date.year().number, 1970);
/// assert_eq!(datetime_buddhist.date.month().ordinal, 1);
/// assert_eq!(datetime_buddhist.date.day_of_month().0, 2);
/// assert_eq!(datetime_buddhist.time.hour.number(), 13);
/// assert_eq!(datetime_buddhist.time.minute.number(), 1);
/// assert_eq!(datetime_buddhist.time.second.number(), 0);
/// ```
pub fn try_new_buddhist_datetime(
year: i32,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: u8,
) -> Result<DateTime<Buddhist>, CalendarError> {
Ok(DateTime {
date: Date::try_new_buddhist_date(year, month, day)?,
time: Time::try_new(hour, minute, second, 0)?,
})
}
}
fn iso_year_as_buddhist(year: i32) -> types::FormattableYear {
let buddhist_year = year + BUDDHIST_ERA_OFFSET;
types::FormattableYear {
era: types::Era(tinystr!(16, "be")),
number: buddhist_year,
cyclic: None,
related_iso: None,
}
}
#[cfg(test)]
mod test {
use calendrical_calculations::rata_die::RataDie;
use super::*;
#[test]
fn test_buddhist_roundtrip_near_rd_zero() {
for i in -10000..=10000 {
let rd = RataDie::new(i);
let iso1 = Iso::iso_from_fixed(rd);
let buddhist = iso1.to_calendar(Buddhist);
let iso2 = buddhist.to_calendar(Iso);
let result = Iso::fixed_from_iso(iso2.inner);
assert_eq!(rd, result);
}
}
#[test]
fn test_buddhist_roundtrip_near_epoch() {
// Buddhist epoch start RD: -198326
for i in -208326..=-188326 {
let rd = RataDie::new(i);
let iso1 = Iso::iso_from_fixed(rd);
let buddhist = iso1.to_calendar(Buddhist);
let iso2 = buddhist.to_calendar(Iso);
let result = Iso::fixed_from_iso(iso2.inner);
assert_eq!(rd, result);
}
}
#[test]
fn test_buddhist_directionality_near_rd_zero() {
for i in -100..=100 {
for j in -100..=100 {
let iso_i = Iso::iso_from_fixed(RataDie::new(i));
let iso_j = Iso::iso_from_fixed(RataDie::new(j));
let buddhist_i = Date::new_from_iso(iso_i, Buddhist);
let buddhist_j = Date::new_from_iso(iso_j, Buddhist);
assert_eq!(
i.cmp(&j),
iso_i.cmp(&iso_j),
"ISO directionality inconsistent with directionality for i: {i}, j: {j}"
);
assert_eq!(
i.cmp(&j),
buddhist_i.cmp(&buddhist_j),
"Buddhist directionality inconsistent with directionality for i: {i}, j: {j}"
);
}
}
}
#[test]
fn test_buddhist_directionality_near_epoch() {
// Buddhist epoch start RD: -198326
for i in -198426..=-198226 {
for j in -198426..=-198226 {
let iso_i = Iso::iso_from_fixed(RataDie::new(i));
let iso_j = Iso::iso_from_fixed(RataDie::new(j));
let buddhist_i = Date::new_from_iso(iso_i, Buddhist);
let buddhist_j = Date::new_from_iso(iso_j, Buddhist);
assert_eq!(
i.cmp(&j),
iso_i.cmp(&iso_j),
"ISO directionality inconsistent with directionality for i: {i}, j: {j}"
);
assert_eq!(
i.cmp(&j),
buddhist_i.cmp(&buddhist_j),
"Buddhist directionality inconsistent with directionality for i: {i}, j: {j}"
);
}
}
}
#[derive(Debug)]
struct TestCase {
iso_year: i32,
iso_month: u8,
iso_day: u8,
buddhist_year: i32,
buddhist_month: u8,
buddhist_day: u8,
}
fn check_test_case(case: TestCase) {
let iso_year = case.iso_year;
let iso_month = case.iso_month;
let iso_day = case.iso_day;
let buddhist_year = case.buddhist_year;
let buddhist_month = case.buddhist_month;
let buddhist_day = case.buddhist_day;
let iso1 = Date::try_new_iso_date(iso_year, iso_month, iso_day).unwrap();
let buddhist1 = iso1.to_calendar(Buddhist);
assert_eq!(
buddhist1.year().number,
buddhist_year,
"Iso -> Buddhist year check failed for case: {case:?}"
);
assert_eq!(
buddhist1.month().ordinal,
buddhist_month as u32,
"Iso -> Buddhist month check failed for case: {case:?}"
);
assert_eq!(
buddhist1.day_of_month().0,
buddhist_day as u32,
"Iso -> Buddhist day check failed for case: {case:?}"
);
let buddhist2 =
Date::try_new_buddhist_date(buddhist_year, buddhist_month, buddhist_day).unwrap();
let iso2 = buddhist2.to_calendar(Iso);
assert_eq!(
iso2.year().number,
iso_year,
"Buddhist -> Iso year check failed for case: {case:?}"
);
assert_eq!(
iso2.month().ordinal,
iso_month as u32,
"Buddhist -> Iso month check failed for case: {case:?}"
);
assert_eq!(
iso2.day_of_month().0,
iso_day as u32,
"Buddhist -> Iso day check failed for case: {case:?}"
);
}
#[test]
fn test_buddhist_cases_near_rd_zero() {
let cases = [
TestCase {
iso_year: -100,
iso_month: 2,
iso_day: 15,
buddhist_year: 443,
buddhist_month: 2,
buddhist_day: 15,
},
TestCase {
iso_year: -3,
iso_month: 10,
iso_day: 29,
buddhist_year: 540,
buddhist_month: 10,
buddhist_day: 29,
},
TestCase {
iso_year: 0,
iso_month: 12,
iso_day: 31,
buddhist_year: 543,
buddhist_month: 12,
buddhist_day: 31,
},
TestCase {
iso_year: 1,
iso_month: 1,
iso_day: 1,
buddhist_year: 544,
buddhist_month: 1,
buddhist_day: 1,
},
TestCase {
iso_year: 4,
iso_month: 2,
iso_day: 29,
buddhist_year: 547,
buddhist_month: 2,
buddhist_day: 29,
},
];
for case in cases {
check_test_case(case);
}
}
#[test]
fn test_buddhist_cases_near_epoch() {
// 1 BE = 543 BCE = -542 ISO
let cases = [
TestCase {
iso_year: -554,
iso_month: 12,
iso_day: 31,
buddhist_year: -11,
buddhist_month: 12,
buddhist_day: 31,
},
TestCase {
iso_year: -553,
iso_month: 1,
iso_day: 1,
buddhist_year: -10,
buddhist_month: 1,
buddhist_day: 1,
},
TestCase {
iso_year: -544,
iso_month: 8,
iso_day: 31,
buddhist_year: -1,
buddhist_month: 8,
buddhist_day: 31,
},
TestCase {
iso_year: -543,
iso_month: 5,
iso_day: 12,
buddhist_year: 0,
buddhist_month: 5,
buddhist_day: 12,
},
TestCase {
iso_year: -543,
iso_month: 12,
iso_day: 31,
buddhist_year: 0,
buddhist_month: 12,
buddhist_day: 31,
},
TestCase {
iso_year: -542,
iso_month: 1,
iso_day: 1,
buddhist_year: 1,
buddhist_month: 1,
buddhist_day: 1,
},
TestCase {
iso_year: -541,
iso_month: 7,
iso_day: 9,
buddhist_year: 2,
buddhist_month: 7,
buddhist_day: 9,
},
];
for case in cases {
check_test_case(case);
}
}
}