1#![allow(dead_code)]
9
10use crate::{Error, SignedDuration};
11
12pub(crate) const DAYS_PER_WEEK: i64 = 7;
13pub(crate) const HOURS_PER_CIVIL_DAY: i64 = 24;
14pub(crate) const MINS_PER_CIVIL_DAY: i64 = HOURS_PER_CIVIL_DAY * MINS_PER_HOUR;
15pub(crate) const MINS_PER_HOUR: i64 = 60;
16pub(crate) const SECS_PER_WEEK: i64 = DAYS_PER_WEEK * SECS_PER_CIVIL_DAY;
17pub(crate) const SECS_PER_CIVIL_DAY: i64 = HOURS_PER_CIVIL_DAY * SECS_PER_HOUR;
18pub(crate) const SECS_PER_HOUR: i64 = SECS_PER_MIN * MINS_PER_HOUR;
19pub(crate) const SECS_PER_MIN: i64 = 60;
20pub(crate) const MILLIS_PER_CIVIL_DAY: i64 =
21 SECS_PER_CIVIL_DAY * MILLIS_PER_SEC;
22pub(crate) const MILLIS_PER_SEC: i64 = 1_000;
23pub(crate) const MICROS_PER_CIVIL_DAY: i64 =
24 SECS_PER_CIVIL_DAY * MICROS_PER_SEC;
25pub(crate) const MICROS_PER_SEC: i64 = MILLIS_PER_SEC * MICROS_PER_MILLI;
26pub(crate) const MICROS_PER_MILLI: i64 = 1_000;
27pub(crate) const NANOS_PER_WEEK: i64 = DAYS_PER_WEEK * NANOS_PER_CIVIL_DAY;
28pub(crate) const NANOS_PER_CIVIL_DAY: i64 =
29 HOURS_PER_CIVIL_DAY * NANOS_PER_HOUR;
30pub(crate) const NANOS_PER_HOUR: i64 = MINS_PER_HOUR * NANOS_PER_MIN;
31pub(crate) const NANOS_PER_MIN: i64 = SECS_PER_MIN * NANOS_PER_SEC;
32pub(crate) const NANOS_PER_SEC: i64 = MILLIS_PER_SEC * NANOS_PER_MILLI;
33pub(crate) const NANOS_PER_MILLI: i64 = MICROS_PER_MILLI * NANOS_PER_MICRO;
34pub(crate) const NANOS_PER_MICRO: i64 = 1_000;
35
36pub(crate) const DAYS_PER_WEEK_32: i32 = 7;
37pub(crate) const HOURS_PER_CIVIL_DAY_32: i32 = 24;
38pub(crate) const MINS_PER_HOUR_32: i32 = 60;
39pub(crate) const SECS_PER_CIVIL_DAY_32: i32 =
40 HOURS_PER_CIVIL_DAY_32 * SECS_PER_HOUR_32;
41pub(crate) const SECS_PER_HOUR_32: i32 = SECS_PER_MIN_32 * MINS_PER_HOUR_32;
42pub(crate) const SECS_PER_MIN_32: i32 = 60;
43pub(crate) const MILLIS_PER_SEC_32: i32 = 1_000;
44pub(crate) const MICROS_PER_SEC_32: i32 =
45 MILLIS_PER_SEC_32 * MICROS_PER_MILLI_32;
46pub(crate) const MICROS_PER_MILLI_32: i32 = 1_000;
47pub(crate) const NANOS_PER_SEC_32: i32 =
48 MILLIS_PER_SEC_32 * NANOS_PER_MILLI_32;
49pub(crate) const NANOS_PER_MILLI_32: i32 =
50 MICROS_PER_MILLI_32 * NANOS_PER_MICRO_32;
51pub(crate) const NANOS_PER_MICRO_32: i32 = 1_000;
52
53macro_rules! define_bounds {
60 ($((
61 $name:ident,
63 $ty:ident,
67 $what:expr,
70 $min:expr,
72 $max:expr $(,)?
74 )),* $(,)?) => {
75 $(
76 pub(crate) struct $name(());
77
78 impl Bounds for $name {
79 const WHAT: &'static str = $what;
80 const MIN: Self::Primitive = $min;
81 const MAX: Self::Primitive = $max;
82 type Primitive = $ty;
83
84 #[cold]
85 #[inline(never)]
86 fn error() -> BoundsError {
87 BoundsError::$name(RawBoundsError::new())
88 }
89 }
90
91 impl $name {
92 pub(crate) const MIN: $ty = <$name as Bounds>::MIN;
93 pub(crate) const MAX: $ty = <$name as Bounds>::MAX;
94 const LEN: i128 = Self::MAX as i128 - Self::MIN as i128 + 1;
95
96 #[cold]
97 pub(crate) const fn error() -> BoundsError {
98 BoundsError::$name(RawBoundsError::new())
99 }
100
101 #[cfg_attr(feature = "perf-inline", inline(always))]
102 pub(crate) fn check(n: impl Into<i64>) -> Result<$ty, BoundsError> {
103 <$name as Bounds>::check(n)
104 }
105
106 #[cfg_attr(feature = "perf-inline", inline(always))]
107 pub(crate) const fn checkc(n: i64) -> Result<$ty, BoundsError> {
108 match self::checkc::$ty(n) {
109 Ok(n) => Ok(n),
110 Err(err) => Err(BoundsError::$name(err)),
111 }
112 }
113
114 #[cfg_attr(feature = "perf-inline", inline(always))]
115 pub(crate) fn check128(n: impl Into<i128>) -> Result<$ty, BoundsError> {
116 <$name as Bounds>::check128(n)
117 }
118
119 #[cfg_attr(feature = "perf-inline", inline(always))]
120 pub(crate) fn parse(bytes: &[u8]) -> Result<$ty, Error> {
121 <$name as Bounds>::parse(bytes)
122 }
123
124 #[cfg_attr(feature = "perf-inline", inline(always))]
125 pub(crate) fn checked_add(n1: $ty, n2: $ty) -> Result<$ty, BoundsError> {
126 <$name as Bounds>::checked_add(n1, n2)
127 }
128
129 #[cfg_attr(feature = "perf-inline", inline(always))]
130 pub(crate) fn checked_sub(n1: $ty, n2: $ty) -> Result<$ty, BoundsError> {
131 <$name as Bounds>::checked_sub(n1, n2)
132 }
133
134 #[cfg_attr(feature = "perf-inline", inline(always))]
135 pub(crate) fn checked_mul(n1: $ty, n2: $ty) -> Result<$ty, BoundsError> {
136 <$name as Bounds>::checked_mul(n1, n2)
137 }
138
139 #[cfg(test)]
140 pub(crate) fn arbitrary(g: &mut quickcheck::Gen) -> $ty {
141 use quickcheck::Arbitrary;
142
143 let mut n: $ty = <$ty>::arbitrary(g);
144 n = n.wrapping_rem_euclid(Self::LEN as $ty);
145 n += Self::MIN;
146 n
147 }
148 }
149 )*
150
151 #[derive(Clone, Debug)]
153 pub(crate) enum BoundsError {
154 $($name(RawBoundsError<$name>),)*
155 }
156
157 impl core::fmt::Display for BoundsError {
158 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
159 match *self {
160 $(BoundsError::$name(ref err) => err.fmt(f),)*
161 }
162 }
163 }
164 }
165}
166
167define_bounds! {
168 (Century, i8, "century", 0, 99),
169 (
170 CivilDayNanosecond,
171 i64,
172 "nanoseconds (in one civil day)",
173 0,
174 NANOS_PER_CIVIL_DAY - 1,
175 ),
176 (
177 CivilDaySecond,
178 i32,
179 "seconds (in one civil day)",
180 0,
181 SECS_PER_CIVIL_DAY_32 - 1,
182 ),
183 (Day, i8, "day", 1, 31),
184 (DayOfYear, i16, "day-of-year", 1, 366),
185 (Hour, i8, "hour", 0, 23),
186 (Hour12, i8, "hour (12 hour clock)", 1, 12),
187 (ISOWeek, i8, "iso-week", 1, 53),
188 (ISOYear, i16, "iso-year", -9999, 9999),
189 (Increment, i64, "rounding increment", 1, 1_000_000_000),
192 (Increment32, i32, "rounding increment", 1, 1_000_000_000),
193 (LeapSecond, i8, "second", 0, 60),
195 (Microsecond, i16, "microsecond", 0, 999),
196 (Millisecond, i16, "millisecond", 0, 999),
197 (Minute, i8, "minute", 0, 59),
198 (Month, i8, "month", 1, 12),
199 (Nanosecond, i16, "nanosecond", 0, 999),
200 (NthWeekday, i32, "nth weekday", SpanWeeks::MIN, SpanWeeks::MAX),
201 (OffsetHours, i8, "time zone offset hours", -25, 25),
222 (OffsetMinutes, i8, "time zone offset minutes", -59, 59),
223 (OffsetSeconds, i8, "time zone offset seconds", -59, 59),
224 (
225 OffsetTotalSeconds,
226 i32,
227 "time zone offset total seconds",
228 -Self::MAX,
229 (OffsetHours::MAX as i32 * SECS_PER_HOUR_32)
230 + (OffsetMinutes::MAX as i32 * MINS_PER_HOUR_32)
231 + OffsetSeconds::MAX as i32,
232 ),
233 (Second, i8, "second", 0, 59),
234 (SignedDurationSeconds, i64, "signed duration seconds", i64::MIN, i64::MAX),
235 (SpanYears, i16, "years", -Self::MAX, (Year::LEN - 1) as i16),
236 (SpanMonths, i32, "months", -Self::MAX, SpanYears::MAX as i32 * 12),
237 (SpanWeeks, i32, "weeks", -Self::MAX, SpanDays::MAX / DAYS_PER_WEEK_32),
238 (SpanDays, i32, "days", -Self::MAX, SpanHours::MAX / HOURS_PER_CIVIL_DAY_32),
239 (SpanHours, i32, "hours", -Self::MAX, (SpanMinutes::MAX / MINS_PER_HOUR) as i32),
240 (SpanMinutes, i64, "minutes", -Self::MAX, SpanSeconds::MAX / SECS_PER_MIN),
241 (
269 SpanSeconds,
270 i64,
271 "seconds",
272 -Self::MAX,
273 next_multiple_of(
274 UnixSeconds::LEN as i64
275 + OffsetTotalSeconds::MAX as i64
276 + SECS_PER_CIVIL_DAY,
277 SECS_PER_CIVIL_DAY,
278 ),
279 ),
280 (
281 SpanMilliseconds,
282 i64,
283 "milliseconds",
284 -Self::MAX,
285 SpanSeconds::MAX * MILLIS_PER_SEC,
286 ),
287 (
288 SpanMicroseconds,
289 i64,
290 "microseconds",
291 -Self::MAX,
292 SpanMilliseconds::MAX * MICROS_PER_MILLI,
293 ),
294 (SpanMultiple, i64, "span multiple", i64::MIN + 1, i64::MAX),
295 (SpanNanoseconds, i64, "nanoseconds", i64::MIN + 1, i64::MAX),
305 (SubsecNanosecond, i32, "subsecond nanosecond", 0, NANOS_PER_SEC_32 - 1),
306 (
307 SignedSubsecNanosecond,
308 i32,
309 "subsecond nanosecond",
310 -SubsecNanosecond::MAX,
311 SubsecNanosecond::MAX,
312 ),
313 (
324 UnixEpochDays,
325 i32,
326 "Unix epoch days",
327 (UnixSeconds::MIN+ OffsetTotalSeconds::MIN as i64).div_euclid(SECS_PER_CIVIL_DAY) as i32,
328 (UnixSeconds::MAX + OffsetTotalSeconds::MAX as i64).div_euclid(SECS_PER_CIVIL_DAY) as i32,
329 ),
330 (
331 UnixMilliseconds,
332 i64,
333 "Unix timestamp milliseconds",
334 UnixSeconds::MIN * MILLIS_PER_SEC,
335 UnixSeconds::MAX * MILLIS_PER_SEC,
336 ),
337 (
338 UnixMicroseconds,
339 i64,
340 "Unix timestamp microseconds",
341 UnixMilliseconds::MIN * MICROS_PER_MILLI,
342 UnixMilliseconds::MAX * MICROS_PER_MILLI,
343 ),
344 (
379 UnixSeconds,
380 i64,
381 "Unix timestamp seconds",
382 -377705116800 - OffsetTotalSeconds::MIN as i64,
383 253402300799 - OffsetTotalSeconds::MAX as i64,
384 ),
385 (WeekNum, i8, "week-number", 0, 53),
386 (WeekdayMondayZero, i8, "weekday (Monday 0-indexed)", 0, 6),
387 (WeekdayMondayOne, i8, "weekday (Monday 1-indexed)", 1, 7),
388 (WeekdaySundayZero, i8, "weekday (Sunday 0-indexed)", 0, 6),
389 (WeekdaySundayOne, i8, "weekday (Sunday 1-indexed)", 1, 7),
390 (Year, i16, "year", -9999, 9999),
400 (YearCE, i16, "CE year", 1, Year::MAX),
401 (YearBCE, i16, "BCE year", 1, Year::MAX + 1),
402 (YearTwoDigit, i16, "year (2 digits)", 0, 99),
403 (
404 ZonedDayNanoseconds,
405 i64,
406 "nanoseconds (in one zoned datetime day)",
407 ZonedDaySeconds::MIN as i64 * NANOS_PER_SEC,
408 ZonedDaySeconds::MAX as i64 * NANOS_PER_SEC,
409 ),
410 (
417 ZonedDaySeconds,
418 i32,
419 "seconds (in one zoned datetime day)",
420 1,
421 7 * SECS_PER_CIVIL_DAY_32,
422 ),
423}
424
425pub(crate) trait Bounds: Sized {
427 const WHAT: &'static str;
430
431 const MIN: Self::Primitive;
433
434 const MAX: Self::Primitive;
436
437 type Primitive: Primitive;
447
448 fn error() -> BoundsError;
458
459 #[cfg_attr(feature = "perf-inline", inline(always))]
473 fn check(n: impl Into<i64>) -> Result<Self::Primitive, BoundsError> {
474 debug_assert!((i128::from(i64::MIN)..=i128::from(i64::MAX))
478 .contains(&Self::MIN.as_i128()));
479 debug_assert!((i128::from(i64::MIN)..=i128::from(i64::MAX))
480 .contains(&Self::MAX.as_i128()));
481
482 let n = n.into();
483 if !(Self::MIN.as_i64() <= n && n <= Self::MAX.as_i64()) {
484 return Err(Self::error());
485 }
486 Ok(Self::Primitive::from_i64(n))
487 }
488
489 #[cfg_attr(feature = "perf-inline", inline(always))]
497 fn check128(n: impl Into<i128>) -> Result<Self::Primitive, BoundsError> {
498 let n = n.into();
499 if !(Self::MIN.as_i128() <= n && n <= Self::MAX.as_i128()) {
500 return Err(Self::error());
501 }
502 Ok(Self::Primitive::from_i128(n))
503 }
504
505 #[cfg_attr(feature = "perf-inline", inline(always))]
513 fn check_self(n: Self::Primitive) -> Result<Self::Primitive, BoundsError> {
514 if !(Self::MIN <= n && n <= Self::MAX) {
515 return Err(Self::error());
516 }
517 Ok(n)
518 }
519
520 #[cfg_attr(feature = "perf-inline", inline(always))]
535 fn parse(bytes: &[u8]) -> Result<Self::Primitive, Error> {
536 Ok(Self::check(crate::util::parse::i64(bytes)?)?)
537 }
538
539 #[cfg_attr(feature = "perf-inline", inline(always))]
547 fn checked_add(
548 n1: Self::Primitive,
549 n2: Self::Primitive,
550 ) -> Result<Self::Primitive, BoundsError> {
551 Self::check_self(n1.checked_add(n2).ok_or_else(Self::error)?)
552 }
553
554 #[cfg_attr(feature = "perf-inline", inline(always))]
562 fn checked_sub(
563 n1: Self::Primitive,
564 n2: Self::Primitive,
565 ) -> Result<Self::Primitive, BoundsError> {
566 Self::check_self(n1.checked_sub(n2).ok_or_else(Self::error)?)
567 }
568
569 #[cfg_attr(feature = "perf-inline", inline(always))]
577 fn checked_mul(
578 n1: Self::Primitive,
579 n2: Self::Primitive,
580 ) -> Result<Self::Primitive, BoundsError> {
581 Self::check_self(n1.checked_mul(n2).ok_or_else(Self::error)?)
582 }
583}
584
585pub(crate) trait Primitive:
589 Clone
590 + Copy
591 + Eq
592 + PartialEq
593 + PartialOrd
594 + Ord
595 + core::fmt::Debug
596 + core::fmt::Display
597{
598 fn as_i8(self) -> i8;
599 fn as_i16(self) -> i16;
600 fn as_i32(self) -> i32;
601 fn as_i64(self) -> i64;
602 fn as_i128(self) -> i128;
603
604 fn from_i8(n: i8) -> Self;
605 fn from_i16(n: i16) -> Self;
606 fn from_i32(n: i32) -> Self;
607 fn from_i64(n: i64) -> Self;
608 fn from_i128(n: i128) -> Self;
609
610 fn checked_add(self, n: Self) -> Option<Self>;
611 fn checked_sub(self, n: Self) -> Option<Self>;
612 fn checked_mul(self, n: Self) -> Option<Self>;
613}
614
615macro_rules! impl_primitive {
616 ($($intty:ty),*) => {
617 $(
618 impl Primitive for $intty {
619 fn as_i8(self) -> i8 { self as i8 }
620 fn as_i16(self) -> i16 { self as i16 }
621 fn as_i32(self) -> i32 { self as i32 }
622 fn as_i64(self) -> i64 { self as i64 }
623 fn as_i128(self) -> i128 { self as i128 }
624
625 fn from_i8(n: i8) -> Self { n as $intty }
626 fn from_i16(n: i16) -> Self { n as $intty }
627 fn from_i32(n: i32) -> Self { n as $intty }
628 fn from_i64(n: i64) -> Self { n as $intty }
629 fn from_i128(n: i128) -> Self { n as $intty }
630
631 fn checked_add(self, n: $intty) -> Option<$intty> {
632 <$intty>::checked_add(self, n)
633 }
634
635 fn checked_sub(self, n: $intty) -> Option<$intty> {
636 <$intty>::checked_sub(self, n)
637 }
638
639 fn checked_mul(self, n: $intty) -> Option<$intty> {
640 <$intty>::checked_mul(self, n)
641 }
642 }
643 )*
644 }
645}
646
647impl_primitive!(i8, i16, i32, i64, i128);
648
649impl From<BoundsError> for Error {
650 fn from(err: BoundsError) -> Error {
651 Error::bounds(err)
652 }
653}
654
655impl crate::error::IntoError for BoundsError {
656 fn into_error(self) -> Error {
657 self.into()
658 }
659}
660
661pub(crate) struct RawBoundsError<B>(core::marker::PhantomData<B>);
662
663impl<B> RawBoundsError<B> {
664 const fn new() -> RawBoundsError<B> {
665 RawBoundsError(core::marker::PhantomData)
666 }
667}
668
669impl<B> Clone for RawBoundsError<B> {
670 fn clone(&self) -> RawBoundsError<B> {
671 RawBoundsError::new()
672 }
673}
674
675impl<B, P> core::fmt::Debug for RawBoundsError<B>
676where
677 B: Bounds<Primitive = P>,
678 P: core::fmt::Debug,
679{
680 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
681 f.debug_struct("RawBoundsError")
682 .field("what", &B::WHAT)
683 .field("min", &B::MIN)
684 .field("max", &B::MAX)
685 .finish()
686 }
687}
688
689impl<B, P> core::fmt::Display for RawBoundsError<B>
690where
691 B: Bounds<Primitive = P>,
692 P: core::fmt::Display,
693{
694 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
695 write!(
696 f,
697 "parameter '{what}' is not in the required range of {min}..={max}",
698 what = B::WHAT,
699 min = B::MIN,
700 max = B::MAX,
701 )
702 }
703}
704
705#[derive(Clone, Debug)]
709pub(crate) enum SpecialBoundsError {
710 UnixNanoseconds,
711 SignedDurationFloatOutOfRangeF32,
712 SignedDurationFloatOutOfRangeF64,
713 SignedToUnsignedDuration,
714}
715
716impl core::fmt::Display for SpecialBoundsError {
717 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
718 use self::SpecialBoundsError::*;
719
720 let (what, min, max) = match *self {
721 UnixNanoseconds => (
722 "Unix timestamp nanoseconds",
723 UnixMicroseconds::MIN as i128 * (NANOS_PER_MICRO as i128),
724 UnixMicroseconds::MAX as i128 * (NANOS_PER_MICRO as i128),
725 ),
726 SignedToUnsignedDuration => {
727 return f.write_str(
728 "negative signed durations cannot be converted \
729 to an unsigned duration",
730 );
731 }
732 SignedDurationFloatOutOfRangeF32 => {
733 return write!(
734 f,
735 "parameter 'floating point seconds' is not in \
736 the required range of {min}..={max}",
737 min = i64::MIN as f32,
738 max = i64::MAX as f32,
739 );
740 }
741 SignedDurationFloatOutOfRangeF64 => {
742 return write!(
743 f,
744 "parameter 'floating point seconds' is not in \
745 the required range of {min}..={max}",
746 min = i64::MIN as f64,
747 max = i64::MAX as f64,
748 );
749 }
750 };
751 write!(
752 f,
753 "parameter '{what}' is not in the required range of {min}..={max}",
754 )
755 }
756}
757
758impl From<SpecialBoundsError> for Error {
759 fn from(err: SpecialBoundsError) -> Error {
760 Error::special_bounds(err)
761 }
762}
763
764impl crate::error::IntoError for SpecialBoundsError {
765 fn into_error(self) -> Error {
766 self.into()
767 }
768}
769
770#[derive(
775 Clone, Copy, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord,
776)]
777#[repr(i8)]
778pub(crate) enum Sign {
779 #[default]
780 Zero = 0,
781 Positive = 1,
782 Negative = -1,
783}
784
785impl Sign {
786 pub(crate) fn is_zero(self) -> bool {
787 matches!(self, Sign::Zero)
788 }
789
790 pub(crate) fn is_positive(self) -> bool {
791 matches!(self, Sign::Positive)
792 }
793
794 pub(crate) fn is_negative(self) -> bool {
795 matches!(self, Sign::Negative)
796 }
797
798 pub(crate) fn signum(self) -> i8 {
799 self.as_i8()
800 }
801
802 pub(crate) fn as_i8(self) -> i8 {
803 self as i8
804 }
805
806 pub(crate) fn as_i16(self) -> i16 {
807 i16::from(self.as_i8())
808 }
809
810 pub(crate) fn as_i32(self) -> i32 {
811 i32::from(self.as_i8())
812 }
813
814 pub(crate) fn as_i64(self) -> i64 {
815 i64::from(self.as_i8())
816 }
817
818 pub(crate) fn as_i128(self) -> i128 {
819 i128::from(self.as_i8())
820 }
821
822 pub(crate) fn from_ordinals<T: Ord>(t1: T, t2: T) -> Sign {
823 use core::cmp::Ordering::*;
824 match t1.cmp(&t2) {
825 Less => Sign::Negative,
826 Equal => Sign::Zero,
827 Greater => Sign::Positive,
828 }
829 }
830}
831
832impl core::ops::Neg for Sign {
833 type Output = Sign;
834
835 fn neg(self) -> Sign {
836 match self {
837 Sign::Positive => Sign::Negative,
838 Sign::Zero => Sign::Zero,
839 Sign::Negative => Sign::Positive,
840 }
841 }
842}
843
844impl From<i8> for Sign {
845 fn from(n: i8) -> Sign {
846 Sign::from(i64::from(n))
847 }
848}
849
850impl From<i16> for Sign {
851 fn from(n: i16) -> Sign {
852 Sign::from(i64::from(n))
853 }
854}
855
856impl From<i32> for Sign {
857 fn from(n: i32) -> Sign {
858 Sign::from(i64::from(n))
859 }
860}
861
862impl From<i64> for Sign {
863 fn from(n: i64) -> Sign {
864 if n == 0 {
865 Sign::Zero
866 } else if n > 0 {
867 Sign::Positive
868 } else {
869 Sign::Negative
870 }
871 }
872}
873
874impl From<i128> for Sign {
875 fn from(n: i128) -> Sign {
876 if n == 0 {
877 Sign::Zero
878 } else if n > 0 {
879 Sign::Positive
880 } else {
881 Sign::Negative
882 }
883 }
884}
885
886impl From<f64> for Sign {
887 fn from(n: f64) -> Sign {
888 use core::num::FpCategory::*;
889
890 if matches!(n.classify(), Nan | Zero) {
901 Sign::Zero
902 } else if n.is_sign_positive() {
903 Sign::Positive
904 } else {
905 Sign::Negative
906 }
907 }
908}
909
910impl From<SignedDuration> for Sign {
911 fn from(n: SignedDuration) -> Sign {
912 if n.is_zero() {
913 Sign::Zero
914 } else if n.is_positive() {
915 Sign::Positive
916 } else {
917 Sign::Negative
918 }
919 }
920}
921
922impl core::ops::Mul<Sign> for Sign {
923 type Output = Sign;
924 fn mul(self, rhs: Sign) -> Sign {
925 match (self, rhs) {
926 (Sign::Zero, _) | (_, Sign::Zero) => Sign::Zero,
927 (Sign::Positive, Sign::Positive) => Sign::Positive,
928 (Sign::Negative, Sign::Negative) => Sign::Positive,
929 (Sign::Positive, Sign::Negative) => Sign::Negative,
930 (Sign::Negative, Sign::Positive) => Sign::Negative,
931 }
932 }
933}
934
935impl core::ops::Mul<i8> for Sign {
936 type Output = i8;
937 fn mul(self, n: i8) -> i8 {
938 self.as_i8() * n
939 }
940}
941
942impl core::ops::Mul<Sign> for i8 {
943 type Output = i8;
944 fn mul(self, n: Sign) -> i8 {
945 self * n.as_i8()
946 }
947}
948
949impl core::ops::Mul<i16> for Sign {
950 type Output = i16;
951 fn mul(self, n: i16) -> i16 {
952 self.as_i16() * n
953 }
954}
955
956impl core::ops::Mul<Sign> for i16 {
957 type Output = i16;
958 fn mul(self, n: Sign) -> i16 {
959 self * n.as_i16()
960 }
961}
962
963impl core::ops::Mul<i32> for Sign {
964 type Output = i32;
965 fn mul(self, n: i32) -> i32 {
966 self.as_i32() * n
967 }
968}
969
970impl core::ops::Mul<Sign> for i32 {
971 type Output = i32;
972 fn mul(self, n: Sign) -> i32 {
973 self * n.as_i32()
974 }
975}
976
977impl core::ops::Mul<i64> for Sign {
978 type Output = i64;
979 fn mul(self, n: i64) -> i64 {
980 self.as_i64() * n
981 }
982}
983
984impl core::ops::Mul<Sign> for i64 {
985 type Output = i64;
986 fn mul(self, n: Sign) -> i64 {
987 self * n.as_i64()
988 }
989}
990
991impl core::ops::Mul<i128> for Sign {
992 type Output = i128;
993 fn mul(self, n: i128) -> i128 {
994 self.as_i128() * n
995 }
996}
997
998impl core::ops::Mul<Sign> for i128 {
999 type Output = i128;
1000 fn mul(self, n: Sign) -> i128 {
1001 self * n.as_i128()
1002 }
1003}
1004
1005impl core::fmt::Display for Sign {
1006 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
1007 if self.is_negative() {
1008 f.write_str("-")
1009 } else {
1010 Ok(())
1011 }
1012 }
1013}
1014
1015mod checkc {
1016 use super::{Bounds, RawBoundsError};
1017
1018 #[cfg_attr(feature = "perf-inline", inline(always))]
1019 pub(super) const fn i8<B>(n: i64) -> Result<i8, RawBoundsError<B>>
1020 where
1021 B: Bounds<Primitive = i8>,
1022 {
1023 debug_assert!(
1027 (i64::MIN as i128) <= (B::MIN as i128)
1028 && (B::MIN as i128) <= (i64::MAX as i128),
1029 );
1030 debug_assert!(
1031 (i64::MIN as i128) <= (B::MAX as i128)
1032 && (B::MAX as i128) <= (i64::MAX as i128),
1033 );
1034
1035 if !((B::MIN as i64) <= n && n <= (B::MAX as i64)) {
1036 return Err(RawBoundsError::new());
1037 }
1038 Ok(n as i8)
1039 }
1040
1041 #[cfg_attr(feature = "perf-inline", inline(always))]
1042 pub(super) const fn i16<B>(n: i64) -> Result<i16, RawBoundsError<B>>
1043 where
1044 B: Bounds<Primitive = i16>,
1045 {
1046 debug_assert!(
1050 (i64::MIN as i128) <= (B::MIN as i128)
1051 && (B::MIN as i128) <= (i64::MAX as i128),
1052 );
1053 debug_assert!(
1054 (i64::MIN as i128) <= (B::MAX as i128)
1055 && (B::MAX as i128) <= (i64::MAX as i128),
1056 );
1057
1058 if !((B::MIN as i64) <= n && n <= (B::MAX as i64)) {
1059 return Err(RawBoundsError::new());
1060 }
1061 Ok(n as i16)
1062 }
1063
1064 #[cfg_attr(feature = "perf-inline", inline(always))]
1065 pub(super) const fn i32<B>(n: i64) -> Result<i32, RawBoundsError<B>>
1066 where
1067 B: Bounds<Primitive = i32>,
1068 {
1069 debug_assert!(
1073 (i64::MIN as i128) <= (B::MIN as i128)
1074 && (B::MIN as i128) <= (i64::MAX as i128),
1075 );
1076 debug_assert!(
1077 (i64::MIN as i128) <= (B::MAX as i128)
1078 && (B::MAX as i128) <= (i64::MAX as i128),
1079 );
1080
1081 if !((B::MIN as i64) <= n && n <= (B::MAX as i64)) {
1082 return Err(RawBoundsError::new());
1083 }
1084 Ok(n as i32)
1085 }
1086
1087 #[cfg_attr(feature = "perf-inline", inline(always))]
1088 pub(super) const fn i64<B>(n: i64) -> Result<i64, RawBoundsError<B>>
1089 where
1090 B: Bounds<Primitive = i64>,
1091 {
1092 debug_assert!(
1096 (i64::MIN as i128) <= (B::MIN as i128)
1097 && (B::MIN as i128) <= (i64::MAX as i128),
1098 );
1099 debug_assert!(
1100 (i64::MIN as i128) <= (B::MAX as i128)
1101 && (B::MAX as i128) <= (i64::MAX as i128),
1102 );
1103
1104 if !(B::MIN <= n && n <= B::MAX) {
1105 return Err(RawBoundsError::new());
1106 }
1107 Ok(n)
1108 }
1109}
1110
1111const fn next_multiple_of(lhs: i64, rhs: i64) -> i64 {
1116 if rhs == -1 {
1118 return lhs;
1119 }
1120
1121 let r = lhs % rhs;
1122 let m = if (r > 0 && rhs < 0) || (r < 0 && rhs > 0) { r + rhs } else { r };
1123 if m == 0 {
1124 lhs
1125 } else {
1126 lhs + (rhs - m)
1127 }
1128}
1129
1130#[cfg(test)]
1131mod tests {
1132 use alloc::string::ToString;
1133
1134 use super::*;
1135
1136 #[test]
1137 fn size_of_bounds_error() {
1138 assert_eq!(1, core::mem::size_of::<BoundsError>());
1142 }
1143
1144 #[test]
1145 fn basic_error_functionality() {
1146 let err = Year::check(10_000).unwrap_err();
1147 assert_eq!(
1148 err.to_string(),
1149 "parameter 'year' is not in the required range of -9999..=9999",
1150 );
1151 }
1152}