use crate::{Calendar, Date, Month, Year, YearMonth};
const YEARS_IN_CYCLE: i32 = 400;
const DAYS_IN_CYCLE: i32 = 146_097;
const DAYS_IN_FIRST_100_YEARS_IN_CYCLE: i32 = 36_524;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Iso;
impl Iso {
#[allow(unsafe_code)]
pub const MAX_YEAR: Year<Self> = unsafe { Year::unchecked_of(Self, 5_000_000) };
#[allow(unsafe_code)]
pub const MIN_YEAR: Year<Self> = unsafe { Year::unchecked_of(Self, -5_000_000) };
}
impl Iso {
#[allow(unsafe_code)]
pub const JANUARY: Month<Self> = unsafe { Month::unchecked_of(Self, 1) };
#[allow(unsafe_code)]
pub const FEBRUARY: Month<Self> = unsafe { Month::unchecked_of(Self, 2) };
#[allow(unsafe_code)]
pub const MARCH: Month<Self> = unsafe { Month::unchecked_of(Self, 3) };
#[allow(unsafe_code)]
pub const APRIL: Month<Self> = unsafe { Month::unchecked_of(Self, 4) };
#[allow(unsafe_code)]
pub const MAY: Month<Self> = unsafe { Month::unchecked_of(Self, 5) };
#[allow(unsafe_code)]
pub const JUNE: Month<Self> = unsafe { Month::unchecked_of(Self, 6) };
#[allow(unsafe_code)]
pub const JULY: Month<Self> = unsafe { Month::unchecked_of(Self, 7) };
#[allow(unsafe_code)]
pub const AUGUST: Month<Self> = unsafe { Month::unchecked_of(Self, 8) };
#[allow(unsafe_code)]
pub const SEPTEMBER: Month<Self> = unsafe { Month::unchecked_of(Self, 9) };
#[allow(unsafe_code)]
pub const OCTOBER: Month<Self> = unsafe { Month::unchecked_of(Self, 10) };
#[allow(unsafe_code)]
pub const NOVEMBER: Month<Self> = unsafe { Month::unchecked_of(Self, 11) };
#[allow(unsafe_code)]
pub const DECEMBER: Month<Self> = unsafe { Month::unchecked_of(Self, 12) };
}
const MAX_DATE_DAYS: i32 = 1_826_212_805;
const MIN_DATE_DAYS: i32 = -1_826_212_560;
impl Iso {
#[allow(unsafe_code)]
pub const MAX_DATE: Date<Self> = unsafe { Date::unchecked_of(Self, MAX_DATE_DAYS) };
#[allow(unsafe_code)]
pub const MIN_DATE: Date<Self> = unsafe { Date::unchecked_of(Self, MIN_DATE_DAYS) };
}
impl Iso {
#[allow(unsafe_code)]
pub(crate) const fn date(self, mut year: i32, month: u8, day: u8) -> crate::Result<Date<Self>> {
ensure_range!(Self::MIN_YEAR.number(), Self::MAX_YEAR.number(), year);
ensure_range!(1, 12, month);
ensure_range!(1, Self.year_month_days(year, month), day);
if month <= 2 {
year -= 1;
}
let cycle = year.div_euclid(YEARS_IN_CYCLE);
let year = year.rem_euclid(YEARS_IN_CYCLE);
let day_of_year = ((153 * (((month as i32) + 9) % 12) + 2) / 5) + (day as i32) - 1;
let day_of_cycle = year * 365 + year / 4 - year / 100 + day_of_year;
let days = cycle * DAYS_IN_CYCLE + day_of_cycle;
ensure_range!(MIN_DATE_DAYS, MAX_DATE_DAYS, days);
Ok(unsafe { Date::unchecked_of(self, days) })
}
#[allow(unsafe_code)]
pub(crate) const fn date_from_ordinal(self, year: i32, day: u16) -> crate::Result<Date<Self>> {
let cycle = year.div_euclid(YEARS_IN_CYCLE);
let year = year.rem_euclid(YEARS_IN_CYCLE);
let day_of_cycle = year * 365 + year / 4 - year / 100 + (day as i32);
let mut days = cycle * DAYS_IN_CYCLE + day_of_cycle;
days -= 61;
ensure_range!(MIN_DATE_DAYS, MAX_DATE_DAYS, days);
Ok(unsafe { Date::unchecked_of(self, days) })
}
pub(crate) const fn date_to_cycle(self, days: i32) -> (i32, i32, i32) {
let cycle = days.div_euclid(DAYS_IN_CYCLE);
let day = days.rem_euclid(DAYS_IN_CYCLE);
let year = (day - day / 1_460 + day / DAYS_IN_FIRST_100_YEARS_IN_CYCLE
- day / (DAYS_IN_CYCLE - 1))
/ 365;
let day = day - CYCLE_DAYS_FROM_YEAR[year as usize];
(cycle, year, day)
}
#[allow(unsafe_code)]
pub(crate) const fn date_components(self, days: i32) -> (Year<Self>, Month<Self>, u8) {
let (cycle, year, day) = self.date_to_cycle(days);
let month = MONTH_FROM_ORDINAL[day as usize] as i32;
let mut year = year + cycle * YEARS_IN_CYCLE;
if month <= 2 {
year += 1;
}
let day = day + YEAR_DAYS_FROM_AKO_MONTH[(month - 1) as usize] as i32;
(
unsafe { Year::unchecked_of(Self, year) },
unsafe { Month::unchecked_of(self, month as u8) },
day as u8,
)
}
#[allow(unsafe_code)]
pub(crate) const fn date_to_ordinal(self, days: i32) -> (Year<Self>, u16) {
let (cycle, year, mut day) = self.date_to_cycle(days);
let month = MONTH_FROM_ORDINAL[day as usize] as i32;
let mut year = year + cycle * YEARS_IN_CYCLE;
if month <= 2 {
year += 1;
day -= 305;
} else {
day += 60;
}
(
unsafe { Year::unchecked_of(Self, year) },
day as u16,
)
}
#[allow(unsafe_code)]
pub(crate) const fn year(self, year: i32) -> crate::Result<Year<Self>> {
ensure_range!(Self::MIN_YEAR.number(), Self::MAX_YEAR.number(), year);
Ok(unsafe { Year::unchecked_of(Self, year) })
}
pub(crate) const fn year_days(self, year: i32) -> u16 {
if Self.year_is_leap(year) { 366 } else { 365 }
}
const fn year_is_leap(self, year: i32) -> bool {
year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
}
pub(crate) const fn year_month(self, year: i32, month: u8) -> crate::Result<YearMonth<Self>> {
ensure_range!(Self::MIN_YEAR.number(), Self::MAX_YEAR.number(), year);
ensure_range!(1, 12, month);
Ok(YearMonth {
calendar: self,
year,
month,
})
}
const fn year_month_days(self, year: i32, month: u8) -> u8 {
if month != 2 || !Self.year_is_leap(year) {
MONTH_LAST_DAY[(month as usize) - 1]
} else {
29
}
}
}
impl Calendar for Iso {
fn date(self, year: i32, month: u8, day: u8) -> crate::Result<Date<Self>> {
Self.date(year, month, day)
}
fn date_from_ordinal(self, year: i32, day: u16) -> crate::Result<Date<Self>> {
Self.date_from_ordinal(year, day)
}
fn date_components(self, days: i32) -> (Year<Self>, Month<Self>, u8) {
Self.date_components(days)
}
fn date_to_ordinal(self, days: i32) -> (Year<Self>, u16) {
Self.date_to_ordinal(days)
}
fn year(self, year: i32) -> crate::Result<Year<Self>> {
Self.year(year)
}
fn year_is_leap(self, year: i32) -> bool {
Self.year_is_leap(year)
}
fn year_days(self, year: i32) -> u16 {
Self.year_days(year)
}
fn year_months(self, _year: i32) -> u8 {
12
}
fn year_month(self, year: i32, month: u8) -> crate::Result<YearMonth<Self>> {
Self.year_month(year, month)
}
fn year_month_days(self, year: i32, month: u8) -> u8 {
Self.year_month_days(year, month)
}
}
const CYCLE_DAYS_FROM_YEAR: [i32; 400] = [
0, 365, 730, 1095, 1461, 1826, 2191, 2556, 2922, 3287, 3652, 4017, 4383, 4748, 5113, 5478,
5844, 6209, 6574, 6939, 7305, 7670, 8035, 8400, 8766, 9131, 9496, 9861, 10227, 10592, 10957,
11322, 11688, 12053, 12418, 12783, 13149, 13514, 13879, 14244, 14610, 14975, 15340, 15705,
16071, 16436, 16801, 17166, 17532, 17897, 18262, 18627, 18993, 19358, 19723, 20088, 20454,
20819, 21184, 21549, 21915, 22280, 22645, 23010, 23376, 23741, 24106, 24471, 24837, 25202,
25567, 25932, 26298, 26663, 27028, 27393, 27759, 28124, 28489, 28854, 29220, 29585, 29950,
30315, 30681, 31046, 31411, 31776, 32142, 32507, 32872, 33237, 33603, 33968, 34333, 34698,
35064, 35429, 35794, 36159, 36524, 36889, 37254, 37619, 37985, 38350, 38715, 39080, 39446,
39811, 40176, 40541, 40907, 41272, 41637, 42002, 42368, 42733, 43098, 43463, 43829, 44194,
44559, 44924, 45290, 45655, 46020, 46385, 46751, 47116, 47481, 47846, 48212, 48577, 48942,
49307, 49673, 50038, 50403, 50768, 51134, 51499, 51864, 52229, 52595, 52960, 53325, 53690,
54056, 54421, 54786, 55151, 55517, 55882, 56247, 56612, 56978, 57343, 57708, 58073, 58439,
58804, 59169, 59534, 59900, 60265, 60630, 60995, 61361, 61726, 62091, 62456, 62822, 63187,
63552, 63917, 64283, 64648, 65013, 65378, 65744, 66109, 66474, 66839, 67205, 67570, 67935,
68300, 68666, 69031, 69396, 69761, 70127, 70492, 70857, 71222, 71588, 71953, 72318, 72683,
73048, 73413, 73778, 74143, 74509, 74874, 75239, 75604, 75970, 76335, 76700, 77065, 77431,
77796, 78161, 78526, 78892, 79257, 79622, 79987, 80353, 80718, 81083, 81448, 81814, 82179,
82544, 82909, 83275, 83640, 84005, 84370, 84736, 85101, 85466, 85831, 86197, 86562, 86927,
87292, 87658, 88023, 88388, 88753, 89119, 89484, 89849, 90214, 90580, 90945, 91310, 91675,
92041, 92406, 92771, 93136, 93502, 93867, 94232, 94597, 94963, 95328, 95693, 96058, 96424,
96789, 97154, 97519, 97885, 98250, 98615, 98980, 99346, 99711, 100076, 100441, 100807, 101172,
101537, 101902, 102268, 102633, 102998, 103363, 103729, 104094, 104459, 104824, 105190, 105555,
105920, 106285, 106651, 107016, 107381, 107746, 108112, 108477, 108842, 109207, 109572, 109937,
110302, 110667, 111033, 111398, 111763, 112128, 112494, 112859, 113224, 113589, 113955, 114320,
114685, 115050, 115416, 115781, 116146, 116511, 116877, 117242, 117607, 117972, 118338, 118703,
119068, 119433, 119799, 120164, 120529, 120894, 121260, 121625, 121990, 122355, 122721, 123086,
123451, 123816, 124182, 124547, 124912, 125277, 125643, 126008, 126373, 126738, 127104, 127469,
127834, 128199, 128565, 128930, 129295, 129660, 130026, 130391, 130756, 131121, 131487, 131852,
132217, 132582, 132948, 133313, 133678, 134043, 134409, 134774, 135139, 135504, 135870, 136235,
136600, 136965, 137331, 137696, 138061, 138426, 138792, 139157, 139522, 139887, 140253, 140618,
140983, 141348, 141714, 142079, 142444, 142809, 143175, 143540, 143905, 144270, 144636, 145001,
145366, 145731,
];
const MONTH_FROM_ORDINAL: [u8; 366] = [
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
];
const YEAR_DAYS_FROM_AKO_MONTH: [i16; 12] = [
-305, -336, 1, -30, -60, -91, -121, -152, -183, -213, -244, -274,
];
const MONTH_LAST_DAY: [u8; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];