Oregami
Repositories/oxedyne/fe2o3

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

3.2 KiB, 26 runs

created by r1870400018:6325, 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 oxedyne_fe2o3_core::prelude::*;
5
6use std::fmt;
7
8/// 0 to 60. The last minute of the hour is 59; 60 is the end of the hour, the
9/// same instant as minute 0 of the next one.
10///
11/// ```ignore
12/// use oxedyne_fe2o3_datime::clock::ClockMinuteres!();
13///
14/// let minute = ClockMinute::new(45)?res!();
15/// let (new_minute, hour_carry) = minute.add_minutes(20)res!();
16/// assert_eq!(new_minute.of(), 5)res!(); // 45 + 20 = 65 -> 5 with carry
17/// assert_eq!(hour_carry, 1)res!();
18/// ```
19#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
20pub struct ClockMinute {
21 value: u8,
22}
23
24impl ClockMinute {
25 pub const MAX_VALUE: u8 = 60; // 60 is the end of the hour, not a minute of it
26 pub const SECONDS_PER_MINUTE: u32 = 60;
27 pub const MILLIS_PER_MINUTE: u32 = 60_000;
28 pub const MICROS_PER_MINUTE: u32 = 60_000_000;
29 pub const NANOS_PER_MINUTE: u64 = 60_000_000_000;
30
31 pub fn new(minute: u8) -> Outcome<Self> {
32 if minute > Self::MAX_VALUE {
33 return Err(err!(
34 "Minute {} is invalid, must be 0-{}",
35 minute,
36 Self::MAX_VALUE;
37 Invalid, Input
38 ));
39 }
40 Ok(Self { value: minute })
41 }
42
43 pub(crate) fn new_unchecked(minute: u8) -> Self {
44 Self { value: minute }
45 }
46
47 pub fn of(&self) -> u8 {
48 self.value
49 }
50
51 /// 60 is a valid ClockMinute but not a minute within an hour.
52 pub fn is_valid_hour_minute(&self) -> bool {
53 self.value < 60
54 }
55
56 pub fn is_end_of_hour(&self) -> bool {
57 self.value == 60
58 }
59
60 pub fn add_minutes(&self, minutes: u32) -> (Self, u32) {
61 let total = self.value as u32 + minutes;
62 let hour_carry = total / 60;
63 let new_minute = total % 60;
64 (Self::new_unchecked(new_minute as u8), hour_carry)
65 }
66
67 pub fn sub_minutes(&self, minutes: u32) -> (Self, u32) {
68 let minutes = minutes % (60 * 24); // reasonable limit
69 if minutes as u8 > self.value {
70 let borrow_needed = ((minutes as u8 - self.value + 59) / 60) as u32;
71 let effective_minutes = borrow_needed * 60 + self.value as u32 - minutes;
72 (Self::new_unchecked(effective_minutes as u8), borrow_needed)
73 } else {
74 (Self::new_unchecked(self.value - minutes as u8), 0)
75 }
76 }
77}
78
79// Validation methods.
80impl ClockMinute {
81 pub fn is_valid(&self) -> bool {
82 self.value <= Self::MAX_VALUE
83 }
84}
85
86impl fmt::Display for ClockMinute {
87 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88 write!(f, "{:02}", self.value)
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 #[test]
97 fn test_minute_creation() {
98 assert!(ClockMinute::new(0).is_ok());
99 assert!(ClockMinute::new(30).is_ok());
100 assert!(ClockMinute::new(59).is_ok());
101 assert!(ClockMinute::new(60).is_ok());
102 assert!(ClockMinute::new(61).is_err());
103 }
104
105 #[test]
106 fn test_minute_arithmetic() {
107 let minute = ClockMinute::new(30).unwrap();
108 let (new_min, carry) = minute.add_minutes(20);
109 assert_eq!(new_min.of(), 50);
110 assert_eq!(carry, 0);
111
112 let (new_min, carry) = minute.add_minutes(45);
113 assert_eq!(new_min.of(), 15);
114 assert_eq!(carry, 1);
115
116 let (new_min, borrow) = minute.sub_minutes(10);
117 assert_eq!(new_min.of(), 20);
118 assert_eq!(borrow, 0);
119
120 let (new_min, borrow) = minute.sub_minutes(45);
121 assert_eq!(new_min.of(), 45);
122 assert_eq!(borrow, 1);
123 }
124}