1use std::ops::Sub;
2use thiserror::Error;
3use time::{Duration, Time, ext::NumericalDuration};
4
5#[derive(Error, Debug)]
6pub enum TimeError {
7 #[error("Invalid time format. Expected HH:MM or H:MM, got: {0}")]
8 InvalidFormat(String),
9 #[error("Invalid time components: {0}:{1}")]
10 InvalidComponents(u8, u8),
11 #[error("Failed to reset seconds for time: {0:?}")]
12 ResetSecondsError(Time),
13 #[error("Invalid seconds value: {0}")]
14 InvalidSeconds(i64),
15 #[error("Invalid alignment unit: {0}")]
16 InvalidAlignmentUnit(u64),
17 #[error("Failed to add time: {0:?}")]
18 AddTimeError(Time),
19}
20
21pub trait ExtTime {
23 fn to_shorten(&self) -> String;
34
35 fn from_str(time_str: &str) -> Result<Time, TimeError>;
44
45 fn sub_ext(&self, right: Time) -> Duration;
53
54 fn reset_minute(&self) -> Result<Time, TimeError>;
56
57 fn is_same_minute(&self, other: &Time) -> bool;
59
60 fn is_between(&self, start: Time, end: Time) -> bool;
63
64 fn add_minutes(&self, minutes: i64) -> Time;
66
67 fn from_seconds(seconds: i64) -> Result<Time, TimeError>;
76
77 fn to_seconds(&self) -> i64;
82
83 fn align_to(&self, interval: i64) -> Result<Time, TimeError>;
92
93 fn next_day(&self) -> Time;
95
96 fn next_hour(&self) -> Time;
98
99 fn next_minute(&self) -> Time;
101
102 fn next_second(&self) -> Time;
104
105 fn to_hour_seconds(&self) -> i64;
112
113 fn to_minute_seconds(&self) -> i64;
120}
121
122impl ExtTime for Time {
123 fn to_shorten(&self) -> String {
124 format!("{}:{:02}", self.hour(), self.minute())
125 }
126
127 fn from_str(time_str: &str) -> Result<Time, TimeError> {
128 let parts: Vec<&str> = time_str.split(':').collect();
129 if parts.len() == 2 {
130 if let (Ok(hour), Ok(minute)) = (parts[0].parse::<u8>(), parts[1].parse::<u8>()) {
131 if hour < 24 && minute < 60 {
132 return Time::from_hms(hour, minute, 0)
133 .map_err(|_| TimeError::InvalidComponents(hour, minute));
134 }
135 }
136 }
137
138 Err(TimeError::InvalidFormat(time_str.to_string()))
139 }
140
141 fn sub_ext(&self, right: Time) -> Duration {
142 let diff = self.clone().sub(right);
143 if diff.is_negative() {
144 24.hours() + diff
145 } else {
146 diff
147 }
148 }
149
150 fn reset_minute(&self) -> Result<Time, TimeError> {
151 Time::from_hms(self.hour(), self.minute(), 0)
152 .map_err(|_| TimeError::ResetSecondsError(*self))
153 }
154
155 fn is_same_minute(&self, other: &Time) -> bool {
156 self.minute() == other.minute() && self.hour() == other.hour()
157 }
158
159 fn is_between(&self, start: Time, end: Time) -> bool {
160 if start <= end {
161 *self >= start && *self <= end
162 } else {
163 *self >= start || *self <= end
165 }
166 }
167
168 fn add_minutes(&self, minutes: i64) -> Time {
169 let total_minutes = self.hour() as i64 * 60 + self.minute() as i64 + minutes;
170 let normalized_minutes = total_minutes.rem_euclid(24 * 60);
171 let hours = (normalized_minutes / 60) as u8;
172 let minutes = (normalized_minutes % 60) as u8;
173 Time::from_hms(hours, minutes, self.second()).unwrap()
174 }
175
176 fn from_seconds(seconds: i64) -> Result<Time, TimeError> {
177 if seconds < 0 || seconds >= 24 * 3600 {
178 return Err(TimeError::InvalidSeconds(seconds));
179 }
180
181 let hours = (seconds / 3600) as u8;
182 let minutes = ((seconds % 3600) / 60) as u8;
183 let secs = (seconds % 60) as u8;
184
185 Time::from_hms(hours, minutes, secs)
186 .map_err(|_| TimeError::InvalidComponents(hours, minutes))
187 }
188
189 fn to_seconds(&self) -> i64 {
190 self.hour() as i64 * 3600 + self.minute() as i64 * 60 + self.second() as i64
191 }
192
193 fn align_to(&self, interval: i64) -> Result<Time, TimeError> {
194 if interval == 0 {
195 return Err(TimeError::InvalidAlignmentUnit(interval.abs() as u64));
196 }
197
198 let total_seconds = self.to_seconds();
199 let aligned_seconds = (total_seconds / interval) * interval;
200
201 Time::from_seconds(aligned_seconds).map_err(|_| TimeError::InvalidSeconds(aligned_seconds))
202 }
203
204 fn next_day(&self) -> Time {
205 *self
207 }
208
209 fn next_hour(&self) -> Time {
210 let next_hour = (self.hour() + 1) % 24;
211 Time::from_hms(next_hour, self.minute(), self.second()).unwrap()
212 }
213
214 fn next_minute(&self) -> Time {
215 if self.minute() == 59 {
216 let next_hour = (self.hour() + 1) % 24;
217 Time::from_hms(next_hour, 0, self.second()).unwrap()
218 } else {
219 Time::from_hms(self.hour(), self.minute() + 1, self.second()).unwrap()
220 }
221 }
222
223 fn next_second(&self) -> Time {
224 if self.second() == 59 {
225 if self.minute() == 59 {
226 let next_hour = (self.hour() + 1) % 24;
227 Time::from_hms(next_hour, 0, 0).unwrap()
228 } else {
229 Time::from_hms(self.hour(), self.minute() + 1, 0).unwrap()
230 }
231 } else {
232 Time::from_hms(self.hour(), self.minute(), self.second() + 1).unwrap()
233 }
234 }
235
236 fn to_hour_seconds(&self) -> i64 {
237 self.hour() as i64 * 3600
238 }
239
240 fn to_minute_seconds(&self) -> i64 {
241 self.hour() as i64 * 3600 + self.minute() as i64 * 60
242 }
243}
244