zino_core/datetime/
time.rs1use crate::{AvroValue, JsonValue, error::Error};
2use chrono::{Local, NaiveTime, Timelike, format::ParseError};
3use serde::{Deserialize, Serialize, Serializer};
4use std::{
5 fmt,
6 ops::{Add, AddAssign, Sub, SubAssign},
7 str::FromStr,
8 time::Duration,
9};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Deserialize)]
13pub struct Time(NaiveTime);
14
15impl Time {
16 #[inline]
18 pub fn try_new(hour: u32, minute: u32, second: u32) -> Result<Self, Error> {
19 NaiveTime::from_hms_opt(hour, minute, second)
20 .map(Self)
21 .ok_or_else(|| {
22 let message = format!(
23 "fail to create a time from hour: `{hour}`, minute: `{minute}`, second: `{second}`"
24 );
25 Error::new(message)
26 })
27 }
28
29 #[inline]
31 pub fn now() -> Self {
32 Self(Local::now().time())
33 }
34
35 #[inline]
37 pub fn midnight() -> Self {
38 Self(NaiveTime::default())
39 }
40
41 #[inline]
43 pub fn num_secs_from_midnight(&self) -> u32 {
44 self.0.num_seconds_from_midnight()
45 }
46
47 #[inline]
49 pub fn num_millis_from_midnight(&self) -> u32 {
50 self.0.num_seconds_from_midnight() * 1000 + self.0.nanosecond() / 1_000_000
51 }
52
53 #[inline]
55 pub fn num_micros_from_midnight(&self) -> u32 {
56 self.0.num_seconds_from_midnight() * 1_000_000 + self.0.nanosecond() / 1000
57 }
58
59 #[inline]
62 pub fn format(&self, fmt: &str) -> String {
63 format!("{}", self.0.format(fmt))
64 }
65
66 #[inline]
69 pub fn duration_since(&self, earlier: Time) -> Duration {
70 (self.0 - earlier.0).to_std().unwrap_or_default()
71 }
72
73 #[inline]
75 pub fn span_between(&self, other: Time) -> Duration {
76 let duration = if self > &other {
77 self.0 - other.0
78 } else {
79 other.0 - self.0
80 };
81 duration.to_std().unwrap_or_default()
82 }
83
84 #[inline]
86 pub fn span_between_now(&self) -> Duration {
87 self.span_between(Self::now())
88 }
89
90 #[inline]
92 pub fn span_before_now(&self) -> Option<Duration> {
93 let current = Self::now();
94 if self <= ¤t {
95 (current.0 - self.0).to_std().ok()
96 } else {
97 None
98 }
99 }
100
101 #[inline]
103 pub fn span_after_now(&self) -> Option<Duration> {
104 let current = Self::now();
105 if self >= ¤t {
106 (self.0 - current.0).to_std().ok()
107 } else {
108 None
109 }
110 }
111
112 #[inline]
114 pub fn hour(&self) -> u32 {
115 self.0.hour()
116 }
117
118 #[inline]
120 pub fn minute(&self) -> u32 {
121 self.0.minute()
122 }
123
124 #[inline]
126 pub fn second(&self) -> u32 {
127 self.0.second()
128 }
129}
130
131impl Default for Time {
132 #[inline]
134 fn default() -> Self {
135 Self::now()
136 }
137}
138
139impl fmt::Display for Time {
140 #[inline]
141 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
142 self.0.format("%H:%M:%S%.f").fmt(f)
143 }
144}
145
146impl Serialize for Time {
147 #[inline]
148 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
149 serializer.serialize_str(&self.to_string())
150 }
151}
152
153impl From<NaiveTime> for Time {
154 #[inline]
155 fn from(t: NaiveTime) -> Self {
156 Self(t)
157 }
158}
159
160impl From<Time> for NaiveTime {
161 #[inline]
162 fn from(t: Time) -> Self {
163 t.0
164 }
165}
166
167impl From<Time> for AvroValue {
168 #[inline]
169 fn from(t: Time) -> Self {
170 let micros = t.num_micros_from_midnight();
171 AvroValue::TimeMicros(micros.into())
172 }
173}
174
175impl From<Time> for JsonValue {
176 #[inline]
177 fn from(t: Time) -> Self {
178 JsonValue::String(t.to_string())
179 }
180}
181
182#[cfg(feature = "i18n")]
183impl<'a> From<Time> for fluent::FluentValue<'a> {
184 #[inline]
185 fn from(t: Time) -> Self {
186 fluent::FluentValue::String(t.to_string().into())
187 }
188}
189
190impl FromStr for Time {
191 type Err = ParseError;
192
193 #[inline]
194 fn from_str(s: &str) -> Result<Self, Self::Err> {
195 s.parse::<NaiveTime>().map(Self)
196 }
197}
198
199impl Add<Duration> for Time {
200 type Output = Self;
201
202 #[inline]
203 fn add(self, rhs: Duration) -> Self {
204 let duration = chrono::Duration::from_std(rhs).expect("Duration value is out of range");
205 Self(self.0 + duration)
206 }
207}
208
209impl AddAssign<Duration> for Time {
210 #[inline]
211 fn add_assign(&mut self, rhs: Duration) {
212 *self = *self + rhs;
213 }
214}
215
216impl Sub<Duration> for Time {
217 type Output = Self;
218
219 #[inline]
220 fn sub(self, rhs: Duration) -> Self {
221 let duration = chrono::Duration::from_std(rhs).expect("Duration value is out of range");
222 Self(self.0 - duration)
223 }
224}
225
226impl SubAssign<Duration> for Time {
227 #[inline]
228 fn sub_assign(&mut self, rhs: Duration) {
229 *self = *self - rhs;
230 }
231}
232
233#[cfg(feature = "sqlx")]
234impl<DB> sqlx::Type<DB> for Time
235where
236 DB: sqlx::Database,
237 NaiveTime: sqlx::Type<DB>,
238{
239 #[inline]
240 fn type_info() -> <DB as sqlx::Database>::TypeInfo {
241 <NaiveTime as sqlx::Type<DB>>::type_info()
242 }
243}
244
245#[cfg(feature = "sqlx")]
246impl<'r, DB> sqlx::Decode<'r, DB> for Time
247where
248 DB: sqlx::Database,
249 NaiveTime: sqlx::Decode<'r, DB>,
250{
251 #[inline]
252 fn decode(value: <DB as sqlx::Database>::ValueRef<'r>) -> Result<Self, crate::BoxError> {
253 <NaiveTime as sqlx::Decode<'r, DB>>::decode(value).map(|dt| dt.into())
254 }
255}