Oregami
Repositories/oxedyne/fe2o3

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

5.7 KiB, 32 runs

created by r1870400018:6313, 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::core::Duration;
5
6use oxedyne_fe2o3_core::prelude::*;
7
8use std::fmt;
9
10/// A signed count of nanoseconds, so a duration may run backwards, and the
11/// 64-bit range reaches about ±292 years.
12///
13/// ```ignore
14/// use oxedyne_fe2o3_datime::clock::ClockDurationres!();
15///
16/// let duration1 = ClockDuration::from_hours(2)res!();
17/// let duration2 = ClockDuration::from_minutes(30)res!();
18/// let total = duration1.plus(&duration2)res!();
19/// assert_eq!(total.total_hours(), 2)res!();
20/// assert_eq!(total.total_minutes(), 150)res!();
21/// ```
22#[derive(Clone, Debug, PartialEq)]
23pub struct ClockDuration {
24 nanos: i64,
25}
26
27impl ClockDuration {
28 pub fn from_nanos(nanos: i64) -> Self {
29 Self { nanos }
30 }
31
32 pub fn from_seconds(seconds: i64) -> Self {
33 Self { nanos: seconds * 1_000_000_000 }
34 }
35
36 pub fn from_millis(millis: i64) -> Self {
37 Self { nanos: millis * 1_000_000 }
38 }
39
40 pub fn from_micros(micros: i64) -> Self {
41 Self { nanos: micros * 1_000 }
42 }
43
44 pub fn from_minutes(minutes: i64) -> Self {
45 Self { nanos: minutes * 60 * 1_000_000_000 }
46 }
47
48 pub fn from_hours(hours: i64) -> Self {
49 Self { nanos: hours * 60 * 60 * 1_000_000_000 }
50 }
51
52 pub fn zero() -> Self {
53 Self { nanos: 0 }
54 }
55
56 pub fn total_nanos(&self) -> i64 {
57 self.nanos
58 }
59
60 /// Truncates rather than rounds, as do the coarser totals below.
61 pub fn total_micros(&self) -> i64 {
62 self.nanos / 1_000
63 }
64
65 pub fn total_millis(&self) -> i64 {
66 self.nanos / 1_000_000
67 }
68
69 pub fn total_seconds(&self) -> i64 {
70 self.nanos / 1_000_000_000
71 }
72
73 pub fn total_minutes(&self) -> i64 {
74 self.nanos / (60 * 1_000_000_000)
75 }
76
77 pub fn total_hours(&self) -> i64 {
78 self.nanos / (60 * 60 * 1_000_000_000)
79 }
80
81 pub fn abs(&self) -> Self {
82 Self { nanos: self.nanos.abs() }
83 }
84
85 pub fn negate(&self) -> Self {
86 Self { nanos: -self.nanos }
87 }
88
89 pub fn plus(&self, other: &Self) -> Self {
90 Self { nanos: self.nanos + other.nanos }
91 }
92
93 pub fn minus(&self, other: &Self) -> Self {
94 Self { nanos: self.nanos - other.nanos }
95 }
96
97 pub fn multiply_by(&self, factor: i64) -> Self {
98 Self { nanos: self.nanos * factor }
99 }
100
101 pub fn divide_by(&self, divisor: i64) -> Outcome<Self> {
102 if divisor == 0 {
103 return Err(err!("Cannot divide duration by zero"; Invalid, Input));
104 }
105 Ok(Self { nanos: self.nanos / divisor })
106 }
107
108 /// For a negative duration the sign is carried in the first non-zero component.
109 pub fn to_components(&self) -> (i64, i64, i64, i64) {
110 let mut remaining = self.nanos.abs();
111 let negative = self.nanos < 0;
112
113 let hours = remaining / (60 * 60 * 1_000_000_000);
114 remaining %= 60 * 60 * 1_000_000_000;
115
116 let minutes = remaining / (60 * 1_000_000_000);
117 remaining %= 60 * 1_000_000_000;
118
119 let seconds = remaining / 1_000_000_000;
120 let nanos = remaining % 1_000_000_000;
121
122 if negative {
123 if hours > 0 {
124 (-hours, minutes, seconds, nanos)
125 } else if minutes > 0 {
126 (0, -minutes, seconds, nanos)
127 } else if seconds > 0 {
128 (0, 0, -seconds, nanos)
129 } else {
130 (0, 0, 0, -nanos)
131 }
132 } else {
133 (hours, minutes, seconds, nanos)
134 }
135 }
136}
137
138impl Duration for ClockDuration {
139 fn to_nanos(&self) -> Outcome<i64> {
140 Ok(self.nanos)
141 }
142
143 fn to_seconds(&self) -> Outcome<i64> {
144 Ok(self.total_seconds())
145 }
146
147 fn to_days(&self) -> Outcome<i32> {
148 const NANOS_PER_DAY: i64 = 24 * 60 * 60 * 1_000_000_000;
149 let days = self.nanos / NANOS_PER_DAY;
150
151 if days > i32::MAX as i64 || days < i32::MIN as i64 {
152 return Err(err!("Duration too large to represent in days"; Overflow));
153 }
154
155 Ok(days as i32)
156 }
157
158 fn is_negative(&self) -> bool {
159 self.nanos < 0
160 }
161}
162
163impl fmt::Display for ClockDuration {
164 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165 let (hours, minutes, seconds, nanos) = self.to_components();
166
167 if hours != 0 {
168 if nanos == 0 {
169 write!(f, "{}:{:02}:{:02}", hours, minutes.abs(), seconds.abs())
170 } else {
171 write!(f, "{}:{:02}:{:02}.{:09}", hours, minutes.abs(), seconds.abs(), nanos.abs())
172 }
173 } else if minutes != 0 {
174 if nanos == 0 {
175 write!(f, "{}:{:02}", minutes, seconds.abs())
176 } else {
177 write!(f, "{}:{:02}.{:09}", minutes, seconds.abs(), nanos.abs())
178 }
179 } else if seconds != 0 {
180 if nanos == 0 {
181 write!(f, "{}s", seconds)
182 } else {
183 write!(f, "{}.{:09}s", seconds, nanos.abs())
184 }
185 } else {
186 write!(f, "{}ns", nanos)
187 }
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194
195 #[test]
196 fn test_duration_creation() {
197 let dur = ClockDuration::from_seconds(3661); // 1h 1m 1s
198 assert_eq!(dur.total_hours(), 1);
199 assert_eq!(dur.total_minutes(), 61);
200 assert_eq!(dur.total_seconds(), 3661);
201
202 let (h, m, s, n) = dur.to_components();
203 assert_eq!(h, 1);
204 assert_eq!(m, 1);
205 assert_eq!(s, 1);
206 assert_eq!(n, 0);
207 }
208
209 #[test]
210 fn test_duration_arithmetic() {
211 let dur1 = ClockDuration::from_minutes(30);
212 let dur2 = ClockDuration::from_minutes(45);
213
214 let sum = dur1.plus(&dur2);
215 assert_eq!(sum.total_minutes(), 75);
216
217 let diff = dur2.minus(&dur1);
218 assert_eq!(diff.total_minutes(), 15);
219 }
220
221 #[test]
222 fn test_negative_duration() {
223 let dur = ClockDuration::from_seconds(-3661);
224 assert!(dur.is_negative());
225
226 let (h, m, s, n) = dur.to_components();
227 assert_eq!(h, -1);
228 assert_eq!(m, 1); // magnitude
229 assert_eq!(s, 1); // magnitude
230 assert_eq!(n, 0);
231 }
232
233 #[test]
234 fn test_duration_division() {
235 let dur = ClockDuration::from_hours(6);
236 let half = dur.divide_by(2).unwrap();
237 assert_eq!(half.total_hours(), 3);
238
239 assert!(dur.divide_by(0).is_err());
240 }
241
242 #[test]
243 fn test_duration_display() {
244 let dur = ClockDuration::from_nanos(3661_123_456_789);
245 let display = fmt!("{}", dur);
246 assert!(display.contains("1:01:01"));
247 assert!(display.contains("123456789"));
248 }
249}