Skip to main content

jiff_core/civil/
time.rs

1use crate::{
2    bounds::{self as b, RangeError},
3    civil::{self, DateTime},
4    constants as c,
5    macros::{rbail, rtry, unwrapr},
6};
7
8/// The civil time of day.
9///
10/// This time's representation uses nanosecond precision. The full range of
11/// clock values are `00:00:00.000000000` to `23:59:59.999999999` inclusive.
12#[derive(Clone, Copy, Eq, Hash, PartialEq, PartialOrd, Ord)]
13#[cfg_attr(feature = "defmt", derive(defmt::Format))]
14pub struct Time {
15    hour: i8,
16    minute: i8,
17    second: i8,
18    subsec_nanosecond: i32,
19}
20
21impl Time {
22    /// The minimum allowed civil time.
23    ///
24    /// This corresponds to midnight.
25    pub const MIN: Time =
26        Time { hour: 0, minute: 0, second: 0, subsec_nanosecond: 0 };
27
28    /// The maximum allowed civil time.
29    ///
30    /// This corresponds to the last nanosecond in a civil day.
31    pub const MAX: Time = Time {
32        hour: 23,
33        minute: 59,
34        second: 59,
35        subsec_nanosecond: 999_999_999,
36    };
37
38    /// Creates a new civil time from its constituent components.
39    ///
40    /// If any of the values are out of their supported ranges, then an error
41    /// is returned.
42    #[inline]
43    pub const fn new(
44        hour: i8,
45        minute: i8,
46        second: i8,
47        subsec_nanosecond: i32,
48    ) -> Result<Time, RangeError> {
49        let hour = rtry!(b::Hour::checkc(hour as i64));
50        let minute = rtry!(b::Minute::checkc(minute as i64));
51        let second = rtry!(b::Second::checkc(second as i64));
52        let subsec_nanosecond =
53            rtry!(b::SubsecNanosecond::checkc(subsec_nanosecond as i64));
54        Ok(Time { hour, minute, second, subsec_nanosecond })
55    }
56
57    /// Returns the hour component of this civil time.
58    ///
59    /// The value returned is guaranteed to be in the range specified by
60    /// [`Hour`](crate::bounds::Hour).
61    #[inline]
62    pub const fn hour(self) -> i8 {
63        self.hour
64    }
65
66    /// Returns the minute component of this civil time.
67    ///
68    /// The value returned is guaranteed to be in the range specified by
69    /// [`Minute`](crate::bounds::Minute).
70    #[inline]
71    pub const fn minute(self) -> i8 {
72        self.minute
73    }
74
75    /// Returns the second component of this civil time.
76    ///
77    /// The value returned is guaranteed to be in the range specified by
78    /// [`Second`](crate::bounds::Second).
79    #[inline]
80    pub const fn second(self) -> i8 {
81        self.second
82    }
83
84    /// Returns the "millisecond" component of this time.
85    ///
86    /// The value returned is guaranteed to be in the range `0..=999`.
87    #[inline]
88    pub const fn millisecond(self) -> i16 {
89        (self.subsec_nanosecond() as u32 / c::NANOS_PER_MILLI_32 as u32) as i16
90    }
91
92    /// Returns the "microsecond" component of this time.
93    ///
94    /// The value returned is guaranteed to be in the range `0..=999`.
95    #[inline]
96    pub const fn microsecond(self) -> i16 {
97        ((self.subsec_nanosecond() as u32 / c::NANOS_PER_MICRO_32 as u32)
98            % c::MICROS_PER_MILLI_32 as u32) as i16
99    }
100
101    /// Returns the "nanosecond" component of this time.
102    ///
103    /// The value returned is guaranteed to be in the range `0..=999`.
104    #[inline]
105    pub const fn nanosecond(self) -> i16 {
106        (self.subsec_nanosecond() as u32 % c::NANOS_PER_MICRO_32 as u32) as i16
107    }
108
109    /// Returns the fractional second (to nanosecond precision) component of
110    /// this civil time.
111    ///
112    /// The value returned is guaranteed to be in the range specified by
113    /// [`SubsecNanosecond`](crate::bounds::SubsecNanosecond).
114    #[inline]
115    pub const fn subsec_nanosecond(self) -> i32 {
116        self.subsec_nanosecond
117    }
118
119    /// Returns this time with its subsecond component replaced with the three
120    /// given subsecond components.
121    ///
122    /// If any of the given components are out of range (`0..=999`), then an
123    /// error is returned.
124    #[inline]
125    pub const fn with_subsec_parts(
126        self,
127        millisecond: i16,
128        microsecond: i16,
129        nanosecond: i16,
130    ) -> Result<Time, RangeError> {
131        let millisecond = rtry!(b::Millisecond::checkc(millisecond as i64));
132        let microsecond = rtry!(b::Microsecond::checkc(microsecond as i64));
133        let nanosecond = rtry!(b::Nanosecond::checkc(nanosecond as i64));
134        let subsec_nanosecond = (millisecond as i32 * c::NANOS_PER_MILLI_32)
135            + (microsecond as i32 * c::NANOS_PER_MICRO_32)
136            + (nanosecond as i32);
137        Ok(Time { subsec_nanosecond, ..self })
138    }
139
140    /// Returns this time with its subsecond component replaced with the
141    /// nanosecond component given.
142    ///
143    /// If the number of nanoseconds is out of range (`0..=999_999_999`), then
144    /// an error is returned.
145    #[inline]
146    pub const fn with_subsec_nanosecond(
147        self,
148        subsec_nanosecond: i32,
149    ) -> Result<Time, RangeError> {
150        let subsec_nanosecond =
151            rtry!(b::SubsecNanosecond::checkc(subsec_nanosecond as i64));
152        Ok(Time { subsec_nanosecond, ..self })
153    }
154
155    /// Converts this civil time to a second value corresponding to the number
156    /// of seconds that has elapsed since midnight until this time. If this
157    /// time is midnight, then the second value returned is `0`.
158    ///
159    /// Note that this drops any subsecond component on this civil time.
160    ///
161    /// The value returned is guaranteed to be in the range specified by
162    /// [`CivilDaySecond`](crate::bounds::CivilDaySecond).
163    #[inline]
164    pub const fn to_second(self) -> TimeSecond {
165        let mut second: i32 = 0;
166        second += (self.hour() as i32) * 3600;
167        second += (self.minute() as i32) * 60;
168        second += self.second() as i32;
169        TimeSecond { second }
170    }
171
172    /// Converts this civil time to a nanosecond value corresponding to the
173    /// number of nanoseconds that has elapsed since midnight until this time.
174    /// If this time is midnight, then the nanosecond value returned is `0`.
175    ///
176    /// The value returned is guaranteed to be in the range specified by
177    /// [`CivilDayNanosecond`](crate::bounds::CivilDayNanosecond).
178    #[inline]
179    pub const fn to_nanosecond(self) -> TimeNanosecond {
180        let mut nanosecond: i64 = 0;
181        nanosecond += (self.hour() as i64) * 3_600_000_000_000;
182        nanosecond += (self.minute() as i64) * 60_000_000_000;
183        nanosecond += (self.second() as i64) * 1_000_000_000;
184        nanosecond += self.subsec_nanosecond() as i64;
185        TimeNanosecond { nanosecond }
186    }
187
188    /// A convenience function for constructing a [`DateTime`] from this time
189    /// on the date given by its components.
190    ///
191    /// # Panics
192    ///
193    /// This routine panics when [`Date::new`](crate::civil::Date) with
194    /// the given inputs would return an error. That is, when the given
195    /// year-month-day does not correspond to a valid date. Namely, all of the
196    /// following must be true:
197    ///
198    /// * The year must be in the range `-9999..=9999`.
199    /// * The month must be in the range `1..=12`.
200    /// * The day must be at least `1` and must be at most the number of days
201    /// in the corresponding month. So for example, `2024-02-29` is valid but
202    /// `2023-02-29` is not.
203    ///
204    /// Similarly, when used in a const context, invalid parameters will
205    /// prevent your Rust program from compiling.
206    #[inline]
207    pub const fn on(self, year: i16, month: i8, day: i8) -> DateTime {
208        DateTime::from_parts(civil::date(year, month, day), self)
209    }
210}
211
212impl core::fmt::Debug for Time {
213    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
214        write!(
215            f,
216            "{:02}:{:02}:{:02}",
217            self.hour(),
218            self.minute(),
219            self.second()
220        )?;
221        let mut subsec = self.subsec_nanosecond();
222        if subsec == 0 {
223            return Ok(());
224        }
225
226        // This is really annoying. But we don't have Jiff's formatting
227        // facilities to handle this for us. We should also support precision
228        // settings from `Formatter`.
229        let mut buf = [b'0'; 9];
230        for i in (0..9).rev() {
231            buf[i] += (subsec % 10) as u8;
232            subsec /= 10;
233        }
234        let mut end = 9;
235        while end > 0 && buf[usize::from(end) - 1] == b'0' {
236            end -= 1;
237        }
238        // OK because `buf` only ever contains ASCII digits.
239        let fractional_digits = core::str::from_utf8(&buf[..end]).unwrap();
240        write!(f, ".{fractional_digits}")
241    }
242}
243
244#[cfg(test)]
245impl quickcheck::Arbitrary for Time {
246    fn arbitrary(g: &mut quickcheck::Gen) -> Time {
247        let hour = b::Hour::arbitrary(g);
248        let minute = b::Minute::arbitrary(g);
249        let second = b::Second::arbitrary(g);
250        let subsec_nanosecond = b::SubsecNanosecond::arbitrary(g);
251        Time::new(hour, minute, second, subsec_nanosecond).unwrap()
252    }
253
254    fn shrink(&self) -> alloc::boxed::Box<dyn Iterator<Item = Time>> {
255        alloc::boxed::Box::new(
256            (
257                self.hour(),
258                self.minute(),
259                self.second(),
260                self.subsec_nanosecond(),
261            )
262                .shrink()
263                .filter_map(
264                    |(hour, minute, second, subsec_nanosecond)| {
265                        Time::new(hour, minute, second, subsec_nanosecond).ok()
266                    },
267                ),
268        )
269    }
270}
271
272/// Represents a single point in a civil day, to second precision.
273#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
274#[cfg_attr(feature = "defmt", derive(defmt::Format))]
275pub struct TimeSecond {
276    second: i32,
277}
278
279impl TimeSecond {
280    /// Creates a new civil time from a given second value.
281    ///
282    /// The value must correspond to the number of seconds elapsed since
283    /// the start of a civil day. It cannot exceed the length of a civil day
284    /// (in seconds).
285    #[inline]
286    pub const fn new(second: i32) -> Result<TimeSecond, RangeError> {
287        let second = rtry!(b::CivilDaySecond::checkc(second as i64));
288        Ok(TimeSecond { second })
289    }
290
291    /// Creates a new civil time from a given second value.
292    ///
293    /// This panics when `second` exceeds the maximum number of seconds in a
294    /// single civil day.
295    #[inline]
296    pub const fn constant(second: i32) -> TimeSecond {
297        unwrapr!(TimeSecond::new(second), "invalid civil day second")
298    }
299
300    /// Returns the second value.
301    ///
302    /// The value returned is guaranteed to be in the range specified by
303    /// [`CivilDaySecond`](crate::bounds::CivilDaySecond).
304    #[inline]
305    pub const fn second(self) -> i32 {
306        self.second
307    }
308
309    /// Adds the given number of seconds to this civil time and returns the
310    /// resulting civil time with any overflowing amount in units of civil
311    /// days.
312    ///
313    /// This returns an error when integer overflow occurs. For example, when
314    /// `seconds` is `i32::MAX` and this civil time is any time other than
315    /// midnight.
316    ///
317    /// Note that the number of days returned may exceed the range supported
318    /// by [`UnixEpochDay`](crate::civil::UnixEpochDay). Moreover, the number
319    /// of days returned may be negative. This occurs only when `seconds` is
320    /// negative enough to result in the time wrapping around at `0`.
321    #[inline]
322    pub const fn overflowing_add(
323        self,
324        seconds: i32,
325    ) -> Result<(TimeSecond, i32), RangeError> {
326        let Some(sum) = self.second().checked_add(seconds) else {
327            rbail!(b::CivilDaySecond::error());
328        };
329        let days = sum.div_euclid(c::SECS_PER_CIVIL_DAY_32);
330        let rem = sum.rem_euclid(c::SECS_PER_CIVIL_DAY_32);
331        Ok((TimeSecond { second: rem }, days))
332    }
333
334    /// Converts this second representation of a civil time into the
335    /// components of a civil time.
336    ///
337    /// The subsecond component on the `Time` returned is always `0`.
338    #[inline]
339    pub const fn to_time(&self) -> Time {
340        let mut second = self.second as u32;
341        let mut time = Time::MIN;
342        if second != 0 {
343            time.hour = (second / 3600) as i8;
344            second %= 3600;
345            if second != 0 {
346                time.minute = (second / 60) as i8;
347                time.second = (second % 60) as i8;
348            }
349        }
350        time
351    }
352}
353
354/// Represents a single point in a civil day, to nanosecond precision.
355#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
356#[cfg_attr(feature = "defmt", derive(defmt::Format))]
357pub struct TimeNanosecond {
358    nanosecond: i64,
359}
360
361impl TimeNanosecond {
362    /// Creates a new civil time from a given nanosecond value.
363    ///
364    /// The value must correspond to the number of nanoseconds elapsed since
365    /// the start of a civil day. It cannot exceed the length of a civil day
366    /// (in nanoseconds).
367    #[inline]
368    pub const fn new(nanosecond: i64) -> Result<TimeNanosecond, RangeError> {
369        let nanosecond =
370            rtry!(b::CivilDayNanosecond::checkc(nanosecond as i64));
371        Ok(TimeNanosecond { nanosecond })
372    }
373
374    /// Returns the nanosecond value.
375    ///
376    /// The value returned is guaranteed to be in the range specified by
377    /// [`CivilDayNanosecond`](crate::bounds::CivilDayNanosecond).
378    #[inline]
379    pub const fn nanosecond(self) -> i64 {
380        self.nanosecond
381    }
382
383    /// Adds the given number of nanoseconds to this civil time and returns the
384    /// resulting civil time with any overflowing amount in units of civil
385    /// days.
386    ///
387    /// This returns an error when integer overflow occurs. For example, when
388    /// `seconds` is `i64::MAX` and this civil time is any time other than
389    /// midnight.
390    ///
391    /// Note that the number of days returned may exceed the range supported by
392    /// [`UnixEpochDay`](crate::civil::UnixEpochDay). Moreover, the number of
393    /// days returned may be negative. This occurs only when `nanoseconds` is
394    /// negative enough to result in the time wrapping around at `0`.
395    #[inline]
396    pub const fn overflowing_add(
397        self,
398        nanoseconds: i64,
399    ) -> Result<(TimeNanosecond, i64), RangeError> {
400        let Some(sum) = self.nanosecond().checked_add(nanoseconds) else {
401            rbail!(b::CivilDayNanosecond::error());
402        };
403        let days = sum.div_euclid(c::NANOS_PER_CIVIL_DAY);
404        let rem = sum.rem_euclid(c::NANOS_PER_CIVIL_DAY);
405        Ok((TimeNanosecond { nanosecond: rem }, days))
406    }
407
408    /// Converts this second representation of a civil time into the
409    /// components of a civil time.
410    ///
411    /// The subsecond component on the `Time` returned is always `0`.
412    #[inline]
413    pub const fn to_time(&self) -> Time {
414        let mut nanosecond = self.nanosecond as u64;
415        let mut time = Time::MIN;
416        if nanosecond != 0 {
417            time.hour = (nanosecond / 3_600_000_000_000) as i8;
418            nanosecond %= 3_600_000_000_000;
419            if nanosecond != 0 {
420                time.minute = (nanosecond / 60_000_000_000) as i8;
421                nanosecond %= 60_000_000_000;
422                if nanosecond != 0 {
423                    time.second = (nanosecond / 1_000_000_000) as i8;
424                    time.subsec_nanosecond =
425                        (nanosecond % 1_000_000_000) as i32;
426                }
427            }
428        }
429        time
430    }
431}
432
433#[cfg(test)]
434mod tests {
435    use super::*;
436
437    fn time(hour: i8, minute: i8, second: i8) -> Time {
438        timesub(hour, minute, second, 0)
439    }
440
441    fn timesub(hour: i8, minute: i8, second: i8, subsec: i32) -> Time {
442        Time::new(hour, minute, second, subsec).unwrap()
443    }
444
445    fn timesec(second: i32) -> TimeSecond {
446        TimeSecond::new(second).unwrap()
447    }
448
449    fn timenano(nanosecond: i64) -> TimeNanosecond {
450        TimeNanosecond::new(nanosecond).unwrap()
451    }
452
453    #[test]
454    fn time_to_second_various() {
455        let t = time(0, 0, 0);
456        assert_eq!(t.to_second().second(), 0);
457
458        let t = timesub(0, 0, 0, 1);
459        assert_eq!(t.to_second().second(), 0);
460
461        let t = timesub(0, 0, 0, 999_999_999);
462        assert_eq!(t.to_second().second(), 0);
463
464        let t = time(0, 0, 1);
465        assert_eq!(t.to_second().second(), 1);
466
467        let t = time(0, 1, 1);
468        assert_eq!(t.to_second().second(), 60 + 1);
469
470        let t = time(1, 1, 1);
471        assert_eq!(t.to_second().second(), 3600 + 60 + 1);
472
473        let t = time(23, 59, 59);
474        assert_eq!(t.to_second().second(), 86_399);
475    }
476
477    #[test]
478    fn second_to_time_various() {
479        let ts = timesec(0);
480        assert_eq!(ts.to_time(), time(0, 0, 0));
481
482        let ts = timesec(1);
483        assert_eq!(ts.to_time(), time(0, 0, 1));
484
485        let ts = timesec(60 + 1);
486        assert_eq!(ts.to_time(), time(0, 1, 1));
487
488        let ts = timesec(3600 + 60 + 1);
489        assert_eq!(ts.to_time(), time(1, 1, 1));
490
491        let ts = timesec(86_399);
492        assert_eq!(ts.to_time(), time(23, 59, 59));
493    }
494
495    #[test]
496    fn time_to_nanosecond_various() {
497        let t = timesub(0, 0, 0, 0);
498        assert_eq!(t.to_nanosecond().nanosecond(), 0);
499
500        let t = timesub(0, 0, 0, 1);
501        assert_eq!(t.to_nanosecond().nanosecond(), 1);
502
503        let t = timesub(0, 0, 0, 999_999_999);
504        assert_eq!(t.to_nanosecond().nanosecond(), 999_999_999);
505
506        let t = timesub(0, 0, 1, 1);
507        assert_eq!(t.to_nanosecond().nanosecond(), 1_000_000_000 + 1);
508
509        let t = timesub(0, 1, 1, 1);
510        assert_eq!(
511            t.to_nanosecond().nanosecond(),
512            (60 + 1) * 1_000_000_000 + 1
513        );
514
515        let t = timesub(1, 1, 1, 1);
516        assert_eq!(
517            t.to_nanosecond().nanosecond(),
518            (3600 + 60 + 1) * 1_000_000_000 + 1
519        );
520
521        let t = timesub(23, 59, 59, 1);
522        assert_eq!(t.to_nanosecond().nanosecond(), 86_399 * 1_000_000_000 + 1);
523
524        let t = timesub(23, 59, 59, 999_999_999);
525        assert_eq!(
526            t.to_nanosecond().nanosecond(),
527            86_399 * 1_000_000_000 + 999_999_999
528        );
529    }
530
531    #[test]
532    fn nanosecond_to_time_various() {
533        let ts = timenano(0);
534        assert_eq!(ts.to_time(), timesub(0, 0, 0, 0));
535
536        let ts = timenano(1);
537        assert_eq!(ts.to_time(), timesub(0, 0, 0, 1));
538
539        let ts = timenano(1_000_000_000 + 1);
540        assert_eq!(ts.to_time(), timesub(0, 0, 1, 1));
541
542        let ts = timenano(61 * 1_000_000_000 + 1);
543        assert_eq!(ts.to_time(), timesub(0, 1, 1, 1));
544
545        let ts = timenano((3600 + 60 + 1) * 1_000_000_000 + 1);
546        assert_eq!(ts.to_time(), timesub(1, 1, 1, 1));
547
548        let ts = timenano(86_399 * 1_000_000_000);
549        assert_eq!(ts.to_time(), timesub(23, 59, 59, 0));
550
551        let ts = timenano(86_399 * 1_000_000_000 + 1);
552        assert_eq!(ts.to_time(), timesub(23, 59, 59, 1));
553
554        let ts = timenano(86_399 * 1_000_000_000 + 999_999_999);
555        assert_eq!(ts.to_time(), timesub(23, 59, 59, 999_999_999));
556    }
557
558    #[test]
559    fn second_overflowing_add() {
560        let ts = timesec(0);
561        assert_eq!(ts.overflowing_add(86_399), Ok((timesec(86_399), 0)));
562        assert_eq!(ts.overflowing_add(86_400), Ok((timesec(0), 1)));
563        assert_eq!(ts.overflowing_add(86_401), Ok((timesec(1), 1)));
564        assert_eq!(ts.overflowing_add(i32::MAX), Ok((timesec(11_647), 24855)));
565
566        assert_eq!(ts.overflowing_add(-1), Ok((timesec(86_399), -1)));
567        assert_eq!(ts.overflowing_add(-86_399), Ok((timesec(1), -1)));
568        assert_eq!(ts.overflowing_add(-86_400), Ok((timesec(0), -1)));
569        assert_eq!(ts.overflowing_add(-86_401), Ok((timesec(86_399), -2)));
570        assert_eq!(
571            ts.overflowing_add(i32::MIN),
572            Ok((timesec(74_752), -24856))
573        );
574
575        let ts = timesec(86_399);
576        assert_eq!(
577            ts.overflowing_add(i32::MIN),
578            Ok((timesec(74_751), -24855))
579        );
580
581        let ts = timesec(1);
582        assert!(ts.overflowing_add(i32::MAX).is_err());
583    }
584
585    #[test]
586    fn nanosecond_overflowing_add() {
587        let ts = timenano(0);
588        assert_eq!(
589            ts.overflowing_add(86_399_000_000_000),
590            Ok((timenano(86_399_000_000_000), 0))
591        );
592        assert_eq!(
593            ts.overflowing_add(86_400_000_000_000),
594            Ok((timenano(0), 1))
595        );
596        assert_eq!(
597            ts.overflowing_add(86_401_000_000_000),
598            Ok((timenano(1_000_000_000), 1))
599        );
600        assert_eq!(
601            ts.overflowing_add(i64::MAX),
602            Ok((timenano(85_636_854_775_807), 106_751))
603        );
604
605        assert_eq!(
606            ts.overflowing_add(-1),
607            Ok((timenano(86_399_999_999_999), -1))
608        );
609        assert_eq!(
610            ts.overflowing_add(-1_000_000_000),
611            Ok((timenano(86_399_000_000_000), -1))
612        );
613        assert_eq!(
614            ts.overflowing_add(-86_399_000_000_000),
615            Ok((timenano(1_000_000_000), -1))
616        );
617        assert_eq!(
618            ts.overflowing_add(-86_400_000_000_000),
619            Ok((timenano(0), -1))
620        );
621        assert_eq!(
622            ts.overflowing_add(-86_401_000_000_000),
623            Ok((timenano(86_399_000_000_000), -2))
624        );
625        assert_eq!(
626            ts.overflowing_add(i64::MIN),
627            Ok((timenano(763_145_224_192), -106_752))
628        );
629
630        let ts = timenano(86_399_000_000_000);
631        assert_eq!(
632            ts.overflowing_add(i64::MIN),
633            Ok((timenano(762_145_224_192), -106_751))
634        );
635
636        let ts = timenano(1_000_000_000);
637        assert!(ts.overflowing_add(i64::MAX).is_err());
638        let ts = timenano(1);
639        assert!(ts.overflowing_add(i64::MAX).is_err());
640    }
641
642    quickcheck::quickcheck! {
643        fn prop_time_to_second_roundtrip(t: Time) -> bool {
644            let t = Time::new(t.hour(), t.minute(), t.second(), 0).unwrap();
645            t == t.to_second().to_time()
646        }
647
648        fn prop_time_to_nanosecond_roundtrip(t: Time) -> bool {
649            t == t.to_nanosecond().to_time()
650        }
651    }
652}