Oregami
Repositories/oxedyne/fe2o3

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

3.9 KiB, 32 runs

created by r1870400018:6317, 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 24 on the 24-hour clock. The last hour of the day is 23; 24 is the end
9/// of the day, the same instant as midnight starting the next one.
10///
11/// ```ignore
12/// use oxedyne_fe2o3_datime::clock::ClockHourres!();
13///
14/// let hour = ClockHour::new(14)?res!();
15/// let (twelve_hour, is_pm) = hour.to_twelve_hour()res!();
16/// assert_eq!(twelve_hour, 2)res!();
17/// assert_eq!(is_pm, true)res!();
18/// ```
19#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
20pub struct ClockHour {
21 value: u8,
22}
23
24impl ClockHour {
25 pub const MAX_VALUE: u8 = 24; // 24 is the end of day, not an hour of it
26 pub const MINUTES_PER_HOUR: u32 = 60;
27 pub const SECONDS_PER_HOUR: u32 = 3_600;
28 pub const MILLIS_PER_HOUR: u32 = 3_600_000;
29 pub const MICROS_PER_HOUR: u64 = 3_600_000_000;
30 pub const NANOS_PER_HOUR: u64 = 3_600_000_000_000;
31
32 pub fn new(hour: u8) -> Outcome<Self> {
33 if hour > Self::MAX_VALUE {
34 return Err(err!(
35 "Hour {} is invalid, must be 0-{}",
36 hour,
37 Self::MAX_VALUE;
38 Invalid, Input
39 ));
40 }
41 Ok(Self { value: hour })
42 }
43
44 pub(crate) fn new_unchecked(hour: u8) -> Self {
45 Self { value: hour }
46 }
47
48 pub fn of(&self) -> u8 {
49 self.value
50 }
51
52 /// 24 is a valid ClockHour but not an hour within a day.
53 pub fn is_valid_day_hour(&self) -> bool {
54 self.value < 24
55 }
56
57 pub fn is_end_of_day(&self) -> bool {
58 self.value == 24
59 }
60
61 /// Hour 0 becomes 12 AM, hour 12 becomes 12 PM, and hour 24 becomes 12 AM.
62 pub fn to_twelve_hour(&self) -> (u8, bool) {
63 match self.value {
64 0 => (12, false), // midnight
65 1..=11 => (self.value, false), // AM
66 12 => (12, true), // noon
67 13..=23 => (self.value - 12, true), // PM
68 24 => (12, false), // end of day = midnight
69 _ => unreachable!(),
70 }
71 }
72
73 /// Wraps within 0-23.
74 pub fn add_hours(&self, hours: u32) -> Self {
75 let new_hour = (self.value as u32 + hours) % 24;
76 Self::new_unchecked(new_hour as u8)
77 }
78
79 pub fn sub_hours(&self, hours: u32) -> Self {
80 let hours = hours % 24;
81 let new_hour = if hours as u8 > self.value {
82 24 - (hours as u8 - self.value)
83 } else {
84 self.value - hours as u8
85 };
86 Self::new_unchecked(new_hour)
87 }
88
89 pub fn from_12_hour(hour: u8, is_pm: bool) -> Outcome<Self> {
90 if hour == 0 || hour > 12 {
91 return Err(err!(
92 "12-hour format hour {} is invalid, must be 1-12",
93 hour;
94 Invalid, Input
95 ));
96 }
97
98 let hour_24 = match (hour, is_pm) {
99 (12, false) => 0, // 12 AM = midnight
100 (12, true) => 12, // 12 PM = noon
101 (h, false) => h, // 1-11 AM
102 (h, true) => h + 12, // 1-11 PM
103 };
104
105 Ok(Self::new_unchecked(hour_24))
106 }
107}
108
109// Validation methods.
110impl ClockHour {
111 pub fn is_valid(&self) -> bool {
112 self.value <= Self::MAX_VALUE
113 }
114}
115
116impl fmt::Display for ClockHour {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 write!(f, "{:02}", self.value)
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
127 fn test_hour_creation() {
128 assert!(ClockHour::new(0).is_ok());
129 assert!(ClockHour::new(12).is_ok());
130 assert!(ClockHour::new(23).is_ok());
131 assert!(ClockHour::new(24).is_ok());
132 assert!(ClockHour::new(25).is_err());
133 }
134
135 #[test]
136 fn test_twelve_hour_conversion() {
137 assert_eq!(ClockHour::new(0).unwrap().to_twelve_hour(), (12, false));
138 assert_eq!(ClockHour::new(1).unwrap().to_twelve_hour(), (1, false));
139 assert_eq!(ClockHour::new(12).unwrap().to_twelve_hour(), (12, true));
140 assert_eq!(ClockHour::new(13).unwrap().to_twelve_hour(), (1, true));
141 assert_eq!(ClockHour::new(23).unwrap().to_twelve_hour(), (11, true));
142 }
143
144 #[test]
145 fn test_hour_arithmetic() {
146 let hour = ClockHour::new(10).unwrap();
147 assert_eq!(hour.add_hours(5).of(), 15);
148 assert_eq!(hour.add_hours(20).of(), 6); // wraps around
149 assert_eq!(hour.sub_hours(5).of(), 5);
150 assert_eq!(hour.sub_hours(15).of(), 19); // wraps around
151 }
152}