zino_core/datetime/
time.rs

1use 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/// A wrapper type for [`chrono::NaiveTime`].
12#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Deserialize)]
13pub struct Time(NaiveTime);
14
15impl Time {
16    /// Attempts to create a new instance.
17    #[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    /// Returns a new instance which corresponds to the current time.
30    #[inline]
31    pub fn now() -> Self {
32        Self(Local::now().time())
33    }
34
35    /// Returns a new instance which corresponds to the midnight.
36    #[inline]
37    pub fn midnight() -> Self {
38        Self(NaiveTime::default())
39    }
40
41    /// Returns the number of non-leap seconds past the last midnight.
42    #[inline]
43    pub fn num_secs_from_midnight(&self) -> u32 {
44        self.0.num_seconds_from_midnight()
45    }
46
47    /// Returns the number of non-leap milliseconds past the last midnight.
48    #[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    /// Returns the number of non-leap microseconds past the last midnight.
54    #[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    /// Formats the time with the specified format string.
60    /// See [`format::strftime`](chrono::format::strftime) for the supported escape sequences.
61    #[inline]
62    pub fn format(&self, fmt: &str) -> String {
63        format!("{}", self.0.format(fmt))
64    }
65
66    /// Returns the amount of time elapsed from another time to this one,
67    /// or zero duration if that time is later than this one.
68    #[inline]
69    pub fn duration_since(&self, earlier: Time) -> Duration {
70        (self.0 - earlier.0).to_std().unwrap_or_default()
71    }
72
73    /// Returns the duration of time between `self` and `other`.
74    #[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    /// Returns the duration of time between `self` and `Time::now()`.
85    #[inline]
86    pub fn span_between_now(&self) -> Duration {
87        self.span_between(Self::now())
88    }
89
90    /// Returns the duration of time from `self` to `Time::now()`.
91    #[inline]
92    pub fn span_before_now(&self) -> Option<Duration> {
93        let current = Self::now();
94        if self <= &current {
95            (current.0 - self.0).to_std().ok()
96        } else {
97            None
98        }
99    }
100
101    /// Returns the duration of time from `Time::now()` to `self`.
102    #[inline]
103    pub fn span_after_now(&self) -> Option<Duration> {
104        let current = Self::now();
105        if self >= &current {
106            (self.0 - current.0).to_std().ok()
107        } else {
108            None
109        }
110    }
111
112    /// Returns the hour number from 0 to 23.
113    #[inline]
114    pub fn hour(&self) -> u32 {
115        self.0.hour()
116    }
117
118    /// Returns the minute number from 0 to 59.
119    #[inline]
120    pub fn minute(&self) -> u32 {
121        self.0.minute()
122    }
123
124    /// Returns the second number from 0 to 59.
125    #[inline]
126    pub fn second(&self) -> u32 {
127        self.0.second()
128    }
129}
130
131impl Default for Time {
132    /// Returns an instance which corresponds to **the current time**.
133    #[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}