From d30371ee163ef17db2d13989a7c6144ab3a435e9 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 14 Dec 2023 17:36:27 -0800 Subject: [PATCH] Share code for month codes, document month indexing --- components/calendar/src/chinese.rs | 59 +---------------- components/calendar/src/chinese_based.rs | 82 +++++++++++++++++++++++- components/calendar/src/dangi.rs | 59 +---------------- 3 files changed, 83 insertions(+), 117 deletions(-) diff --git a/components/calendar/src/chinese.rs b/components/calendar/src/chinese.rs index b40480b5ee7..18154bf05cf 100644 --- a/components/calendar/src/chinese.rs +++ b/components/calendar/src/chinese.rs @@ -228,64 +228,7 @@ impl Calendar for Chinese { /// leap months. For example, in a year where an intercalary month is added after the second /// month, the month codes for ordinal months 1, 2, 3, 4, 5 would be "M01", "M02", "M02L", "M03", "M04". fn month(&self, date: &Self::DateInner) -> types::FormattableMonth { - let ordinal = date.0 .0.month; - let leap_month_option = date.0 .0.year_info.leap_month(); - let leap_month = if let Some(leap) = leap_month_option { - leap.get() - } else { - 14 - }; - let code_inner = if leap_month == ordinal { - // Month cannot be 1 because a year cannot have a leap month before the first actual month, - // and the maximum num of months ina leap year is 13. - debug_assert!((2..=13).contains(&ordinal)); - match ordinal { - 2 => tinystr!(4, "M01L"), - 3 => tinystr!(4, "M02L"), - 4 => tinystr!(4, "M03L"), - 5 => tinystr!(4, "M04L"), - 6 => tinystr!(4, "M05L"), - 7 => tinystr!(4, "M06L"), - 8 => tinystr!(4, "M07L"), - 9 => tinystr!(4, "M08L"), - 10 => tinystr!(4, "M09L"), - 11 => tinystr!(4, "M10L"), - 12 => tinystr!(4, "M11L"), - 13 => tinystr!(4, "M12L"), - _ => tinystr!(4, "und"), - } - } else { - let mut adjusted_ordinal = ordinal; - if ordinal > leap_month { - // Before adjusting for leap month, if ordinal > leap_month, - // the month cannot be 1 because this implies the leap month is < 1, which is impossible; - // cannot be 2 because that implies the leap month is = 1, which is impossible, - // and cannot be more than 13 because max number of months in a year is 13. - debug_assert!((2..=13).contains(&ordinal)); - adjusted_ordinal -= 1; - } - debug_assert!((1..=12).contains(&adjusted_ordinal)); - match adjusted_ordinal { - 1 => tinystr!(4, "M01"), - 2 => tinystr!(4, "M02"), - 3 => tinystr!(4, "M03"), - 4 => tinystr!(4, "M04"), - 5 => tinystr!(4, "M05"), - 6 => tinystr!(4, "M06"), - 7 => tinystr!(4, "M07"), - 8 => tinystr!(4, "M08"), - 9 => tinystr!(4, "M09"), - 10 => tinystr!(4, "M10"), - 11 => tinystr!(4, "M11"), - 12 => tinystr!(4, "M12"), - _ => tinystr!(4, "und"), - } - }; - let code = types::MonthCode(code_inner); - types::FormattableMonth { - ordinal: ordinal as u32, - code, - } + date.0.month() } /// The calendar-specific day-of-month represented by `date` diff --git a/components/calendar/src/chinese_based.rs b/components/calendar/src/chinese_based.rs index eb68c331d43..26594f97558 100644 --- a/components/calendar/src/chinese_based.rs +++ b/components/calendar/src/chinese_based.rs @@ -21,13 +21,14 @@ use crate::{ calendar_arithmetic::{ArithmeticDate, CalendarArithmetic, PrecomputedDataSource}, - types::MonthCode, + types::{FormattableMonth, MonthCode}, Calendar, CalendarError, Iso, }; use calendrical_calculations::chinese_based::{self, ChineseBased, YearBounds}; use calendrical_calculations::rata_die::RataDie; use core::num::NonZeroU8; +use tinystr::tinystr; /// The trait ChineseBased is used by Chinese-based calendars to perform computations shared by such calendar. /// @@ -194,6 +195,10 @@ impl ChineseBasedYearInfo { self.new_year + i64::from(self.last_day_of_month[12]) } + /// Get which month is the leap month. This produces the month *number* + /// that is the leap month (not the ordinal month). In other words, for + /// a year with an M05L, this will return Some(5). Note that the regular month precedes + /// the leap month. pub(crate) fn leap_month(self) -> Option { self.leap_month } @@ -414,6 +419,76 @@ impl u16 { self.0.year_info.last_day_of_previous_month(self.0.month) + u16::from(self.0.day) } + + /// The calendar-specific month code represented by `date`; + /// since the Chinese calendar has leap months, an "L" is appended to the month code for + /// leap months. For example, in a year where an intercalary month is added after the second + /// month, the month codes for ordinal months 1, 2, 3, 4, 5 would be "M01", "M02", "M02L", "M03", "M04". + pub(crate) fn month(&self) -> FormattableMonth { + let ordinal = self.0.month; + let leap_month_option = self.0.year_info.leap_month(); + + // 1 indexed leap month name. This is also the ordinal for the leap month + // in the year (e.g. in `M01, M01L, M02, ..`, the leap month is for month 1, and it is also + // ordinally `month 2`, zero-indexed) + let leap_month = if let Some(leap) = leap_month_option { + leap.get() + } else { + // sentinel value + 14 + }; + let code_inner = if leap_month == ordinal { + // Month cannot be 1 because a year cannot have a leap month before the first actual month, + // and the maximum num of months ina leap year is 13. + debug_assert!((2..=13).contains(&ordinal)); + match ordinal { + 2 => tinystr!(4, "M01L"), + 3 => tinystr!(4, "M02L"), + 4 => tinystr!(4, "M03L"), + 5 => tinystr!(4, "M04L"), + 6 => tinystr!(4, "M05L"), + 7 => tinystr!(4, "M06L"), + 8 => tinystr!(4, "M07L"), + 9 => tinystr!(4, "M08L"), + 10 => tinystr!(4, "M09L"), + 11 => tinystr!(4, "M10L"), + 12 => tinystr!(4, "M11L"), + 13 => tinystr!(4, "M12L"), + _ => tinystr!(4, "und"), + } + } else { + let mut adjusted_ordinal = ordinal; + if ordinal > leap_month { + // Before adjusting for leap month, if ordinal > leap_month, + // the month cannot be 1 because this implies the leap month is < 1, which is impossible; + // cannot be 2 because that implies the leap month is = 1, which is impossible, + // and cannot be more than 13 because max number of months in a year is 13. + debug_assert!((2..=13).contains(&ordinal)); + adjusted_ordinal -= 1; + } + debug_assert!((1..=12).contains(&adjusted_ordinal)); + match adjusted_ordinal { + 1 => tinystr!(4, "M01"), + 2 => tinystr!(4, "M02"), + 3 => tinystr!(4, "M03"), + 4 => tinystr!(4, "M04"), + 5 => tinystr!(4, "M05"), + 6 => tinystr!(4, "M06"), + 7 => tinystr!(4, "M07"), + 8 => tinystr!(4, "M08"), + 9 => tinystr!(4, "M09"), + 10 => tinystr!(4, "M10"), + 11 => tinystr!(4, "M11"), + 12 => tinystr!(4, "M12"), + _ => tinystr!(4, "und"), + } + }; + let code = MonthCode(code_inner); + FormattableMonth { + ordinal: ordinal as u32, + code, + } + } } impl CalendarArithmetic for C { @@ -477,6 +552,7 @@ pub(crate) fn chinese_based_ordinal_lunar_month_from_code( if code.0.len() == 4 && bytes[3] != b'L' { return None; } + // Unadjusted is zero-indexed month index, must add one to it to use let mut unadjusted = 0; if bytes[1] == b'0' { if bytes[2] >= b'1' && bytes[2] <= b'9' { @@ -486,13 +562,17 @@ pub(crate) fn chinese_based_ordinal_lunar_month_from_code( unadjusted = 10 + bytes[2] - b'0'; } if bytes[3] == b'L' { + // Asked for a leap month that doesn't exist if unadjusted + 1 != leap_month { return None; } else { + // The leap month occurs after the regular month of the same name return Some(unadjusted + 1); } } if unadjusted != 0 { + // If the month has an index greater than that of the leap month, + // bump it up by one if unadjusted + 1 > leap_month { return Some(unadjusted + 1); } else { diff --git a/components/calendar/src/dangi.rs b/components/calendar/src/dangi.rs index 754b0d38c5c..24678e5f235 100644 --- a/components/calendar/src/dangi.rs +++ b/components/calendar/src/dangi.rs @@ -206,64 +206,7 @@ impl Calendar for Dangi { } fn month(&self, date: &Self::DateInner) -> crate::types::FormattableMonth { - let ordinal = date.0 .0.month; - let leap_month_option = date.0 .0.year_info.leap_month(); - let leap_month = if let Some(leap) = leap_month_option { - leap.get() - } else { - 14 - }; - let code_inner = if leap_month == ordinal { - // Month cannot be 1 because a year cannot have a leap month before the first actual month, - // and the maximum num of months ina leap year is 13. - debug_assert!((2..=13).contains(&ordinal)); - match ordinal { - 2 => tinystr!(4, "M01L"), - 3 => tinystr!(4, "M02L"), - 4 => tinystr!(4, "M03L"), - 5 => tinystr!(4, "M04L"), - 6 => tinystr!(4, "M05L"), - 7 => tinystr!(4, "M06L"), - 8 => tinystr!(4, "M07L"), - 9 => tinystr!(4, "M08L"), - 10 => tinystr!(4, "M09L"), - 11 => tinystr!(4, "M10L"), - 12 => tinystr!(4, "M11L"), - 13 => tinystr!(4, "M12L"), - _ => tinystr!(4, "und"), - } - } else { - let mut adjusted_ordinal = ordinal; - if ordinal > leap_month { - // Before adjusting for leap month, if ordinal > leap_month, - // the month cannot be 1 because this implies the leap month is < 1, which is impossible; - // cannot be 2 because that implies the leap month is = 1, which is impossible, - // and cannot be more than 13 because max number of months in a year is 13. - debug_assert!((2..=13).contains(&ordinal)); - adjusted_ordinal -= 1; - } - debug_assert!((1..=12).contains(&adjusted_ordinal)); - match adjusted_ordinal { - 1 => tinystr!(4, "M01"), - 2 => tinystr!(4, "M02"), - 3 => tinystr!(4, "M03"), - 4 => tinystr!(4, "M04"), - 5 => tinystr!(4, "M05"), - 6 => tinystr!(4, "M06"), - 7 => tinystr!(4, "M07"), - 8 => tinystr!(4, "M08"), - 9 => tinystr!(4, "M09"), - 10 => tinystr!(4, "M10"), - 11 => tinystr!(4, "M11"), - 12 => tinystr!(4, "M12"), - _ => tinystr!(4, "und"), - } - }; - let code = types::MonthCode(code_inner); - types::FormattableMonth { - ordinal: ordinal as u32, - code, - } + date.0.month() } fn day_of_month(&self, date: &Self::DateInner) -> crate::types::DayOfMonth {