sim_restaurant/
time_of_day.rs

1// Copyright (c) 2026 Graphcore Ltd. All rights reserved.
2
3use 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}