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