Skip to main content

dos_date_time/dos_time/
convert.rs

1// SPDX-FileCopyrightText: 2025 Shun Sakai
2//
3// SPDX-License-Identifier: Apache-2.0 OR MIT
4
5//! Implementations of conversions between [`Time`] and other types.
6
7#[cfg(feature = "chrono")]
8use chrono::{NaiveTime, Timelike};
9#[cfg(feature = "jiff")]
10use jiff::civil;
11
12use super::Time;
13
14impl From<Time> for u16 {
15    fn from(time: Time) -> Self {
16        time.to_raw()
17    }
18}
19
20impl From<Time> for time::Time {
21    /// Converts a `Time` to a [`time::Time`].
22    ///
23    /// # Examples
24    ///
25    /// ```
26    /// # use dos_date_time::{
27    /// #     Time,
28    /// #     time::{self, macros::time},
29    /// # };
30    /// #
31    /// assert_eq!(time::Time::from(Time::MIN), time::Time::MIDNIGHT);
32    /// assert_eq!(time::Time::from(Time::MAX), time!(23:59:58));
33    /// ```
34    fn from(time: Time) -> Self {
35        let (hour, minute, second) = (time.hour(), time.minute(), time.second());
36        Self::from_hms(hour, minute, second).expect("time should be in the range of `time::Time`")
37    }
38}
39
40#[cfg(feature = "chrono")]
41impl From<Time> for NaiveTime {
42    /// Converts a `Time` to a [`NaiveTime`].
43    ///
44    /// # Examples
45    ///
46    /// ```
47    /// # use dos_date_time::{Time, chrono::NaiveTime};
48    /// #
49    /// assert_eq!(NaiveTime::from(Time::MIN), NaiveTime::MIN);
50    /// assert_eq!(
51    ///     NaiveTime::from(Time::MAX),
52    ///     "23:59:58".parse::<NaiveTime>().unwrap()
53    /// );
54    /// ```
55    fn from(time: Time) -> Self {
56        let (hour, minute, second) = (
57            time.hour().into(),
58            time.minute().into(),
59            time.second().into(),
60        );
61        Self::from_hms_opt(hour, minute, second)
62            .expect("time should be in the range of `NaiveTime`")
63    }
64}
65
66#[cfg(feature = "jiff")]
67impl From<Time> for civil::Time {
68    /// Converts a `Time` to a [`civil::Time`].
69    ///
70    /// # Examples
71    ///
72    /// ```
73    /// # use dos_date_time::{Time, jiff::civil};
74    /// #
75    /// assert_eq!(civil::Time::from(Time::MIN), civil::Time::MIN);
76    /// assert_eq!(civil::Time::from(Time::MAX), civil::time(23, 59, 58, 0));
77    /// ```
78    fn from(time: Time) -> Self {
79        let (hour, minute, second) = (
80            time.hour()
81                .try_into()
82                .expect("hour should be in the range of `i8`"),
83            time.minute()
84                .try_into()
85                .expect("minute should be in the range of `i8`"),
86            time.second()
87                .try_into()
88                .expect("second should be in the range of `i8`"),
89        );
90        civil::time(hour, minute, second, i32::default())
91    }
92}
93
94impl From<time::Time> for Time {
95    /// Converts a [`time::Time`] to a `Time`.
96    ///
97    /// <div class="warning">
98    ///
99    /// This method may round towards zero, truncating more precise times that a
100    /// `Time` cannot store.
101    ///
102    /// </div>
103    ///
104    /// # Examples
105    ///
106    /// ```
107    /// # use dos_date_time::{
108    /// #     Time,
109    /// #     time::{self, macros::time},
110    /// # };
111    /// #
112    /// assert_eq!(Time::from(time::Time::MIDNIGHT), Time::MIN);
113    /// assert_eq!(Time::from(time!(23:59:58)), Time::MAX);
114    /// ```
115    fn from(time: time::Time) -> Self {
116        Self::from_time(time)
117    }
118}
119
120#[cfg(feature = "chrono")]
121impl From<NaiveTime> for Time {
122    /// Converts a [`NaiveTime`] to a `Time`.
123    ///
124    /// <div class="warning">
125    ///
126    /// This method may round towards zero, truncating more precise times that a
127    /// `Time` cannot store.
128    ///
129    /// </div>
130    ///
131    /// # Examples
132    ///
133    /// ```
134    /// # use dos_date_time::{Time, chrono::NaiveTime};
135    /// #
136    /// assert_eq!(Time::from(NaiveTime::MIN), Time::MIN);
137    /// assert_eq!(
138    ///     Time::from("23:59:58".parse::<NaiveTime>().unwrap()),
139    ///     Time::MAX
140    /// );
141    /// ```
142    fn from(time: NaiveTime) -> Self {
143        let (hour, minute, second) = (
144            time.hour()
145                .try_into()
146                .expect("hour should be in the range of `u8`"),
147            time.minute()
148                .try_into()
149                .expect("minute should be in the range of `u8`"),
150            time.second()
151                .try_into()
152                .expect("second should be in the range of `u8`"),
153        );
154        let time = time::Time::from_hms(hour, minute, second)
155            .expect("time should be in the range of `time::Time`");
156        Self::from_time(time)
157    }
158}
159
160#[cfg(feature = "jiff")]
161impl From<civil::Time> for Time {
162    /// Converts a [`civil::Time`] to a `Time`.
163    ///
164    /// <div class="warning">
165    ///
166    /// This method may round towards zero, truncating more precise times that a
167    /// `Time` cannot store.
168    ///
169    /// </div>
170    ///
171    /// # Examples
172    ///
173    /// ```
174    /// # use dos_date_time::{Time, jiff::civil};
175    /// #
176    /// assert_eq!(Time::from(civil::Time::MIN), Time::MIN);
177    /// assert_eq!(Time::from(civil::time(23, 59, 58, 0)), Time::MAX);
178    /// ```
179    fn from(time: civil::Time) -> Self {
180        let (hour, minute, second) = (
181            time.hour()
182                .try_into()
183                .expect("hour should be in the range of `u8`"),
184            time.minute()
185                .try_into()
186                .expect("minute should be in the range of `u8`"),
187            time.second()
188                .try_into()
189                .expect("second should be in the range of `u8`"),
190        );
191        let time = time::Time::from_hms(hour, minute, second)
192            .expect("time should be in the range of `time::Time`");
193        Self::from_time(time)
194    }
195}
196
197#[cfg(test)]
198mod tests {
199    use time::macros::time;
200
201    use super::*;
202
203    #[test]
204    fn from_time_to_u16() {
205        assert_eq!(u16::from(Time::MIN), u16::MIN);
206        // <https://devblogs.microsoft.com/oldnewthing/20030905-02/?p=42653>.
207        assert_eq!(
208            u16::from(Time::new(0b1001_1011_0010_0000).unwrap()),
209            0b1001_1011_0010_0000
210        );
211        // <https://github.com/zip-rs/zip/blob/v0.6.4/src/types.rs#L553-L569>.
212        assert_eq!(
213            u16::from(Time::new(0b0101_0100_1100_1111).unwrap()),
214            0b0101_0100_1100_1111
215        );
216        assert_eq!(u16::from(Time::MAX), 0b1011_1111_0111_1101);
217    }
218
219    #[test]
220    fn from_time_to_time_time() {
221        assert_eq!(time::Time::from(Time::MIN), time::Time::MIDNIGHT);
222        // <https://devblogs.microsoft.com/oldnewthing/20030905-02/?p=42653>.
223        assert_eq!(
224            time::Time::from(Time::new(0b1001_1011_0010_0000).unwrap()),
225            time!(19:25:00)
226        );
227        // <https://github.com/zip-rs/zip/blob/v0.6.4/src/types.rs#L553-L569>.
228        assert_eq!(
229            time::Time::from(Time::new(0b0101_0100_1100_1111).unwrap()),
230            time!(10:38:30)
231        );
232        assert_eq!(time::Time::from(Time::MAX), time!(23:59:58));
233    }
234
235    #[cfg(feature = "chrono")]
236    #[test]
237    fn from_time_to_chrono_naive_time() {
238        assert_eq!(NaiveTime::from(Time::MIN), NaiveTime::MIN);
239        // <https://devblogs.microsoft.com/oldnewthing/20030905-02/?p=42653>.
240        assert_eq!(
241            NaiveTime::from(Time::new(0b1001_1011_0010_0000).unwrap()),
242            "19:25:00".parse::<NaiveTime>().unwrap()
243        );
244        // <https://github.com/zip-rs/zip/blob/v0.6.4/src/types.rs#L553-L569>.
245        assert_eq!(
246            NaiveTime::from(Time::new(0b0101_0100_1100_1111).unwrap()),
247            "10:38:30".parse::<NaiveTime>().unwrap()
248        );
249        assert_eq!(
250            NaiveTime::from(Time::MAX),
251            "23:59:58".parse::<NaiveTime>().unwrap()
252        );
253    }
254
255    #[cfg(feature = "jiff")]
256    #[test]
257    fn from_time_to_jiff_civil_time() {
258        assert_eq!(civil::Time::from(Time::MIN), civil::Time::MIN);
259        // <https://devblogs.microsoft.com/oldnewthing/20030905-02/?p=42653>.
260        assert_eq!(
261            civil::Time::from(Time::new(0b1001_1011_0010_0000).unwrap()),
262            civil::time(19, 25, 0, 0)
263        );
264        // <https://github.com/zip-rs/zip/blob/v0.6.4/src/types.rs#L553-L569>.
265        assert_eq!(
266            civil::Time::from(Time::new(0b0101_0100_1100_1111).unwrap()),
267            civil::time(10, 38, 30, 0)
268        );
269        assert_eq!(civil::Time::from(Time::MAX), civil::time(23, 59, 58, 0));
270    }
271
272    #[test]
273    fn from_time_time_to_time() {
274        assert_eq!(Time::from(time::Time::MIDNIGHT), Time::MIN);
275        assert_eq!(Time::from(time!(00:00:01)), Time::MIN);
276        // <https://devblogs.microsoft.com/oldnewthing/20030905-02/?p=42653>.
277        assert_eq!(
278            Time::from(time!(19:25:00)),
279            Time::new(0b1001_1011_0010_0000).unwrap()
280        );
281        // <https://github.com/zip-rs/zip/blob/v0.6.4/src/types.rs#L553-L569>.
282        assert_eq!(
283            Time::from(time!(10:38:30)),
284            Time::new(0b0101_0100_1100_1111).unwrap()
285        );
286        assert_eq!(Time::from(time!(23:59:58)), Time::MAX);
287        assert_eq!(Time::from(time!(23:59:59)), Time::MAX);
288    }
289
290    #[cfg(feature = "chrono")]
291    #[test]
292    fn from_chrono_naive_time_to_time() {
293        assert_eq!(Time::from(NaiveTime::MIN), Time::MIN);
294        assert_eq!(
295            Time::from("00:00:01".parse::<NaiveTime>().unwrap()),
296            Time::MIN
297        );
298        // <https://devblogs.microsoft.com/oldnewthing/20030905-02/?p=42653>.
299        assert_eq!(
300            Time::from("19:25:00".parse::<NaiveTime>().unwrap()),
301            Time::new(0b1001_1011_0010_0000).unwrap()
302        );
303        // <https://github.com/zip-rs/zip/blob/v0.6.4/src/types.rs#L553-L569>.
304        assert_eq!(
305            Time::from("10:38:30".parse::<NaiveTime>().unwrap()),
306            Time::new(0b0101_0100_1100_1111).unwrap()
307        );
308        assert_eq!(
309            Time::from("23:59:58".parse::<NaiveTime>().unwrap()),
310            Time::MAX
311        );
312        assert_eq!(
313            Time::from("23:59:59".parse::<NaiveTime>().unwrap()),
314            Time::MAX
315        );
316    }
317
318    #[cfg(feature = "jiff")]
319    #[test]
320    fn from_jiff_civil_time_to_time() {
321        assert_eq!(Time::from(civil::Time::MIN), Time::MIN);
322        assert_eq!(Time::from(civil::time(0, 0, 1, 0)), Time::MIN);
323        // <https://devblogs.microsoft.com/oldnewthing/20030905-02/?p=42653>.
324        assert_eq!(
325            Time::from(civil::time(19, 25, 0, 0)),
326            Time::new(0b1001_1011_0010_0000).unwrap()
327        );
328        // <https://github.com/zip-rs/zip/blob/v0.6.4/src/types.rs#L553-L569>.
329        assert_eq!(
330            Time::from(civil::time(10, 38, 30, 0)),
331            Time::new(0b0101_0100_1100_1111).unwrap()
332        );
333        assert_eq!(Time::from(civil::time(23, 59, 58, 0)), Time::MAX);
334        assert_eq!(Time::from(civil::time(23, 59, 59, 0)), Time::MAX);
335    }
336}