Skip to content

Commit dcee50a

Browse files
General HijriTabular constructor (#6406)
#6330 #6397
1 parent 087343d commit dcee50a

File tree

19 files changed

+314
-241
lines changed

19 files changed

+314
-241
lines changed

components/calendar/benches/convert.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,10 @@ fn convert_benches(c: &mut Criterion) {
7878
bench_calendar(
7979
&mut group,
8080
"calendar/islamic/civil",
81-
icu::calendar::cal::HijriTabular::new_civil_epoch(),
81+
icu::calendar::cal::HijriTabular::new(
82+
icu::calendar::cal::HijriTabularEpoch::Friday,
83+
icu::calendar::cal::HijriTabularLeapYears::TypeII,
84+
),
8285
);
8386

8487
bench_calendar(
@@ -90,7 +93,10 @@ fn convert_benches(c: &mut Criterion) {
9093
bench_calendar(
9194
&mut group,
9295
"calendar/islamic/tabular",
93-
icu::calendar::cal::HijriTabular::new_astronomical_epoch(),
96+
icu::calendar::cal::HijriTabular::new(
97+
icu::calendar::cal::HijriTabularEpoch::Thursday,
98+
icu::calendar::cal::HijriTabularLeapYears::TypeII,
99+
),
94100
);
95101

96102
group.finish();

components/calendar/benches/date.rs

+16-4
Original file line numberDiff line numberDiff line change
@@ -197,13 +197,19 @@ fn date_benches(c: &mut Criterion) {
197197
&mut group,
198198
"calendar/islamic/civil",
199199
&fxs,
200-
icu::calendar::cal::HijriTabular::new_civil_epoch(),
200+
icu::calendar::cal::HijriTabular::new(
201+
icu::calendar::cal::HijriTabularEpoch::Friday,
202+
icu::calendar::cal::HijriTabularLeapYears::TypeII,
203+
),
201204
|y, m, d| {
202205
Date::try_new_hijri_tabular_with_calendar(
203206
y,
204207
m,
205208
d,
206-
icu::calendar::cal::HijriTabular::new_civil_epoch(),
209+
icu::calendar::cal::HijriTabular::new(
210+
icu::calendar::cal::HijriTabularEpoch::Friday,
211+
icu::calendar::cal::HijriTabularLeapYears::TypeII,
212+
),
207213
)
208214
.unwrap()
209215
},
@@ -213,13 +219,19 @@ fn date_benches(c: &mut Criterion) {
213219
&mut group,
214220
"calendar/islamic/tabular",
215221
&fxs,
216-
icu::calendar::cal::HijriTabular::new_astronomical_epoch(),
222+
icu::calendar::cal::HijriTabular::new(
223+
icu::calendar::cal::HijriTabularEpoch::Thursday,
224+
icu::calendar::cal::HijriTabularLeapYears::TypeII,
225+
),
217226
|y, m, d| {
218227
Date::try_new_hijri_tabular_with_calendar(
219228
y,
220229
m,
221230
d,
222-
icu::calendar::cal::HijriTabular::new_astronomical_epoch(),
231+
icu::calendar::cal::HijriTabular::new(
232+
icu::calendar::cal::HijriTabularEpoch::Thursday,
233+
icu::calendar::cal::HijriTabularLeapYears::TypeII,
234+
),
223235
)
224236
.unwrap()
225237
},

components/calendar/src/any_calendar.rs

+129-109
Large diffs are not rendered by default.

components/calendar/src/cal/hijri.rs

+74-39
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,11 @@ pub struct HijriUmmAlQura {
9595
#[derive(Debug, Clone, Copy)]
9696
pub struct HijriUmmAlQuraMarker;
9797

98-
/// The [tabular Hijri Calendar](https://en.wikipedia.org/wiki/Tabular_Islamic_calendar) (astronomical epoch)
98+
/// The [tabular Hijri Calendar](https://en.wikipedia.org/wiki/Tabular_Islamic_calendar).
9999
///
100-
/// This is a tabular/arithmetic Hijri calendar with leap years (1-based) 2, 5, 7, 10,
101-
/// 13, 16, 18, 21, 24, 26, and 29 in the 30-year cycle.
100+
/// See [`HijriTabularEpoch`] and [`HijriTabularLeapYears`] for customization.
101+
///
102+
/// The most common version of this calendar uses [`HijriTabularEpoch::Friday`] and [`HijriTabularLeapYears::TypeII`].
102103
///
103104
/// # Era codes
104105
///
@@ -111,7 +112,10 @@ pub struct HijriUmmAlQuraMarker;
111112
/// This calendar is a pure lunar calendar with no leap months. It uses month codes
112113
/// `"M01" - "M12"`.
113114
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
114-
pub struct HijriTabular(pub(crate) RataDie);
115+
pub struct HijriTabular {
116+
pub(crate) epoch: HijriTabularEpoch,
117+
pub(crate) leap_years: HijriTabularLeapYears,
118+
}
115119

116120
impl HijriSimulated {
117121
/// Creates a new [`HijriSimulated`] for reference location Mecca, with some compiled data containing precomputed calendrical calculations.
@@ -226,21 +230,40 @@ impl HijriUmmAlQura {
226230
}
227231
}
228232

229-
impl HijriTabular {
230-
/// Construct a new [`HijriTabular`] with the civil/Friday epoch. That is,
231-
/// the year 1 starts on Friday July 16, 622 AD (0622-07-19 ISO).
232-
///
233-
/// This is the most common version of the tabular Hijri calendar.
234-
pub const fn new_civil_epoch() -> Self {
235-
Self(ISLAMIC_EPOCH_FRIDAY)
233+
/// The epoch for the [`HijriTabular`] calendar.
234+
#[non_exhaustive]
235+
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
236+
pub enum HijriTabularEpoch {
237+
/// Thusday July 15, 622 AD (0622-07-18 ISO)
238+
Thursday,
239+
/// Friday July 16, 622 AD (0622-07-19 ISO)
240+
Friday,
241+
}
242+
243+
impl HijriTabularEpoch {
244+
fn rata_die(self) -> RataDie {
245+
match self {
246+
Self::Thursday => ISLAMIC_EPOCH_THURSDAY,
247+
Self::Friday => ISLAMIC_EPOCH_FRIDAY,
248+
}
236249
}
250+
}
237251

238-
/// Construct a new [`HijriTabular`] with the astronomical/Thursday epoch.
239-
/// That is, the AH era starts on Thusday July 15, 622 AD (0622-07-18 ISO).
240-
///
241-
/// This version of the calendar is also known as the "Microsoft Kuwaiti calendar".
242-
pub const fn new_astronomical_epoch() -> Self {
243-
Self(ISLAMIC_EPOCH_THURSDAY)
252+
/// The leap year rule for the [`HijriTabular`] calendar.
253+
///
254+
/// This specifies which years of a 30-year cycle have an additional day at
255+
/// the end of the year.
256+
#[non_exhaustive]
257+
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
258+
pub enum HijriTabularLeapYears {
259+
/// Leap years 2, 5, 7, 10, 13, 16, 18, 21, 24, 26, 29
260+
TypeII,
261+
}
262+
263+
impl HijriTabular {
264+
/// Construct a new [`HijriTabular`] with the given epoch and leap year rule.
265+
pub const fn new(epoch: HijriTabularEpoch, leap_years: HijriTabularLeapYears) -> Self {
266+
Self { epoch, leap_years }
244267
}
245268
}
246269

@@ -960,28 +983,39 @@ impl Calendar for HijriTabular {
960983
) -> Result<Self::DateInner, DateError> {
961984
let year = match era {
962985
Some("ah") | None => year,
963-
Some("islamic-civil" | "islamicc") if self.0 == ISLAMIC_EPOCH_FRIDAY => year,
964-
Some("islamic-tbla") if self.0 == ISLAMIC_EPOCH_THURSDAY => year,
986+
Some("islamic-civil" | "islamicc") if self.epoch == HijriTabularEpoch::Friday => year,
987+
Some("islamic-tbla") if self.epoch == HijriTabularEpoch::Thursday => year,
965988
Some(_) => return Err(DateError::UnknownEra),
966989
};
967990

968991
ArithmeticDate::new_from_codes(self, year, month_code, day).map(HijriTabularDateInner)
969992
}
970993

971994
fn from_rata_die(&self, rd: RataDie) -> Self::DateInner {
972-
let (y, m, d) = calendrical_calculations::islamic::tabular_islamic_from_fixed(rd, self.0);
995+
let (y, m, d) = match self.leap_years {
996+
HijriTabularLeapYears::TypeII => {
997+
calendrical_calculations::islamic::tabular_islamic_from_fixed(
998+
rd,
999+
self.epoch.rata_die(),
1000+
)
1001+
}
1002+
};
9731003

9741004
debug_assert!(Date::try_new_hijri_tabular_with_calendar(y, m, d, crate::Ref(self)).is_ok());
9751005
HijriTabularDateInner(ArithmeticDate::new_unchecked(y, m, d))
9761006
}
9771007

9781008
fn to_rata_die(&self, date: &Self::DateInner) -> RataDie {
979-
calendrical_calculations::islamic::fixed_from_tabular_islamic(
980-
date.0.year,
981-
date.0.month,
982-
date.0.day,
983-
self.0,
984-
)
1009+
match self.leap_years {
1010+
HijriTabularLeapYears::TypeII => {
1011+
calendrical_calculations::islamic::fixed_from_tabular_islamic(
1012+
date.0.year,
1013+
date.0.month,
1014+
date.0.day,
1015+
self.epoch.rata_die(),
1016+
)
1017+
}
1018+
}
9851019
}
9861020

9871021
fn from_iso(&self, iso: IsoDateInner) -> Self::DateInner {
@@ -1020,18 +1054,17 @@ impl Calendar for HijriTabular {
10201054
}
10211055

10221056
fn debug_name(&self) -> &'static str {
1023-
match self.0 {
1024-
ISLAMIC_EPOCH_FRIDAY => "Hijri (civil)",
1025-
_ => "Hijri (astronomical)",
1057+
match self.epoch {
1058+
HijriTabularEpoch::Friday => "Hijri (civil)",
1059+
HijriTabularEpoch::Thursday => "Hijri (astronomical)",
10261060
}
10271061
}
10281062

10291063
fn year(&self, date: &Self::DateInner) -> types::YearInfo {
10301064
year_as_hijri(
1031-
if self.0 == ISLAMIC_EPOCH_FRIDAY {
1032-
tinystr!(16, "islamic-civil")
1033-
} else {
1034-
tinystr!(16, "islamic-tbla")
1065+
match self.epoch {
1066+
HijriTabularEpoch::Friday => tinystr!(16, "islamic-civil"),
1067+
HijriTabularEpoch::Thursday => tinystr!(16, "islamic-tbla"),
10351068
},
10361069
date.0.year,
10371070
)
@@ -1064,10 +1097,10 @@ impl<A: AsCalendar<Calendar = HijriTabular>> Date<A> {
10641097
/// Has no negative years, only era is the AH.
10651098
///
10661099
/// ```rust
1067-
/// use icu::calendar::cal::HijriTabular;
1100+
/// use icu::calendar::cal::{HijriTabular, HijriTabularEpoch, HijriTabularLeapYears};
10681101
/// use icu::calendar::Date;
10691102
///
1070-
/// let hijri = HijriTabular::new_civil_epoch();
1103+
/// let hijri = HijriTabular::new(HijriTabularEpoch::Thursday, HijriTabularLeapYears::TypeII);
10711104
///
10721105
/// let date_hijri =
10731106
/// Date::try_new_hijri_tabular_with_calendar(1392, 4, 25, hijri)
@@ -1814,7 +1847,7 @@ mod test {
18141847

18151848
#[test]
18161849
fn test_rd_from_hijri() {
1817-
let calendar = HijriTabular::new_civil_epoch();
1850+
let calendar = HijriTabular::new(HijriTabularEpoch::Friday, HijriTabularLeapYears::TypeII);
18181851
let calendar = Ref(&calendar);
18191852
for (case, f_date) in ARITHMETIC_CASES.iter().zip(TEST_RD.iter()) {
18201853
let date = Date::try_new_hijri_tabular_with_calendar(
@@ -1827,7 +1860,7 @@ mod test {
18271860

18281861
#[test]
18291862
fn test_hijri_from_rd() {
1830-
let calendar = HijriTabular::new_civil_epoch();
1863+
let calendar = HijriTabular::new(HijriTabularEpoch::Friday, HijriTabularLeapYears::TypeII);
18311864
let calendar = Ref(&calendar);
18321865
for (case, f_date) in ARITHMETIC_CASES.iter().zip(TEST_RD.iter()) {
18331866
let date = Date::try_new_hijri_tabular_with_calendar(
@@ -1842,7 +1875,8 @@ mod test {
18421875

18431876
#[test]
18441877
fn test_rd_from_hijri_tbla() {
1845-
let calendar = HijriTabular::new_astronomical_epoch();
1878+
let calendar =
1879+
HijriTabular::new(HijriTabularEpoch::Thursday, HijriTabularLeapYears::TypeII);
18461880
let calendar = Ref(&calendar);
18471881
for (case, f_date) in ASTRONOMICAL_CASES.iter().zip(TEST_RD.iter()) {
18481882
let date = Date::try_new_hijri_tabular_with_calendar(
@@ -1855,7 +1889,8 @@ mod test {
18551889

18561890
#[test]
18571891
fn test_hijri_tbla_from_rd() {
1858-
let calendar = HijriTabular::new_astronomical_epoch();
1892+
let calendar =
1893+
HijriTabular::new(HijriTabularEpoch::Thursday, HijriTabularLeapYears::TypeII);
18591894
let calendar = Ref(&calendar);
18601895
for (case, f_date) in ASTRONOMICAL_CASES.iter().zip(TEST_RD.iter()) {
18611896
let date = Date::try_new_hijri_tabular_with_calendar(

components/calendar/src/cal/mod.rs

+4-21
Original file line numberDiff line numberDiff line change
@@ -26,31 +26,14 @@ pub use dangi::Dangi;
2626
pub use ethiopian::{Ethiopian, EthiopianEraStyle};
2727
pub use gregorian::Gregorian;
2828
pub use hebrew::Hebrew;
29-
pub use hijri::{HijriSimulated, HijriTabular, HijriUmmAlQura};
29+
pub use hijri::{
30+
HijriSimulated, HijriTabular, HijriTabularEpoch, HijriTabularLeapYears, HijriUmmAlQura,
31+
};
3032
pub use indian::Indian;
3133
pub use iso::Iso;
3234
pub use japanese::{Japanese, JapaneseExtended};
3335
pub use julian::Julian;
3436
pub use persian::Persian;
3537
pub use roc::Roc;
3638

37-
pub use crate::any_calendar::AnyCalendar;
38-
39-
/// Scaffolding types: You shouldn't need to use these, they need to be public for the `Calendar` trait impl to work.
40-
pub mod scaffold {
41-
pub use super::chinese::ChineseDateInner;
42-
pub use super::coptic::CopticDateInner;
43-
pub use super::dangi::DangiDateInner;
44-
pub use super::ethiopian::EthiopianDateInner;
45-
pub use super::gregorian::GregorianDateInner;
46-
pub use super::hebrew::HebrewDateInner;
47-
pub use super::hijri::{HijriDateInner, HijriTabularDateInner, HijriUmmAlQuraDateInner};
48-
pub use super::indian::Indian;
49-
pub use super::iso::Iso;
50-
pub use super::japanese::Japanese;
51-
pub use super::julian::JulianDateInner;
52-
pub use super::persian::PersianDateInner;
53-
pub use super::roc::RocDateInner;
54-
55-
pub use crate::any_calendar::AnyDateInner;
56-
}
39+
pub use crate::any_calendar::{AnyCalendar, AnyCalendarKind};

components/calendar/src/lib.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ extern crate alloc;
9696
mod date;
9797

9898
// Public modules
99-
pub mod any_calendar;
99+
mod any_calendar;
100100
pub mod cal;
101101
pub mod provider;
102102
pub mod types;
@@ -110,6 +110,7 @@ mod error;
110110
mod ixdtf;
111111

112112
// Top-level types
113+
pub use any_calendar::IntoAnyCalendar;
113114
pub use calendar::Calendar;
114115
pub use date::{AsCalendar, Date, Ref};
115116
#[doc(hidden)] // unstable
@@ -120,12 +121,11 @@ pub use ixdtf::ParseError;
120121

121122
// Reexports
122123
#[doc(no_inline)]
123-
pub use any_calendar::{AnyCalendar, AnyCalendarKind, CalendarPreferences};
124-
#[doc(no_inline)]
125-
pub use cal::{Gregorian, Iso};
124+
pub use cal::{AnyCalendar, AnyCalendarKind, Gregorian, Iso};
126125

127126
/// Locale preferences used by this crate
128127
pub mod preferences {
128+
pub use crate::any_calendar::CalendarPreferences;
129129
#[doc(inline)]
130130
/// **This is a reexport of a type in [`icu::locale`](icu_locale_core::preferences::extensions::unicode::keywords)**.
131131
#[doc = "\n"] // prevent autoformatting

components/calendar/src/tests/continuity_test.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,10 @@ fn test_indian_continuity() {
147147

148148
#[test]
149149
fn test_hijri_civil_continuity() {
150-
let cal = crate::cal::HijriTabular::new_civil_epoch();
150+
let cal = crate::cal::HijriTabular::new(
151+
crate::cal::HijriTabularEpoch::Friday,
152+
crate::cal::HijriTabularLeapYears::TypeII,
153+
);
151154
let cal = Ref(&cal);
152155
let date = Date::try_new_hijri_tabular_with_calendar(-10, 1, 1, cal);
153156
check_continuity(date.unwrap());
@@ -169,7 +172,10 @@ fn test_hijri_simulated_mecca_continuity() {
169172

170173
#[test]
171174
fn test_hijri_tabular_continuity() {
172-
let cal = crate::cal::HijriTabular::new_astronomical_epoch();
175+
let cal = crate::cal::HijriTabular::new(
176+
crate::cal::HijriTabularEpoch::Thursday,
177+
crate::cal::HijriTabularLeapYears::TypeII,
178+
);
173179
let cal = Ref(&cal);
174180
let date = Date::try_new_hijri_tabular_with_calendar(-10, 1, 1, cal);
175181
check_continuity(date.unwrap());

components/datetime/src/error.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
use crate::pattern::PatternLoadError;
66
use displaydoc::Display;
77
use icu_calendar::{
8-
any_calendar::AnyCalendarKind,
98
types::{FormattingEra, MonthCode},
9+
AnyCalendarKind,
1010
};
1111
use icu_provider::DataError;
1212

components/datetime/src/neo.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ use crate::MismatchedCalendarError;
2222
use crate::{external_loaders::*, DateTimeWriteError};
2323
use core::fmt;
2424
use core::marker::PhantomData;
25-
use icu_calendar::any_calendar::IntoAnyCalendar;
26-
use icu_calendar::{AnyCalendar, CalendarPreferences};
25+
use icu_calendar::{preferences::CalendarPreferences, AnyCalendar, IntoAnyCalendar};
2726
use icu_decimal::DecimalFormatterPreferences;
2827
use icu_locale_core::preferences::{define_preferences, prefs_convert};
2928
use icu_provider::prelude::*;

0 commit comments

Comments
 (0)