#[doc(no_inline)]
pub use calendrical_calculations::rata_die::RataDie;
use core::fmt;
use tinystr::TinyAsciiStr;
use tinystr::{TinyStr16, TinyStr4};
use zerovec::maps::ZeroMapKV;
use zerovec::ule::AsULE;
#[derive(Copy, Clone, Debug, PartialEq)]
#[non_exhaustive]
pub enum YearInfo {
Era(EraYear),
Cyclic(CyclicYear),
}
impl From<EraYear> for YearInfo {
fn from(value: EraYear) -> Self {
Self::Era(value)
}
}
impl From<CyclicYear> for YearInfo {
fn from(value: CyclicYear) -> Self {
Self::Cyclic(value)
}
}
impl YearInfo {
pub fn era_year_or_related_iso(self) -> i32 {
match self {
YearInfo::Era(e) => e.year,
YearInfo::Cyclic(c) => c.related_iso,
}
}
pub fn era(self) -> Option<EraYear> {
match self {
Self::Era(e) => Some(e),
Self::Cyclic(_) => None,
}
}
pub fn cyclic(self) -> Option<CyclicYear> {
match self {
Self::Era(_) => None,
Self::Cyclic(c) => Some(c),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[allow(clippy::exhaustive_enums)] pub enum YearAmbiguity {
Unambiguous,
CenturyRequired,
EraRequired,
EraAndCenturyRequired,
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[non_exhaustive]
pub struct EraYear {
pub year: i32,
pub era: TinyStr16,
pub era_index: Option<u8>,
pub ambiguity: YearAmbiguity,
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[non_exhaustive]
pub struct CyclicYear {
pub year: u8,
pub related_iso: i32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[allow(clippy::exhaustive_structs)] #[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
#[cfg_attr(feature = "datagen", databake(path = icu_calendar::types))]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
pub struct MonthCode(pub TinyStr4);
impl MonthCode {
pub fn get_normal_if_leap(self) -> Option<MonthCode> {
let bytes = self.0.all_bytes();
if bytes[3] == b'L' {
Some(MonthCode(TinyAsciiStr::try_from_utf8(&bytes[0..3]).ok()?))
} else {
None
}
}
pub fn parsed(self) -> Option<(u8, bool)> {
let bytes = self.0.all_bytes();
let is_leap = bytes[3] == b'L';
if bytes[0] != b'M' {
return None;
}
if bytes[1] == b'0' {
if bytes[2] >= b'1' && bytes[2] <= b'9' {
return Some((bytes[2] - b'0', is_leap));
}
} else if bytes[1] == b'1' && bytes[2] >= b'0' && bytes[2] <= b'3' {
return Some((10 + bytes[2] - b'0', is_leap));
}
None
}
pub fn new_normal(number: u8) -> Option<Self> {
let tens = number / 10;
let ones = number % 10;
if tens > 9 {
return None;
}
let bytes = [b'M', b'0' + tens, b'0' + ones, 0];
Some(MonthCode(TinyAsciiStr::try_from_raw(bytes).ok()?))
}
}
#[test]
fn test_get_normal_month_code_if_leap() {
let mc1 = MonthCode(tinystr::tinystr!(4, "M01L"));
let result1 = mc1.get_normal_if_leap();
assert_eq!(result1, Some(MonthCode(tinystr::tinystr!(4, "M01"))));
let mc2 = MonthCode(tinystr::tinystr!(4, "M11L"));
let result2 = mc2.get_normal_if_leap();
assert_eq!(result2, Some(MonthCode(tinystr::tinystr!(4, "M11"))));
let mc_invalid = MonthCode(tinystr::tinystr!(4, "M10"));
let result_invalid = mc_invalid.get_normal_if_leap();
assert_eq!(result_invalid, None);
}
impl AsULE for MonthCode {
type ULE = TinyStr4;
fn to_unaligned(self) -> TinyStr4 {
self.0
}
fn from_unaligned(u: TinyStr4) -> Self {
Self(u)
}
}
impl<'a> ZeroMapKV<'a> for MonthCode {
type Container = zerovec::ZeroVec<'a, MonthCode>;
type Slice = zerovec::ZeroSlice<MonthCode>;
type GetType = <MonthCode as AsULE>::ULE;
type OwnedType = MonthCode;
}
impl fmt::Display for MonthCode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[non_exhaustive]
pub struct MonthInfo {
pub ordinal: u8,
pub standard_code: MonthCode,
pub formatting_code: MonthCode,
}
impl MonthInfo {
pub fn month_number(self) -> u8 {
self.standard_code
.parsed()
.map(|(i, _)| i)
.unwrap_or(self.ordinal)
}
pub fn is_leap(self) -> bool {
self.standard_code.parsed().map(|(_, l)| l).unwrap_or(false)
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[allow(clippy::exhaustive_structs)] pub struct DayOfYear(pub u16);
#[allow(clippy::exhaustive_structs)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct DayOfMonth(pub u8);
#[derive(Clone, Copy, Debug, PartialEq)]
#[allow(clippy::exhaustive_structs)] pub struct IsoWeekOfYear {
pub week_number: u8,
pub iso_year: i32,
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[allow(clippy::exhaustive_structs)] pub struct DayOfWeekInMonth(pub u8);
impl From<DayOfMonth> for DayOfWeekInMonth {
fn from(day_of_month: DayOfMonth) -> Self {
DayOfWeekInMonth(1 + ((day_of_month.0 - 1) / 7))
}
}
#[test]
fn test_day_of_week_in_month() {
assert_eq!(DayOfWeekInMonth::from(DayOfMonth(1)).0, 1);
assert_eq!(DayOfWeekInMonth::from(DayOfMonth(7)).0, 1);
assert_eq!(DayOfWeekInMonth::from(DayOfMonth(8)).0, 2);
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[allow(missing_docs)] #[repr(i8)]
#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
#[cfg_attr(feature = "datagen", databake(path = icu_calendar::types))]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
#[allow(clippy::exhaustive_enums)] pub enum Weekday {
Monday = 1,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday,
}
const SUNDAY: RataDie = RataDie::new(0);
impl From<RataDie> for Weekday {
fn from(value: RataDie) -> Self {
use Weekday::*;
match (value - SUNDAY).rem_euclid(7) {
0 => Sunday,
1 => Monday,
2 => Tuesday,
3 => Wednesday,
4 => Thursday,
5 => Friday,
6 => Saturday,
_ => unreachable!(),
}
}
}
impl Weekday {
pub fn from_days_since_sunday(input: isize) -> Self {
(SUNDAY + input as i64).into()
}
pub(crate) fn next_day(self) -> Weekday {
use Weekday::*;
match self {
Monday => Tuesday,
Tuesday => Wednesday,
Wednesday => Thursday,
Thursday => Friday,
Friday => Saturday,
Saturday => Sunday,
Sunday => Monday,
}
}
}