1use crate::{
2 bounds::{self as b, RangeError},
3 civil::{self, DateTime},
4 constants as c,
5 macros::{rbail, rtry, unwrapr},
6};
7
8#[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 pub const MIN: Time =
26 Time { hour: 0, minute: 0, second: 0, subsec_nanosecond: 0 };
27
28 pub const MAX: Time = Time {
32 hour: 23,
33 minute: 59,
34 second: 59,
35 subsec_nanosecond: 999_999_999,
36 };
37
38 #[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 #[inline]
62 pub const fn hour(self) -> i8 {
63 self.hour
64 }
65
66 #[inline]
71 pub const fn minute(self) -> i8 {
72 self.minute
73 }
74
75 #[inline]
80 pub const fn second(self) -> i8 {
81 self.second
82 }
83
84 #[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 #[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 #[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 #[inline]
115 pub const fn subsec_nanosecond(self) -> i32 {
116 self.subsec_nanosecond
117 }
118
119 #[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 #[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 #[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 #[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 #[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 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 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#[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 #[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 #[inline]
296 pub const fn constant(second: i32) -> TimeSecond {
297 unwrapr!(TimeSecond::new(second), "invalid civil day second")
298 }
299
300 #[inline]
305 pub const fn second(self) -> i32 {
306 self.second
307 }
308
309 #[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 #[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#[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 #[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 #[inline]
379 pub const fn nanosecond(self) -> i64 {
380 self.nanosecond
381 }
382
383 #[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 #[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}