jiff/civil/
weekday.rs

1use crate::{
2    error::Error,
3    shared::util::itime::IWeekday,
4    util::{
5        rangeint::{RFrom, RInto},
6        t::{self, C},
7    },
8};
9
10/// A representation for the day of the week.
11///
12/// The default representation follows ISO 8601. That is, the week starts with
13/// Monday and numbering starts at `1`. However, the various constructors and
14/// accessors support using other schemes in wide use:
15///
16/// * [`Weekday::from_monday_zero_offset`] builds a weekday from
17/// a scheme that starts the week on Monday at offset `0`, while
18/// [`Weekday::to_monday_zero_offset`] converts to it.
19/// * [`Weekday::from_monday_one_offset`] builds a weekday from a scheme
20/// that starts the week on Monday at offset `1` (the default representation),
21/// while [`Weekday::to_monday_one_offset`] converts to it.
22/// * [`Weekday::from_sunday_zero_offset`] builds a weekday from
23/// a scheme that starts the week on Sunday at offset `0`, while
24/// [`Weekday::to_sunday_zero_offset`] converts to it.
25/// * [`Weekday::from_sunday_one_offset`] builds a weekday from
26/// a scheme that starts the week on Sunday at offset `1`, while
27/// [`Weekday::to_sunday_one_offset`] converts to it.
28///
29/// # Arithmetic
30///
31/// This type provides [`Weekday::wrapping_add`] and [`Weekday::wrapping_sub`]
32/// for performing wrapping arithmetic on weekdays. These are also available
33/// via operator overloading:
34///
35/// ```
36/// use jiff::civil::Weekday;
37///
38/// assert_eq!(Weekday::Monday + 1, Weekday::Tuesday);
39/// assert_eq!(Weekday::Sunday - 1, Weekday::Saturday);
40/// ```
41///
42/// # Comparisons
43///
44/// This type provides `Eq` and `PartialEq` trait implementations for easy
45/// comparison:
46///
47/// ```
48/// use jiff::civil::Weekday;
49///
50/// assert_eq!(Weekday::Wednesday, Weekday::Wednesday + 7);
51/// assert_ne!(Weekday::Thursday, Weekday::Friday);
52/// ```
53///
54/// But this type specifically does not provide `Ord` or `PartialOrd` trait
55/// implementations. Such an implementation would require deciding whether
56/// Sunday is less than Monday or greater than Monday. The former case
57/// corresponds to a week scheme where Sunday is the first day in the week,
58/// where as the latter corresponds to a scheme where Monday is the first day.
59/// Since both schemes are in widespread use, it would be inappropriate to bake
60/// in an assumption of one or the other. Instead, one can convert a weekday
61/// into the desired offset scheme, and then compare the offsets:
62///
63/// ```
64/// use jiff::civil::Weekday;
65///
66/// let (sun, mon) = (Weekday::Sunday, Weekday::Monday);
67/// assert!(sun.to_sunday_zero_offset() < mon.to_sunday_zero_offset());
68/// assert!(sun.to_monday_zero_offset() > mon.to_monday_zero_offset());
69/// ```
70///
71/// # Example
72///
73/// This example shows the result of converting to and from different schemes:
74///
75/// ```
76/// use jiff::civil::Weekday;
77///
78/// // The different representations of Monday.
79/// assert_eq!(Weekday::Monday.to_monday_zero_offset(), 0);
80/// assert_eq!(Weekday::Monday.to_monday_one_offset(), 1);
81/// assert_eq!(Weekday::Monday.to_sunday_zero_offset(), 1);
82/// assert_eq!(Weekday::Monday.to_sunday_one_offset(), 2);
83///
84/// // The different representations of Sunday.
85/// assert_eq!(Weekday::Sunday.to_monday_zero_offset(), 6);
86/// assert_eq!(Weekday::Sunday.to_monday_one_offset(), 7);
87/// assert_eq!(Weekday::Sunday.to_sunday_zero_offset(), 0);
88/// assert_eq!(Weekday::Sunday.to_sunday_one_offset(), 1);
89/// ```
90#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
91#[repr(u8)]
92#[allow(missing_docs)]
93pub enum Weekday {
94    Monday = 1,
95    Tuesday = 2,
96    Wednesday = 3,
97    Thursday = 4,
98    Friday = 5,
99    Saturday = 6,
100    Sunday = 7,
101}
102
103impl Weekday {
104    /// Convert an offset to a structured `Weekday`.
105    ///
106    /// The offset should be from a scheme where the first day of the week
107    /// is Monday and starts numbering at `0`.
108    ///
109    /// # Errors
110    ///
111    /// This returns an error when the given offset is not in the range
112    /// `0..=6`.
113    ///
114    /// # Example
115    ///
116    /// ```
117    /// use jiff::civil::Weekday;
118    ///
119    /// let weekday = Weekday::from_monday_zero_offset(3)?;
120    /// assert_eq!(weekday, Weekday::Thursday);
121    ///
122    /// assert!(Weekday::from_monday_zero_offset(7).is_err());
123    /// assert!(Weekday::from_monday_zero_offset(-1).is_err());
124    ///
125    /// # Ok::<(), Box<dyn std::error::Error>>(())
126    /// ```
127    #[inline]
128    pub fn from_monday_zero_offset(offset: i8) -> Result<Weekday, Error> {
129        let offset = t::WeekdayZero::try_new("weekday", offset)?;
130        Ok(Weekday::from_monday_zero_offset_ranged(offset))
131    }
132
133    /// Convert an offset to a structured `Weekday`.
134    ///
135    /// The offset should be from a scheme where the first day of the week
136    /// is Monday and starts numbering at `1`.
137    ///
138    /// # Errors
139    ///
140    /// This returns an error when the given offset is not in the range
141    /// `1..=7`.
142    ///
143    /// # Example
144    ///
145    /// ```
146    /// use jiff::civil::Weekday;
147    ///
148    /// let weekday = Weekday::from_monday_one_offset(4)?;
149    /// assert_eq!(weekday, Weekday::Thursday);
150    ///
151    /// assert!(Weekday::from_monday_one_offset(8).is_err());
152    /// assert!(Weekday::from_monday_one_offset(0).is_err());
153    ///
154    /// # Ok::<(), Box<dyn std::error::Error>>(())
155    /// ```
156    #[inline]
157    pub fn from_monday_one_offset(offset: i8) -> Result<Weekday, Error> {
158        let offset = t::WeekdayOne::try_new("weekday", offset)?;
159        Ok(Weekday::from_monday_one_offset_ranged(offset))
160    }
161
162    /// Convert an offset to a structured `Weekday`.
163    ///
164    /// The offset should be from a scheme where the first day of the week
165    /// is Sunday and starts numbering at `0`.
166    ///
167    /// # Errors
168    ///
169    /// This returns an error when the given offset is not in the range
170    /// `0..=6`.
171    ///
172    /// # Example
173    ///
174    /// ```
175    /// use jiff::civil::Weekday;
176    ///
177    /// let weekday = Weekday::from_sunday_zero_offset(4)?;
178    /// assert_eq!(weekday, Weekday::Thursday);
179    ///
180    /// assert!(Weekday::from_sunday_zero_offset(7).is_err());
181    /// assert!(Weekday::from_sunday_zero_offset(-1).is_err());
182    ///
183    /// # Ok::<(), Box<dyn std::error::Error>>(())
184    /// ```
185    #[inline]
186    pub fn from_sunday_zero_offset(offset: i8) -> Result<Weekday, Error> {
187        let offset = t::WeekdayZero::try_new("weekday", offset)?;
188        Ok(Weekday::from_sunday_zero_offset_ranged(offset))
189    }
190
191    /// Convert an offset to a structured `Weekday`.
192    ///
193    /// The offset should be from a scheme where the first day of the week
194    /// is Sunday and starts numbering at `1`.
195    ///
196    /// # Errors
197    ///
198    /// This returns an error when the given offset is not in the range
199    /// `1..=7`.
200    ///
201    /// # Example
202    ///
203    /// ```
204    /// use jiff::civil::Weekday;
205    ///
206    /// let weekday = Weekday::from_sunday_one_offset(5)?;
207    /// assert_eq!(weekday, Weekday::Thursday);
208    ///
209    /// assert!(Weekday::from_sunday_one_offset(8).is_err());
210    /// assert!(Weekday::from_sunday_one_offset(0).is_err());
211    ///
212    /// # Ok::<(), Box<dyn std::error::Error>>(())
213    /// ```
214    #[inline]
215    pub fn from_sunday_one_offset(offset: i8) -> Result<Weekday, Error> {
216        let offset = t::WeekdayOne::try_new("weekday", offset)?;
217        Ok(Weekday::from_sunday_one_offset_ranged(offset))
218    }
219
220    /// Returns this weekday as an offset.
221    ///
222    /// The offset returned is computed based on a week that starts with Monday
223    /// and begins numbering at `0`.
224    ///
225    /// # Example
226    ///
227    /// ```
228    /// use jiff::civil::Weekday;
229    ///
230    /// assert_eq!(Weekday::Thursday.to_monday_zero_offset(), 3);
231    ///
232    /// # Ok::<(), Box<dyn std::error::Error>>(())
233    /// ```
234    #[inline]
235    pub fn to_monday_zero_offset(self) -> i8 {
236        self.to_monday_zero_offset_ranged().get()
237    }
238
239    /// Returns this weekday as an offset.
240    ///
241    /// The offset returned is computed based on a week that starts with Monday
242    /// and begins numbering at `1`.
243    ///
244    /// # Example
245    ///
246    /// ```
247    /// use jiff::civil::Weekday;
248    ///
249    /// assert_eq!(Weekday::Thursday.to_monday_one_offset(), 4);
250    ///
251    /// # Ok::<(), Box<dyn std::error::Error>>(())
252    /// ```
253    #[inline]
254    pub fn to_monday_one_offset(self) -> i8 {
255        self.to_monday_one_offset_ranged().get()
256    }
257
258    /// Returns this weekday as an offset.
259    ///
260    /// The offset returned is computed based on a week that starts with Sunday
261    /// and begins numbering at `0`.
262    ///
263    /// # Example
264    ///
265    /// ```
266    /// use jiff::civil::Weekday;
267    ///
268    /// assert_eq!(Weekday::Thursday.to_sunday_zero_offset(), 4);
269    ///
270    /// # Ok::<(), Box<dyn std::error::Error>>(())
271    /// ```
272    #[inline]
273    pub fn to_sunday_zero_offset(self) -> i8 {
274        self.to_sunday_zero_offset_ranged().get()
275    }
276
277    /// Returns this weekday as an offset.
278    ///
279    /// The offset returned is computed based on a week that starts with Sunday
280    /// and begins numbering at `1`.
281    ///
282    /// # Example
283    ///
284    /// ```
285    /// use jiff::civil::Weekday;
286    ///
287    /// assert_eq!(Weekday::Thursday.to_sunday_one_offset(), 5);
288    ///
289    /// # Ok::<(), Box<dyn std::error::Error>>(())
290    /// ```
291    #[inline]
292    pub fn to_sunday_one_offset(self) -> i8 {
293        self.to_sunday_one_offset_ranged().get()
294    }
295
296    /// Returns the next weekday, wrapping around at the end of week to the
297    /// beginning of the week.
298    ///
299    /// This is a convenience routing for calling [`Weekday::wrapping_add`]
300    /// with a value of `1`.
301    ///
302    /// # Example
303    ///
304    /// ```
305    /// use jiff::civil::Weekday;
306    ///
307    /// assert_eq!(Weekday::Wednesday.next(), Weekday::Thursday);
308    /// assert_eq!(Weekday::Sunday.next(), Weekday::Monday);
309    /// assert_eq!(Weekday::Saturday.next(), Weekday::Sunday);
310    /// ```
311    #[inline]
312    pub fn next(self) -> Weekday {
313        self.wrapping_add(1)
314    }
315
316    /// Returns the previous weekday, wrapping around at the beginning of week
317    /// to the end of the week.
318    ///
319    /// This is a convenience routing for calling [`Weekday::wrapping_sub`]
320    /// with a value of `1`.
321    ///
322    /// # Example
323    ///
324    /// ```
325    /// use jiff::civil::Weekday;
326    ///
327    /// assert_eq!(Weekday::Wednesday.previous(), Weekday::Tuesday);
328    /// assert_eq!(Weekday::Sunday.previous(), Weekday::Saturday);
329    /// assert_eq!(Weekday::Saturday.previous(), Weekday::Friday);
330    /// ```
331    #[inline]
332    pub fn previous(self) -> Weekday {
333        self.wrapping_sub(1)
334    }
335
336    /// Returns the number of days from `other` to this weekday.
337    ///
338    /// Adding the returned number of days to `other` is guaranteed to sum to
339    /// this weekday. The number of days returned is guaranteed to be in the
340    /// range `0..=6`.
341    ///
342    /// # Example
343    ///
344    /// ```
345    /// use jiff::civil::Weekday;
346    ///
347    /// assert_eq!(Weekday::Friday.since(Weekday::Tuesday), 3);
348    /// assert_eq!(Weekday::Tuesday.since(Weekday::Tuesday), 0);
349    /// assert_eq!(Weekday::Monday.since(Weekday::Sunday), 1);
350    /// assert_eq!(Weekday::Sunday.since(Weekday::Monday), 6);
351    /// ```
352    #[inline]
353    pub fn since(self, other: Weekday) -> i8 {
354        self.since_ranged(other).get()
355    }
356
357    /// Returns the number of days until `other` from this weekday.
358    ///
359    /// Adding the returned number of days to this weekday is guaranteed to sum
360    /// to `other` weekday. The number of days returned is guaranteed to be in
361    /// the range `0..=6`.
362    ///
363    /// # Example
364    ///
365    /// ```
366    /// use jiff::civil::Weekday;
367    ///
368    /// assert_eq!(Weekday::Friday.until(Weekday::Tuesday), 4);
369    /// assert_eq!(Weekday::Tuesday.until(Weekday::Tuesday), 0);
370    /// assert_eq!(Weekday::Monday.until(Weekday::Sunday), 6);
371    /// assert_eq!(Weekday::Sunday.until(Weekday::Monday), 1);
372    /// ```
373    #[inline]
374    pub fn until(self, other: Weekday) -> i8 {
375        self.until_ranged(other).get()
376    }
377
378    /// Add the given number of days to this weekday, using wrapping arithmetic,
379    /// and return the resulting weekday.
380    ///
381    /// Adding a multiple of `7` (including `0`) is guaranteed to produce the
382    /// same weekday as this one.
383    ///
384    /// Note that this routine is also available via the `+` operator.
385    ///
386    /// # Example
387    ///
388    /// ```
389    /// use jiff::civil::Weekday;
390    ///
391    /// assert_eq!(Weekday::Sunday.wrapping_add(1), Weekday::Monday);
392    /// assert_eq!(Weekday::Sunday.wrapping_add(2), Weekday::Tuesday);
393    /// assert_eq!(Weekday::Saturday.wrapping_add(1), Weekday::Sunday);
394    /// assert_eq!(Weekday::Saturday.wrapping_add(7), Weekday::Saturday);
395    /// assert_eq!(Weekday::Sunday.wrapping_add(-1), Weekday::Saturday);
396    /// ```
397    ///
398    /// Wrapping arithmetic is also performed by the `+` operator:
399    ///
400    /// ```
401    /// use jiff::civil::Weekday;
402    ///
403    /// assert_eq!(Weekday::Sunday + 1, Weekday::Monday);
404    /// assert_eq!(Weekday::Sunday + 2, Weekday::Tuesday);
405    /// assert_eq!(Weekday::Saturday + 1, Weekday::Sunday);
406    /// assert_eq!(Weekday::Saturday + 7, Weekday::Saturday);
407    /// assert_eq!(Weekday::Sunday + -1, Weekday::Saturday);
408    /// // The weekday can also be on the right hand side of addition:
409    /// assert_eq!(1 + Weekday::Sunday, Weekday::Monday);
410    /// ```
411    #[inline]
412    pub fn wrapping_add<D: Into<i64>>(self, days: D) -> Weekday {
413        let start = t::NoUnits::rfrom(self.to_monday_zero_offset_ranged());
414        // OK because all i64 values fit in a NoUnits.
415        let rhs = t::NoUnits::new(days.into()).unwrap();
416        let end = start.wrapping_add(rhs) % C(7);
417        Weekday::from_monday_zero_offset_ranged(end)
418    }
419
420    /// Subtract the given number of days from this weekday, using wrapping
421    /// arithmetic, and return the resulting weekday.
422    ///
423    /// Subtracting a multiple of `7` (including `0`) is guaranteed to produce
424    /// the same weekday as this one.
425    ///
426    /// Note that this routine is also available via the `-` operator.
427    ///
428    /// # Example
429    ///
430    /// ```
431    /// use jiff::civil::Weekday;
432    ///
433    /// assert_eq!(Weekday::Sunday.wrapping_sub(1), Weekday::Saturday);
434    /// assert_eq!(Weekday::Sunday.wrapping_sub(2), Weekday::Friday);
435    /// assert_eq!(Weekday::Saturday.wrapping_sub(1), Weekday::Friday);
436    /// assert_eq!(Weekday::Saturday.wrapping_sub(7), Weekday::Saturday);
437    /// assert_eq!(Weekday::Sunday.wrapping_sub(-1), Weekday::Monday);
438    /// ```
439    ///
440    /// Wrapping arithmetic is also performed by the `-` operator:
441    ///
442    /// ```
443    /// use jiff::civil::Weekday;
444    ///
445    /// assert_eq!(Weekday::Sunday - 1, Weekday::Saturday);
446    /// assert_eq!(Weekday::Sunday - 2, Weekday::Friday);
447    /// assert_eq!(Weekday::Saturday - 1, Weekday::Friday);
448    /// assert_eq!(Weekday::Saturday - 7, Weekday::Saturday);
449    /// assert_eq!(Weekday::Sunday - -1, Weekday::Monday);
450    /// ```
451    ///
452    /// Unlike addition, since subtraction is not commutative and negating a
453    /// weekday has no semantic meaning, the weekday cannot be on the right
454    /// hand side of the `-` operator.
455    #[inline]
456    pub fn wrapping_sub<D: Into<i64>>(self, days: D) -> Weekday {
457        self.wrapping_add(-days.into())
458    }
459
460    /// Starting with this weekday, this returns an unending iterator that
461    /// cycles forward through the days of the week.
462    ///
463    /// # Example
464    ///
465    /// ```
466    /// use jiff::civil::Weekday;
467    ///
468    /// let mut it = Weekday::Tuesday.cycle_forward();
469    /// assert_eq!(it.next(), Some(Weekday::Tuesday));
470    /// assert_eq!(it.next(), Some(Weekday::Wednesday));
471    /// assert_eq!(it.next(), Some(Weekday::Thursday));
472    /// assert_eq!(it.next(), Some(Weekday::Friday));
473    /// assert_eq!(it.next(), Some(Weekday::Saturday));
474    /// assert_eq!(it.next(), Some(Weekday::Sunday));
475    /// assert_eq!(it.next(), Some(Weekday::Monday));
476    /// assert_eq!(it.next(), Some(Weekday::Tuesday));
477    /// ```
478    #[inline]
479    pub fn cycle_forward(self) -> WeekdaysForward {
480        let nexts = [
481            self,
482            self.wrapping_add(1),
483            self.wrapping_add(2),
484            self.wrapping_add(3),
485            self.wrapping_add(4),
486            self.wrapping_add(5),
487            self.wrapping_add(6),
488        ];
489        WeekdaysForward { it: nexts.into_iter().cycle() }
490    }
491
492    /// Starting with this weekday, this returns an unending iterator that
493    /// cycles backward through the days of the week.
494    ///
495    /// # Example
496    ///
497    /// ```
498    /// use jiff::civil::Weekday;
499    ///
500    /// let mut it = Weekday::Tuesday.cycle_reverse();
501    /// assert_eq!(it.next(), Some(Weekday::Tuesday));
502    /// assert_eq!(it.next(), Some(Weekday::Monday));
503    /// assert_eq!(it.next(), Some(Weekday::Sunday));
504    /// assert_eq!(it.next(), Some(Weekday::Saturday));
505    /// assert_eq!(it.next(), Some(Weekday::Friday));
506    /// assert_eq!(it.next(), Some(Weekday::Thursday));
507    /// assert_eq!(it.next(), Some(Weekday::Wednesday));
508    /// assert_eq!(it.next(), Some(Weekday::Tuesday));
509    /// ```
510    #[inline]
511    pub fn cycle_reverse(self) -> WeekdaysReverse {
512        let nexts = [
513            self,
514            self.wrapping_sub(1),
515            self.wrapping_sub(2),
516            self.wrapping_sub(3),
517            self.wrapping_sub(4),
518            self.wrapping_sub(5),
519            self.wrapping_sub(6),
520        ];
521        WeekdaysReverse { it: nexts.into_iter().cycle() }
522    }
523}
524
525impl Weekday {
526    #[inline]
527    pub(crate) fn from_monday_zero_offset_ranged(
528        offset: impl RInto<t::WeekdayZero>,
529    ) -> Weekday {
530        match offset.rinto().get() {
531            0 => Weekday::Monday,
532            1 => Weekday::Tuesday,
533            2 => Weekday::Wednesday,
534            3 => Weekday::Thursday,
535            4 => Weekday::Friday,
536            5 => Weekday::Saturday,
537            6 => Weekday::Sunday,
538            _ => unreachable!(),
539        }
540    }
541
542    #[inline]
543    pub(crate) fn from_monday_one_offset_ranged(
544        offset: impl RInto<t::WeekdayOne>,
545    ) -> Weekday {
546        let offset_zero = offset.rinto() - C(1);
547        Weekday::from_monday_zero_offset_ranged(offset_zero)
548    }
549
550    #[inline]
551    pub(crate) fn from_sunday_zero_offset_ranged(
552        offset: impl RInto<t::WeekdayZero>,
553    ) -> Weekday {
554        let offset_sunday = (offset.rinto() - C(1)) % C(7);
555        Weekday::from_monday_zero_offset_ranged(offset_sunday)
556    }
557
558    #[inline]
559    pub(crate) fn from_sunday_one_offset_ranged(
560        offset: impl RInto<t::WeekdayOne>,
561    ) -> Weekday {
562        let offset_zero = offset.rinto() - C(1);
563        Weekday::from_sunday_zero_offset_ranged(offset_zero)
564    }
565
566    #[inline]
567    pub(crate) fn from_iweekday(iweekday: IWeekday) -> Weekday {
568        match iweekday.to_monday_one_offset() {
569            1 => Weekday::Monday,
570            2 => Weekday::Tuesday,
571            3 => Weekday::Wednesday,
572            4 => Weekday::Thursday,
573            5 => Weekday::Friday,
574            6 => Weekday::Saturday,
575            7 => Weekday::Sunday,
576            _ => unreachable!(),
577        }
578    }
579
580    #[inline]
581    pub(crate) fn to_monday_zero_offset_ranged(self) -> t::WeekdayZero {
582        (self.to_monday_one_offset_ranged() - C(1)).rinto()
583    }
584
585    #[inline]
586    pub(crate) fn to_monday_one_offset_ranged(self) -> t::WeekdayOne {
587        t::WeekdayOne::new_unchecked(self as i8)
588    }
589
590    #[inline]
591    pub(crate) fn to_sunday_zero_offset_ranged(self) -> t::WeekdayZero {
592        (self.to_monday_zero_offset_ranged() + C(1)) % C(7)
593    }
594
595    #[inline]
596    pub(crate) fn to_sunday_one_offset_ranged(self) -> t::WeekdayOne {
597        (self.to_sunday_zero_offset_ranged() + C(1)).rinto()
598    }
599
600    #[inline]
601    pub(crate) fn to_iweekday(self) -> IWeekday {
602        IWeekday::from_monday_one_offset(self.to_monday_one_offset())
603    }
604
605    #[inline]
606    pub(crate) fn since_ranged(self, other: Weekday) -> t::WeekdayZero {
607        (self.to_monday_zero_offset_ranged()
608            - other.to_monday_zero_offset_ranged())
609            % C(7)
610    }
611
612    #[inline]
613    pub(crate) fn until_ranged(self, other: Weekday) -> t::WeekdayZero {
614        other.since_ranged(self)
615    }
616}
617
618impl core::ops::Add<i8> for Weekday {
619    type Output = Weekday;
620
621    #[inline]
622    fn add(self, rhs: i8) -> Weekday {
623        self.wrapping_add(rhs)
624    }
625}
626
627impl core::ops::Add<i16> for Weekday {
628    type Output = Weekday;
629
630    #[inline]
631    fn add(self, rhs: i16) -> Weekday {
632        self.wrapping_add(rhs)
633    }
634}
635
636impl core::ops::Add<i32> for Weekday {
637    type Output = Weekday;
638
639    #[inline]
640    fn add(self, rhs: i32) -> Weekday {
641        self.wrapping_add(rhs)
642    }
643}
644
645impl core::ops::Add<i64> for Weekday {
646    type Output = Weekday;
647
648    #[inline]
649    fn add(self, rhs: i64) -> Weekday {
650        self.wrapping_add(rhs)
651    }
652}
653
654// Since addition is commutative, we don't might if users write `n + weekday`
655// or `weekday + n`.
656
657impl core::ops::Add<Weekday> for i8 {
658    type Output = Weekday;
659
660    #[inline]
661    fn add(self, rhs: Weekday) -> Weekday {
662        rhs.wrapping_add(self)
663    }
664}
665
666impl core::ops::Add<Weekday> for i16 {
667    type Output = Weekday;
668
669    #[inline]
670    fn add(self, rhs: Weekday) -> Weekday {
671        rhs.wrapping_add(self)
672    }
673}
674
675impl core::ops::Add<Weekday> for i32 {
676    type Output = Weekday;
677
678    #[inline]
679    fn add(self, rhs: Weekday) -> Weekday {
680        rhs.wrapping_add(self)
681    }
682}
683
684impl core::ops::Add<Weekday> for i64 {
685    type Output = Weekday;
686
687    #[inline]
688    fn add(self, rhs: Weekday) -> Weekday {
689        rhs.wrapping_add(self)
690    }
691}
692
693impl core::ops::AddAssign<i8> for Weekday {
694    #[inline]
695    fn add_assign(&mut self, rhs: i8) {
696        *self = *self + rhs;
697    }
698}
699
700impl core::ops::AddAssign<i16> for Weekday {
701    #[inline]
702    fn add_assign(&mut self, rhs: i16) {
703        *self = *self + rhs;
704    }
705}
706
707impl core::ops::AddAssign<i32> for Weekday {
708    #[inline]
709    fn add_assign(&mut self, rhs: i32) {
710        *self = *self + rhs;
711    }
712}
713
714impl core::ops::AddAssign<i64> for Weekday {
715    #[inline]
716    fn add_assign(&mut self, rhs: i64) {
717        *self = *self + rhs;
718    }
719}
720
721// Subtraction isn't commutative, so we only define it when the right hand
722// side is an integer. Otherwise we'd need a concept of what it means to
723// "negate" a weekday, which doesn't really make sense?
724
725impl core::ops::Sub<i8> for Weekday {
726    type Output = Weekday;
727
728    #[inline]
729    fn sub(self, rhs: i8) -> Weekday {
730        self.wrapping_sub(rhs)
731    }
732}
733
734impl core::ops::Sub<i16> for Weekday {
735    type Output = Weekday;
736
737    #[inline]
738    fn sub(self, rhs: i16) -> Weekday {
739        self.wrapping_sub(rhs)
740    }
741}
742
743impl core::ops::Sub<i32> for Weekday {
744    type Output = Weekday;
745
746    #[inline]
747    fn sub(self, rhs: i32) -> Weekday {
748        self.wrapping_sub(rhs)
749    }
750}
751
752impl core::ops::Sub<i64> for Weekday {
753    type Output = Weekday;
754
755    #[inline]
756    fn sub(self, rhs: i64) -> Weekday {
757        self.wrapping_sub(rhs)
758    }
759}
760
761impl core::ops::SubAssign<i8> for Weekday {
762    #[inline]
763    fn sub_assign(&mut self, rhs: i8) {
764        *self = *self - rhs;
765    }
766}
767
768impl core::ops::SubAssign<i16> for Weekday {
769    #[inline]
770    fn sub_assign(&mut self, rhs: i16) {
771        *self = *self - rhs;
772    }
773}
774
775impl core::ops::SubAssign<i32> for Weekday {
776    #[inline]
777    fn sub_assign(&mut self, rhs: i32) {
778        *self = *self - rhs;
779    }
780}
781
782impl core::ops::SubAssign<i64> for Weekday {
783    #[inline]
784    fn sub_assign(&mut self, rhs: i64) {
785        *self = *self - rhs;
786    }
787}
788
789#[cfg(test)]
790impl quickcheck::Arbitrary for Weekday {
791    fn arbitrary(g: &mut quickcheck::Gen) -> Weekday {
792        let offset = t::WeekdayZero::arbitrary(g);
793        Weekday::from_monday_zero_offset_ranged(offset)
794    }
795
796    fn shrink(&self) -> alloc::boxed::Box<dyn Iterator<Item = Weekday>> {
797        alloc::boxed::Box::new(
798            self.to_monday_zero_offset_ranged()
799                .shrink()
800                .map(Weekday::from_monday_zero_offset_ranged),
801        )
802    }
803}
804
805/// An unending iterator of the days of the week.
806///
807/// This iterator is created by calling [`Weekday::cycle_forward`].
808#[derive(Clone, Debug)]
809pub struct WeekdaysForward {
810    it: core::iter::Cycle<core::array::IntoIter<Weekday, 7>>,
811}
812
813impl Iterator for WeekdaysForward {
814    type Item = Weekday;
815
816    #[inline]
817    fn next(&mut self) -> Option<Weekday> {
818        self.it.next()
819    }
820}
821
822impl core::iter::FusedIterator for WeekdaysForward {}
823
824/// An unending iterator of the days of the week in reverse.
825///
826/// This iterator is created by calling [`Weekday::cycle_reverse`].
827#[derive(Clone, Debug)]
828pub struct WeekdaysReverse {
829    it: core::iter::Cycle<core::array::IntoIter<Weekday, 7>>,
830}
831
832impl Iterator for WeekdaysReverse {
833    type Item = Weekday;
834
835    #[inline]
836    fn next(&mut self) -> Option<Weekday> {
837        self.it.next()
838    }
839}
840
841impl core::iter::FusedIterator for WeekdaysReverse {}
842
843#[cfg(test)]
844mod tests {
845    use super::*;
846
847    quickcheck::quickcheck! {
848        fn prop_since_add_equals_self(wd1: Weekday, wd2: Weekday) -> bool {
849            let days = wd1.since(wd2);
850            wd2.wrapping_add(days) == wd1
851        }
852
853        fn prop_until_add_equals_other(wd1: Weekday, wd2: Weekday) -> bool {
854            let days = wd1.until(wd2);
855            wd1.wrapping_add(days) == wd2
856        }
857    }
858}