icu_calendar/types.rs
1// This file is part of ICU4X. For terms of use, please see the file
2// called LICENSE at the top level of the ICU4X source tree
3// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4
5//! This module contains various types used by `icu::calendar` and `icu::datetime`
6
7#[doc(no_inline)]
8pub use calendrical_calculations::rata_die::RataDie;
9use core::fmt;
10use tinystr::TinyAsciiStr;
11use zerovec::ule::AsULE;
12
13// Export the duration types from here
14#[cfg(feature = "unstable")]
15pub use crate::duration::{DateDuration, DateDurationUnit};
16use crate::error::MonthCodeParseError;
17
18#[cfg(feature = "unstable")]
19pub use unstable::DateFields;
20#[cfg(not(feature = "unstable"))]
21pub(crate) use unstable::DateFields;
22
23mod unstable {
24 /// A bag of various ways of expressing the year, month, and/or day.
25 ///
26 /// Pass this into [`Date::try_from_fields`](crate::Date::try_from_fields).
27 ///
28 /// <div class="stab unstable">
29 /// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
30 /// including in SemVer minor releases. Do not use this type unless you are prepared for things to occasionally break.
31 ///
32 /// Graduation tracking issue: [issue #7161](https://github.com/unicode-org/icu4x/issues/7161).
33 /// </div>
34 ///
35 /// ✨ *Enabled with the `unstable` Cargo feature.*
36 #[derive(Copy, Clone, PartialEq, Default)]
37 #[non_exhaustive]
38 pub struct DateFields<'a> {
39 /// The era code as a UTF-8 string.
40 ///
41 /// The acceptable codes are defined by CLDR and documented on each calendar.
42 ///
43 /// If set, [`Self::era_year`] must also be set.
44 ///
45 /// # Examples
46 ///
47 /// To set the era field, use a byte string:
48 ///
49 /// ```
50 /// use icu::calendar::types::DateFields;
51 ///
52 /// let mut fields = DateFields::default();
53 ///
54 /// // As a byte string literal:
55 /// fields.era = Some(b"reiwa");
56 ///
57 /// // Using str::as_bytes:
58 /// fields.era = Some("reiwa".as_bytes());
59 /// ```
60 ///
61 /// For a full example, see [`Self::extended_year`].
62 pub era: Option<&'a [u8]>,
63 /// The numeric year in [`Self::era`].
64 ///
65 /// If set, [`Self::era`] must also be set.
66 ///
67 /// For an example, see [`Self::extended_year`].
68 pub era_year: Option<i32>,
69 /// See [`Date::extended_year()`](crate::Date::extended_year).
70 ///
71 /// If both this and [`Self::era`]/[`Self::era_year`] are set, they must
72 /// refer to the same year.
73 ///
74 /// # Examples
75 ///
76 /// Either `extended_year` or `era` + `era_year` can be used in DateFields:
77 ///
78 /// ```
79 /// use icu::calendar::cal::Japanese;
80 /// use icu::calendar::types::DateFields;
81 /// use icu::calendar::Date;
82 ///
83 /// let mut fields1 = DateFields::default();
84 /// fields1.era = Some(b"reiwa");
85 /// fields1.era_year = Some(7);
86 /// fields1.ordinal_month = Some(1);
87 /// fields1.day = Some(1);
88 ///
89 /// let date1 =
90 /// Date::try_from_fields(fields1, Default::default(), Japanese::new())
91 /// .expect("a well-defined Japanese date from era year");
92 ///
93 /// let mut fields2 = DateFields::default();
94 /// fields2.extended_year = Some(2025);
95 /// fields2.ordinal_month = Some(1);
96 /// fields2.day = Some(1);
97 ///
98 /// let date2 =
99 /// Date::try_from_fields(fields2, Default::default(), Japanese::new())
100 /// .expect("a well-defined Japanese date from extended year");
101 ///
102 /// assert_eq!(date1, date2);
103 ///
104 /// let year_info = date1.year().era().unwrap();
105 /// assert_eq!(year_info.year, 7);
106 /// assert_eq!(year_info.era.as_str(), "reiwa");
107 /// assert_eq!(year_info.extended_year, 2025);
108 /// ```
109 pub extended_year: Option<i32>,
110 /// The month code representing a valid month in this calendar year,
111 /// as a UTF-8 string.
112 ///
113 /// See [`MonthCode`](crate::types::MonthCode) for information on the syntax.
114 ///
115 /// # Examples
116 ///
117 /// To set the month code field, use a byte string:
118 ///
119 /// ```
120 /// use icu::calendar::types::DateFields;
121 ///
122 /// let mut fields = DateFields::default();
123 ///
124 /// // As a byte string literal:
125 /// fields.era = Some(b"M02L");
126 ///
127 /// // Using str::as_bytes:
128 /// fields.era = Some("M02L".as_bytes());
129 /// ```
130 ///
131 /// For a full example, see [`Self::ordinal_month`].
132 pub month_code: Option<&'a [u8]>,
133 /// See [`MonthInfo::ordinal`](crate::types::MonthInfo::ordinal).
134 ///
135 /// If both this and [`Self::month_code`] are set, they must refer to
136 /// the same month.
137 ///
138 /// Note: using [`Self::month_code`] is recommended, because the ordinal month numbers
139 /// can vary from year to year, as illustrated in the following example.
140 ///
141 /// # Examples
142 ///
143 /// Either `month_code` or `ordinal_month` can be used in DateFields, but they
144 /// might not resolve to the same month number:
145 ///
146 /// ```
147 /// use icu::calendar::cal::ChineseTraditional;
148 /// use icu::calendar::types::DateFields;
149 /// use icu::calendar::Date;
150 ///
151 /// // The 2023 Year of the Rabbit had a leap month after the 2nd month.
152 /// let mut fields1 = DateFields::default();
153 /// fields1.extended_year = Some(2023);
154 /// fields1.month_code = Some(b"M02L");
155 /// fields1.day = Some(1);
156 ///
157 /// let date1 = Date::try_from_fields(
158 /// fields1,
159 /// Default::default(),
160 /// ChineseTraditional::new(),
161 /// )
162 /// .expect("a well-defined Chinese date from month code");
163 ///
164 /// let mut fields2 = DateFields::default();
165 /// fields2.extended_year = Some(2023);
166 /// fields2.ordinal_month = Some(3);
167 /// fields2.day = Some(1);
168 ///
169 /// let date2 = Date::try_from_fields(
170 /// fields2,
171 /// Default::default(),
172 /// ChineseTraditional::new(),
173 /// )
174 /// .expect("a well-defined Chinese date from ordinal month");
175 ///
176 /// assert_eq!(date1, date2);
177 ///
178 /// let month_info = date1.month();
179 /// assert_eq!(month_info.ordinal, 3);
180 /// assert_eq!(month_info.standard_code.0, "M02L");
181 /// ```
182 pub ordinal_month: Option<u8>,
183 /// See [`DayOfMonth`](crate::types::DayOfMonth).
184 pub day: Option<u8>,
185 }
186}
187
188// Custom impl to stringify era and month_code where possible.
189impl fmt::Debug for DateFields<'_> {
190 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
191 // Ensures we catch future fields
192 let Self {
193 era,
194 era_year,
195 extended_year,
196 month_code,
197 ordinal_month,
198 day,
199 } = *self;
200 let mut builder = f.debug_struct("DateFields");
201 if let Some(s) = era.and_then(|s| core::str::from_utf8(s).ok()) {
202 builder.field("era", &Some(s));
203 } else {
204 builder.field("era", &era);
205 }
206 builder.field("era_year", &era_year);
207 builder.field("extended_year", &extended_year);
208 if let Some(s) = month_code.and_then(|s| core::str::from_utf8(s).ok()) {
209 builder.field("month_code", &Some(s));
210 } else {
211 builder.field("month_code", &month_code);
212 }
213 builder.field("ordinal_month", &ordinal_month);
214 builder.field("day", &day);
215 builder.finish()
216 }
217}
218
219/// The type of year: Calendars like Chinese don't have an era and instead format with cyclic years.
220#[derive(Copy, Clone, Debug, PartialEq)]
221#[non_exhaustive]
222pub enum YearInfo {
223 /// An era and a year in that era
224 Era(EraYear),
225 /// A cyclic year, and the related ISO year
226 ///
227 /// Knowing the cyclic year is typically not enough to pinpoint a date, however cyclic calendars
228 /// don't typically use eras, so disambiguation can be done by saying things like "Year 甲辰 (2024)"
229 Cyclic(CyclicYear),
230}
231
232impl From<EraYear> for YearInfo {
233 fn from(value: EraYear) -> Self {
234 Self::Era(value)
235 }
236}
237
238impl From<CyclicYear> for YearInfo {
239 fn from(value: CyclicYear) -> Self {
240 Self::Cyclic(value)
241 }
242}
243
244impl YearInfo {
245 /// Get *some* year number that can be displayed
246 ///
247 /// Gets the era year for era calendars, and the related ISO year for cyclic calendars.
248 pub fn era_year_or_related_iso(self) -> i32 {
249 match self {
250 YearInfo::Era(e) => e.year,
251 YearInfo::Cyclic(c) => c.related_iso,
252 }
253 }
254
255 /// Get the extended year (See [`Date::extended_year`](crate::Date::extended_year))
256 /// for more information
257 pub fn extended_year(self) -> i32 {
258 match self {
259 YearInfo::Era(e) => e.extended_year,
260 YearInfo::Cyclic(c) => c.related_iso,
261 }
262 }
263
264 /// Get the era year information, if available
265 pub fn era(self) -> Option<EraYear> {
266 match self {
267 Self::Era(e) => Some(e),
268 Self::Cyclic(_) => None,
269 }
270 }
271
272 /// Get the cyclic year informat, if available
273 pub fn cyclic(self) -> Option<CyclicYear> {
274 match self {
275 Self::Era(_) => None,
276 Self::Cyclic(c) => Some(c),
277 }
278 }
279}
280
281/// Defines whether the era or century is required to interpret the year.
282///
283/// For example 2024 AD can be formatted as `2024`, or even `24`, but 1931 AD
284/// should not be formatted as `31`, and 2024 BC should not be formatted as `2024`.
285#[derive(Copy, Clone, Debug, PartialEq)]
286#[allow(clippy::exhaustive_enums)] // logically complete
287pub enum YearAmbiguity {
288 /// The year is unambiguous without a century or era.
289 Unambiguous,
290 /// The century is required, the era may be included.
291 CenturyRequired,
292 /// The era is required, the century may be included.
293 EraRequired,
294 /// The century and era are required.
295 EraAndCenturyRequired,
296}
297
298/// Year information for a year that is specified with an era
299#[derive(Copy, Clone, Debug, PartialEq)]
300#[non_exhaustive]
301pub struct EraYear {
302 /// The numeric year in that era
303 pub year: i32,
304 /// See [`YearInfo::extended_year()`]
305 pub extended_year: i32,
306 /// The era code as defined by CLDR, expect for cases where CLDR does not define a code.
307 pub era: TinyAsciiStr<16>,
308 /// An era index, for calendars with a small set of eras.
309 ///
310 /// The only guarantee we make is that these values are stable. These do *not*
311 /// match the indices produced by ICU4C or CLDR.
312 ///
313 /// These are used by ICU4X datetime formatting for efficiently storing data.
314 pub era_index: Option<u8>,
315 /// The ambiguity of the era/year combination
316 pub ambiguity: YearAmbiguity,
317}
318
319/// Year information for a year that is specified as a cyclic year
320#[derive(Copy, Clone, Debug, PartialEq)]
321#[non_exhaustive]
322pub struct CyclicYear {
323 /// The year in the cycle, 1-based
324 pub year: u8,
325 /// The ISO year corresponding to this year
326 pub related_iso: i32,
327}
328
329/// Representation of a month in a year
330///
331/// Month codes typically look like `M01`, `M02`, etc, but can handle leap months
332/// (`M03L`) in lunar calendars. Solar calendars will have codes between `M01` and `M12`
333/// potentially with an `M13` for epagomenal months. Check the docs for a particular calendar
334/// for details on what its month codes are.
335///
336/// Month codes are shared with Temporal, [see Temporal proposal][era-proposal].
337///
338/// [era-proposal]: https://tc39.es/proposal-intl-era-monthcode/
339#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
340#[allow(clippy::exhaustive_structs)] // this is a newtype
341#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
342#[cfg_attr(feature = "datagen", databake(path = icu_calendar::types))]
343#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
344pub struct MonthCode(pub TinyAsciiStr<4>);
345
346impl MonthCode {
347 /// Returns an option which is `Some` containing the non-month version of a leap month
348 /// if the [`MonthCode`] this method is called upon is a leap month, and `None` otherwise.
349 /// This method assumes the [`MonthCode`] is valid.
350 #[deprecated(since = "2.1.0")]
351 pub fn get_normal_if_leap(self) -> Option<MonthCode> {
352 let bytes = self.0.all_bytes();
353 if bytes[3] == b'L' {
354 Some(MonthCode(TinyAsciiStr::try_from_utf8(&bytes[0..3]).ok()?))
355 } else {
356 None
357 }
358 }
359
360 #[deprecated(since = "2.1.0")]
361 /// Get the month number and whether or not it is leap from the month code
362 pub fn parsed(self) -> Option<(u8, bool)> {
363 ValidMonthCode::try_from_utf8(self.0.as_bytes())
364 .ok()
365 .map(ValidMonthCode::to_tuple)
366 }
367
368 /// Construct a "normal" month code given a number ("Mxx").
369 ///
370 /// Returns an error for months greater than 99
371 pub fn new_normal(number: u8) -> Option<Self> {
372 (1..=99)
373 .contains(&number)
374 .then(|| ValidMonthCode::new_unchecked(number, false).to_month_code())
375 }
376
377 /// Construct a "leap" month code given a number ("MxxL").
378 ///
379 /// Returns an error for months greater than 99
380 pub fn new_leap(number: u8) -> Option<Self> {
381 (1..=99)
382 .contains(&number)
383 .then(|| ValidMonthCode::new_unchecked(number, true).to_month_code())
384 }
385}
386
387#[test]
388fn test_get_normal_month_code_if_leap() {
389 #![allow(deprecated)]
390 assert_eq!(
391 MonthCode::new_leap(1).unwrap().get_normal_if_leap(),
392 MonthCode::new_normal(1)
393 );
394
395 assert_eq!(
396 MonthCode::new_leap(11).unwrap().get_normal_if_leap(),
397 MonthCode::new_normal(11)
398 );
399
400 assert_eq!(
401 MonthCode::new_normal(10).unwrap().get_normal_if_leap(),
402 None
403 );
404}
405
406impl AsULE for MonthCode {
407 type ULE = TinyAsciiStr<4>;
408 fn to_unaligned(self) -> TinyAsciiStr<4> {
409 self.0
410 }
411 fn from_unaligned(u: TinyAsciiStr<4>) -> Self {
412 Self(u)
413 }
414}
415
416#[cfg(feature = "alloc")]
417impl<'a> zerovec::maps::ZeroMapKV<'a> for MonthCode {
418 type Container = zerovec::ZeroVec<'a, MonthCode>;
419 type Slice = zerovec::ZeroSlice<MonthCode>;
420 type GetType = <MonthCode as AsULE>::ULE;
421 type OwnedType = MonthCode;
422}
423
424impl fmt::Display for MonthCode {
425 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
426 write!(f, "{}", self.0)
427 }
428}
429
430/// A [`MonthCode`] that has been parsed into its internal representation.
431#[derive(Copy, Clone, Debug, PartialEq)]
432pub(crate) struct ValidMonthCode {
433 /// Month number between 0 and 99
434 number: u8,
435 is_leap: bool,
436}
437
438impl ValidMonthCode {
439 #[inline]
440 pub(crate) fn try_from_utf8(bytes: &[u8]) -> Result<Self, MonthCodeParseError> {
441 match *bytes {
442 [b'M', tens, ones] => Ok(Self {
443 number: (tens - b'0') * 10 + ones - b'0',
444 is_leap: false,
445 }),
446 [b'M', tens, ones, b'L'] => Ok(Self {
447 number: (tens - b'0') * 10 + ones - b'0',
448 is_leap: true,
449 }),
450 _ => Err(MonthCodeParseError::InvalidSyntax),
451 }
452 }
453
454 /// Create a new ValidMonthCode without checking that the number is between 1 and 99
455 #[inline]
456 pub(crate) const fn new_unchecked(number: u8, is_leap: bool) -> Self {
457 debug_assert!(1 <= number && number <= 99);
458 Self { number, is_leap }
459 }
460
461 /// Returns the month number according to the month code.
462 ///
463 /// This is NOT the same as the ordinal month!
464 ///
465 /// # Examples
466 ///
467 /// ```ignore
468 /// use icu::calendar::Date;
469 /// use icu::calendar::cal::Hebrew;
470 ///
471 /// let hebrew_date = Date::try_new_iso(2024, 7, 1).unwrap().to_calendar(Hebrew);
472 /// let month_info = hebrew_date.month();
473 ///
474 /// // Hebrew year 5784 was a leap year, so the ordinal month and month number diverge.
475 /// assert_eq!(month_info.ordinal, 10);
476 /// assert_eq!(month_info.valid_month_code.number(), 9);
477 /// ```
478 #[inline]
479 pub fn number(self) -> u8 {
480 self.number
481 }
482
483 /// Returns whether the month is a leap month.
484 ///
485 /// This is true for intercalary months in [`Hebrew`] and [`LunarChinese`].
486 ///
487 /// [`Hebrew`]: crate::cal::Hebrew
488 /// [`LunarChinese`]: crate::cal::LunarChinese
489 #[inline]
490 pub fn is_leap(self) -> bool {
491 self.is_leap
492 }
493
494 #[inline]
495 pub(crate) fn to_tuple(self) -> (u8, bool) {
496 (self.number, self.is_leap)
497 }
498
499 pub(crate) fn to_month_code(self) -> MonthCode {
500 #[allow(clippy::unwrap_used)] // by construction
501 MonthCode(
502 TinyAsciiStr::try_from_raw([
503 b'M',
504 b'0' + self.number / 10,
505 b'0' + self.number % 10,
506 if self.is_leap { b'L' } else { 0 },
507 ])
508 .unwrap(),
509 )
510 }
511}
512
513/// Representation of a formattable month.
514#[derive(Copy, Clone, Debug, PartialEq)]
515#[non_exhaustive]
516pub struct MonthInfo {
517 /// The month number in this given year. For calendars with leap months, all months after
518 /// the leap month will end up with an incremented number.
519 ///
520 /// In general, prefer using the month code in generic code.
521 pub ordinal: u8,
522
523 /// The month code, used to distinguish months during leap years.
524 ///
525 /// Round-trips through `Date` constructors like [`Date::try_new_from_codes`] and [`Date::try_from_fields`].
526 ///
527 /// This follows [Temporal's specification](https://tc39.es/proposal-intl-era-monthcode/#table-additional-month-codes).
528 /// Months considered the "same" have the same code: This means that the Hebrew months "Adar" and "Adar II" ("Adar, but during a leap year")
529 /// are considered the same month and have the code M05.
530 ///
531 /// [`Date::try_new_from_codes`]: crate::Date::try_new_from_codes
532 /// [`Date::try_from_fields`]: crate::Date::try_from_fields
533 pub standard_code: MonthCode,
534
535 /// Same as [`Self::standard_code`] but with invariants validated.
536 pub(crate) valid_standard_code: ValidMonthCode,
537
538 /// A month code, useable for formatting.
539 ///
540 /// Does NOT necessarily round-trip through `Date` constructors like [`Date::try_new_from_codes`] and [`Date::try_from_fields`].
541 ///
542 /// This may not necessarily be the canonical month code for a month in cases where a month has different
543 /// formatting in a leap year, for example Adar/Adar II in the Hebrew calendar in a leap year has
544 /// the standard code M06, but for formatting specifically the Hebrew calendar will return M06L since it is formatted
545 /// differently.
546 ///
547 /// [`Date::try_new_from_codes`]: crate::Date::try_new_from_codes
548 /// [`Date::try_from_fields`]: crate::Date::try_from_fields
549 pub formatting_code: MonthCode,
550
551 /// Same as [`Self::formatting_code`] but with invariants validated.
552 pub(crate) valid_formatting_code: ValidMonthCode,
553}
554
555impl MonthInfo {
556 pub(crate) fn non_lunisolar(number: u8) -> Self {
557 Self::for_code_and_ordinal(ValidMonthCode::new_unchecked(number, false), number)
558 }
559
560 pub(crate) fn for_code_and_ordinal(code: ValidMonthCode, ordinal: u8) -> Self {
561 Self {
562 ordinal,
563 standard_code: code.to_month_code(),
564 valid_standard_code: code,
565 formatting_code: code.to_month_code(),
566 valid_formatting_code: code,
567 }
568 }
569
570 /// Gets the month number. A month number N is not necessarily the Nth month in the year
571 /// if there are leap months in the year, rather it is associated with the Nth month of a "regular"
572 /// year. There may be multiple month Ns in a year
573 pub fn month_number(self) -> u8 {
574 self.valid_standard_code.number()
575 }
576
577 /// Get whether the month is a leap month
578 pub fn is_leap(self) -> bool {
579 self.valid_standard_code.is_leap()
580 }
581
582 #[doc(hidden)]
583 pub fn formatting_month_number(self) -> u8 {
584 self.valid_formatting_code.number()
585 }
586
587 #[doc(hidden)]
588 pub fn formatting_is_leap(self) -> bool {
589 self.valid_formatting_code.is_leap()
590 }
591}
592
593/// The current day of the year, 1-based.
594#[derive(Copy, Clone, Debug, PartialEq)]
595#[allow(clippy::exhaustive_structs)] // this is a newtype
596pub struct DayOfYear(pub u16);
597
598/// A 1-based day number in a month.
599#[allow(clippy::exhaustive_structs)] // this is a newtype
600#[derive(Clone, Copy, Debug, PartialEq)]
601pub struct DayOfMonth(pub u8);
602
603/// A week number in a year
604#[derive(Clone, Copy, Debug, PartialEq)]
605#[allow(clippy::exhaustive_structs)] // this is a newtype
606pub struct IsoWeekOfYear {
607 /// The 1-based ISO week number
608 pub week_number: u8,
609 /// The ISO year
610 pub iso_year: i32,
611}
612
613/// A day of week in month. 1-based.
614#[derive(Clone, Copy, Debug, PartialEq)]
615#[allow(clippy::exhaustive_structs)] // this is a newtype
616pub struct DayOfWeekInMonth(pub u8);
617
618impl From<DayOfMonth> for DayOfWeekInMonth {
619 fn from(day_of_month: DayOfMonth) -> Self {
620 DayOfWeekInMonth(1 + ((day_of_month.0 - 1) / 7))
621 }
622}
623
624#[test]
625fn test_day_of_week_in_month() {
626 assert_eq!(DayOfWeekInMonth::from(DayOfMonth(1)).0, 1);
627 assert_eq!(DayOfWeekInMonth::from(DayOfMonth(7)).0, 1);
628 assert_eq!(DayOfWeekInMonth::from(DayOfMonth(8)).0, 2);
629}
630
631/// A weekday in a 7-day week, according to ISO-8601.
632///
633/// The discriminant values correspond to ISO-8601 weekday numbers (Monday = 1, Sunday = 7).
634///
635/// # Examples
636///
637/// ```
638/// use icu::calendar::types::Weekday;
639///
640/// assert_eq!(1, Weekday::Monday as usize);
641/// assert_eq!(7, Weekday::Sunday as usize);
642/// ```
643#[derive(Clone, Copy, Debug, PartialEq, Eq)]
644#[allow(missing_docs)] // The weekday variants should be self-obvious.
645#[repr(i8)]
646#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
647#[cfg_attr(feature = "datagen", databake(path = icu_calendar::types))]
648#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
649#[allow(clippy::exhaustive_enums)] // This is stable
650pub enum Weekday {
651 Monday = 1,
652 Tuesday,
653 Wednesday,
654 Thursday,
655 Friday,
656 Saturday,
657 Sunday,
658}
659
660// RD 0 is a Sunday
661const SUNDAY: RataDie = RataDie::new(0);
662
663impl From<RataDie> for Weekday {
664 fn from(value: RataDie) -> Self {
665 use Weekday::*;
666 match (value - SUNDAY).rem_euclid(7) {
667 0 => Sunday,
668 1 => Monday,
669 2 => Tuesday,
670 3 => Wednesday,
671 4 => Thursday,
672 5 => Friday,
673 6 => Saturday,
674 _ => unreachable!(),
675 }
676 }
677}
678
679impl Weekday {
680 /// Convert from an ISO-8601 weekday number to an [`Weekday`] enum. 0 is automatically converted
681 /// to 7 (Sunday). If the number is out of range, it is interpreted modulo 7.
682 ///
683 /// # Examples
684 ///
685 /// ```
686 /// use icu::calendar::types::Weekday;
687 ///
688 /// assert_eq!(Weekday::Sunday, Weekday::from_days_since_sunday(0));
689 /// assert_eq!(Weekday::Monday, Weekday::from_days_since_sunday(1));
690 /// assert_eq!(Weekday::Sunday, Weekday::from_days_since_sunday(7));
691 /// assert_eq!(Weekday::Monday, Weekday::from_days_since_sunday(8));
692 /// ```
693 pub fn from_days_since_sunday(input: isize) -> Self {
694 (SUNDAY + input as i64).into()
695 }
696
697 /// Returns the day after the current day.
698 pub(crate) fn next_day(self) -> Weekday {
699 use Weekday::*;
700 match self {
701 Monday => Tuesday,
702 Tuesday => Wednesday,
703 Wednesday => Thursday,
704 Thursday => Friday,
705 Friday => Saturday,
706 Saturday => Sunday,
707 Sunday => Monday,
708 }
709 }
710}