Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_datime/src/clock/second.rs

3.4 KiB, 25 runs

created by r1870400018:6333, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1//! [Written with AI entirely](https://need2know.ai/entirely-ai/code)\
2//! Anthropic Claude
3
4use crate::clock::PerSecondRated;
5
6use oxedyne_fe2o3_core::prelude::*;
7
8use std::fmt;
9
10/// 0 to 60, where 60 is a leap second: rare, but legal in UTC, so the range
11/// cannot stop at 59.
12///
13/// ```ignore
14/// use oxedyne_fe2o3_datime::clock::ClockSecondres!();
15///
16/// let second = ClockSecond::new(45)?res!();
17/// let (new_second, minute_carry) = second.add_seconds(20)res!();
18/// assert_eq!(new_second.of(), 5)res!(); // 45 + 20 = 65 -> 5 with carry
19/// assert_eq!(minute_carry, 1)res!();
20/// ```
21#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
22pub struct ClockSecond {
23 value: u8,
24}
25
26impl ClockSecond {
27 pub const MAX_VALUE: u8 = 60; // 60 is a leap second
28 pub const MILLIS_PER_SECOND: u32 = 1_000;
29 pub const MICROS_PER_SECOND: u32 = 1_000_000;
30 pub const NANOS_PER_SECOND: u64 = 1_000_000_000;
31
32 pub fn new(second: u8) -> Outcome<Self> {
33 if second > Self::MAX_VALUE {
34 return Err(err!(
35 "Second {} is invalid, must be 0-{}",
36 second,
37 Self::MAX_VALUE;
38 Invalid, Input
39 ));
40 }
41 Ok(Self { value: second })
42 }
43
44 pub(crate) fn new_unchecked(second: u8) -> Self {
45 Self { value: second }
46 }
47
48 pub fn of(&self) -> u8 {
49 self.value
50 }
51
52 /// 60 is a valid ClockSecond but not a second within a minute: it is a leap second.
53 pub fn is_valid_minute_second(&self) -> bool {
54 self.value < 60
55 }
56
57 pub fn is_leap_second(&self) -> bool {
58 self.value == 60
59 }
60
61 pub fn add_seconds(&self, seconds: u32) -> (Self, u32) {
62 let total = self.value as u32 + seconds;
63 let minute_carry = total / 60;
64 let new_second = total % 60;
65 (Self::new_unchecked(new_second as u8), minute_carry)
66 }
67
68 pub fn sub_seconds(&self, seconds: u32) -> (Self, u32) {
69 let seconds = seconds % (60 * 60 * 24); // reasonable limit
70 if seconds as u8 > self.value {
71 let borrow_needed = ((seconds as u8 - self.value + 59) / 60) as u32;
72 let effective_seconds = borrow_needed * 60 + self.value as u32 - seconds;
73 (Self::new_unchecked(effective_seconds as u8), borrow_needed)
74 } else {
75 (Self::new_unchecked(self.value - seconds as u8), 0)
76 }
77 }
78}
79
80// Validation methods.
81impl ClockSecond {
82 pub fn is_valid(&self) -> bool {
83 self.value <= Self::MAX_VALUE
84 }
85}
86
87impl PerSecondRated for ClockSecond {
88 fn per_second(&self) -> u64 {
89 1
90 }
91}
92
93impl fmt::Display for ClockSecond {
94 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95 write!(f, "{:02}", self.value)
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 fn test_second_creation() {
105 assert!(ClockSecond::new(0).is_ok());
106 assert!(ClockSecond::new(30).is_ok());
107 assert!(ClockSecond::new(59).is_ok());
108 assert!(ClockSecond::new(60).is_ok()); // leap second
109 assert!(ClockSecond::new(61).is_err());
110 }
111
112 #[test]
113 fn test_second_arithmetic() {
114 let second = ClockSecond::new(30).unwrap();
115 let (new_sec, carry) = second.add_seconds(20);
116 assert_eq!(new_sec.of(), 50);
117 assert_eq!(carry, 0);
118
119 let (new_sec, carry) = second.add_seconds(45);
120 assert_eq!(new_sec.of(), 15);
121 assert_eq!(carry, 1);
122
123 let (new_sec, borrow) = second.sub_seconds(10);
124 assert_eq!(new_sec.of(), 20);
125 assert_eq!(borrow, 0);
126
127 let (new_sec, borrow) = second.sub_seconds(45);
128 assert_eq!(new_sec.of(), 45);
129 assert_eq!(borrow, 1);
130 }
131
132 #[test]
133 fn test_per_second_rated() {
134 let second = ClockSecond::new(42).unwrap();
135 assert_eq!(second.per_second(), 1);
136 }
137}