sim_restaurant/
time_of_day.rs1use std::fmt;
4use std::str::FromStr;
5
6#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
7pub struct TimeOfDay {
8 seconds_since_midnight: u64,
9}
10
11impl TimeOfDay {
12 #[must_use]
13 pub const fn from_hm(hour: u64, minute: u64) -> Self {
14 Self {
15 seconds_since_midnight: hour * 3600 + minute * 60,
16 }
17 }
18
19 #[must_use]
20 pub const fn seconds_since_midnight(self) -> u64 {
21 self.seconds_since_midnight
22 }
23
24 #[must_use]
25 pub fn add_ticks(self, ticks: u64) -> Self {
26 let seconds = (self.seconds_since_midnight + ticks) % (24 * 3600);
27 Self {
28 seconds_since_midnight: seconds,
29 }
30 }
31
32 #[must_use]
33 pub fn round_to_nearest_quarter_hour(self) -> Self {
34 let quarter_hour = 15 * 60;
35 let rounded =
36 ((self.seconds_since_midnight + quarter_hour / 2) / quarter_hour) * quarter_hour;
37 Self {
38 seconds_since_midnight: rounded % (24 * 3600),
39 }
40 }
41}
42
43impl fmt::Display for TimeOfDay {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 let total_minutes = self.seconds_since_midnight / 60;
46 let hour = total_minutes / 60;
47 let minute = total_minutes % 60;
48 write!(f, "{hour:02}:{minute:02}")
49 }
50}
51
52impl FromStr for TimeOfDay {
53 type Err = String;
54
55 fn from_str(value: &str) -> Result<Self, Self::Err> {
56 let mut parts = value.split(':');
57 let hour = parts
58 .next()
59 .ok_or_else(|| "missing hour".to_string())?
60 .parse::<u64>()
61 .map_err(|_| format!("invalid hour in '{value}'"))?;
62 let minute = parts
63 .next()
64 .ok_or_else(|| "missing minutes".to_string())?
65 .parse::<u64>()
66 .map_err(|_| format!("invalid minutes in '{value}'"))?;
67
68 if parts.next().is_some() {
69 return Err(format!(
70 "invalid time '{value}', expected HH:MM in 24-hour format"
71 ));
72 }
73 if hour >= 24 || minute >= 60 {
74 return Err(format!(
75 "invalid time '{value}', expected HH:MM in 24-hour format"
76 ));
77 }
78
79 Ok(Self::from_hm(hour, minute))
80 }
81}