cxx_qt_lib/core/
qtime.rs

1// SPDX-FileCopyrightText: 2022 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
2// SPDX-FileContributor: Andrew Hayzen <andrew.hayzen@kdab.com>
3//
4// SPDX-License-Identifier: MIT OR Apache-2.0
5
6use cxx::{type_id, ExternType};
7use std::fmt;
8
9#[cxx::bridge]
10mod ffi {
11    #[namespace = "Qt"]
12    unsafe extern "C++" {
13        include!("cxx-qt-lib/qt.h");
14        type DateFormat = crate::DateFormat;
15    }
16
17    unsafe extern "C++" {
18        include!("cxx-qt-lib/qtime.h");
19        include!("cxx-qt-lib/qstring.h");
20
21        type QTime = super::QTime;
22        type QString = crate::QString;
23
24        /// Returns a QTime object containing a time ms milliseconds later
25        /// than the time of this object (or earlier if ms is negative).
26        #[rust_name = "add_msecs"]
27        fn addMSecs(self: &QTime, ms: i32) -> QTime;
28
29        /// Returns a QTime object containing a time s seconds later than the
30        /// time of this object (or earlier if s is negative).
31        #[rust_name = "add_secs"]
32        fn addSecs(self: &QTime, s: i32) -> QTime;
33
34        /// Returns the hour part (0 to 23) of the time.
35        fn hour(self: &QTime) -> i32;
36
37        /// Returns true if the time is null (i.e., the QTime object was
38        /// constructed using the default constructor); otherwise returns false.
39        /// A null time is also an invalid time.
40        #[rust_name = "is_null"]
41        fn isNull(self: &QTime) -> bool;
42
43        /// Returns true if the time is valid; otherwise returns false.
44        #[rust_name = "is_valid"]
45        fn isValid(self: &QTime) -> bool;
46
47        /// Returns the minute part (0 to 59) of the time.
48        fn minute(self: &QTime) -> i32;
49
50        /// Returns the millisecond part (0 to 999) of the time.
51        fn msec(self: &QTime) -> i32;
52
53        /// Returns the number of msecs since the start of the day, i.e. since 00:00:00.
54        #[rust_name = "msecs_since_start_of_day"]
55        fn msecsSinceStartOfDay(self: &QTime) -> i32;
56
57        /// Returns the second part (0 to 59) of the time.
58        fn second(self: &QTime) -> i32;
59
60        /// Sets the time to hour h, minute m, seconds s and milliseconds ms.
61        #[rust_name = "set_hms"]
62        fn setHMS(self: &mut QTime, h: i32, m: i32, s: i32, ms: i32) -> bool;
63
64        /// Returns the time as a string. The format parameter determines the format of the result string.
65        #[rust_name = "format"]
66        fn toString(self: &QTime, format: &QString) -> QString;
67
68        /// Returns the time as a string. The format parameter determines the format of the string.
69        #[rust_name = "format_enum"]
70        fn toString(self: &QTime, format: DateFormat) -> QString;
71    }
72
73    #[namespace = "rust::cxxqtlib1"]
74    unsafe extern "C++" {
75        #[doc(hidden)]
76        #[rust_name = "qtime_current_time"]
77        fn qtimeCurrentTime() -> QTime;
78
79        #[doc(hidden)]
80        #[rust_name = "qtime_from_msecs_since_start_of_day"]
81        fn qtimeFromMSecsSinceStartOfDay(msecs: i32) -> QTime;
82
83        #[doc(hidden)]
84        #[rust_name = "qtime_from_string"]
85        fn qtimeFromString(string: &QString, format: &QString) -> QTime;
86        #[doc(hidden)]
87        #[rust_name = "qtime_from_string_enum"]
88        fn qtimeFromString(string: &QString, format: DateFormat) -> QTime;
89
90        #[doc(hidden)]
91        #[rust_name = "qtime_msecs_to"]
92        fn qtimeMSecsTo(time: &QTime, t: QTime) -> i32;
93
94        #[doc(hidden)]
95        #[rust_name = "qtime_secs_to"]
96        fn qtimeSecsTo(time: &QTime, t: QTime) -> i32;
97
98        #[doc(hidden)]
99        #[rust_name = "qtime_is_valid"]
100        fn qtimeIsValid(h: i32, m: i32, s: i32, ms: i32) -> bool;
101    }
102
103    #[namespace = "rust::cxxqtlib1"]
104    unsafe extern "C++" {
105        include!("cxx-qt-lib/common.h");
106
107        #[doc(hidden)]
108        #[rust_name = "qtime_init_default"]
109        fn construct() -> QTime;
110        #[doc(hidden)]
111        #[rust_name = "qtime_init"]
112        fn construct(h: i32, m: i32, s: i32, ms: i32) -> QTime;
113        #[doc(hidden)]
114        #[rust_name = "qtime_to_qstring"]
115        fn toQString(value: &QTime) -> QString;
116    }
117}
118
119/// The QTime class provides clock time functions.
120#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
121#[repr(C)]
122pub struct QTime {
123    mds: i32,
124}
125
126impl QTime {
127    /// Returns the current time as reported by the system clock.
128    pub fn current_time() -> Self {
129        ffi::qtime_current_time()
130    }
131
132    /// Returns a new QTime instance with the time set to the number of msecs
133    /// since the start of the day, i.e. since 00:00:00.
134    pub fn from_msecs_since_start_of_day(msecs: i32) -> Self {
135        ffi::qtime_from_msecs_since_start_of_day(msecs)
136    }
137
138    /// Returns the QTime represented by the string, using the format given, or an invalid time if the string cannot be parsed.
139    pub fn from_string(string: &ffi::QString, format: &ffi::QString) -> Self {
140        ffi::qtime_from_string(string, format)
141    }
142
143    /// Returns the time represented in the string as a QTime using the format given, or an invalid time if this is not possible.
144    pub fn from_string_enum(string: &ffi::QString, format: ffi::DateFormat) -> Self {
145        ffi::qtime_from_string_enum(string, format)
146    }
147
148    /// Returns the number of milliseconds from this time to t.
149    /// If t is earlier than this time, the number of milliseconds returned is negative.
150    pub fn msecs_to(&self, t: Self) -> i32 {
151        ffi::qtime_msecs_to(self, t)
152    }
153
154    /// Constructs a time with hour h, minute m, seconds s and milliseconds ms.
155    pub fn new(h: i32, m: i32, s: i32, ms: i32) -> Self {
156        ffi::qtime_init(h, m, s, ms)
157    }
158
159    /// Returns the number of seconds from this time to t.
160    /// If t is earlier than this time, the number of seconds returned is negative.
161    pub fn secs_to(&self, t: Self) -> i32 {
162        ffi::qtime_secs_to(self, t)
163    }
164
165    /// Returns true if the specified time is valid; otherwise returns false.
166    /// The time is valid if h is in the range 0 to 23, m and s are in the range 0 to 59, and ms is in the range 0 to 999.
167    pub fn is_valid_time(h: i32, m: i32, s: i32, ms: i32) -> bool {
168        ffi::qtime_is_valid(h, m, s, ms)
169    }
170}
171
172impl Default for QTime {
173    /// Constructs a null time object.
174    fn default() -> Self {
175        ffi::qtime_init_default()
176    }
177}
178
179impl fmt::Display for QTime {
180    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
181        write!(f, "{}", ffi::qtime_to_qstring(self))
182    }
183}
184
185impl fmt::Debug for QTime {
186    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
187        write!(f, "{self}")
188    }
189}
190
191#[cfg(feature = "chrono")]
192use chrono::Timelike;
193
194#[cfg(feature = "chrono")]
195impl TryFrom<chrono::NaiveTime> for QTime {
196    type Error = &'static str;
197
198    /// Errors if [chrono::NaiveTime] has milliseconds larger than 999,
199    /// as Qt does not support representing a leap second in this way
200    fn try_from(value: chrono::NaiveTime) -> Result<Self, Self::Error> {
201        let ms = (value.nanosecond() / 1_000_000) as i32;
202        // NaiveTime can have a nanosecond larger than 1 second
203        // to represent a leap second.
204        //
205        // Qt has no way to represent this, we could add 1 second but
206        // when then happens if the time is 23:59:59 + 1 ?
207        if ms > 999 {
208            return Err("leap second is not supported in Qt");
209        }
210
211        Ok(QTime::new(
212            value.hour() as i32,
213            value.minute() as i32,
214            value.second() as i32,
215            ms,
216        ))
217    }
218}
219
220#[cfg(feature = "chrono")]
221impl TryFrom<QTime> for chrono::NaiveTime {
222    type Error = &'static str;
223
224    fn try_from(value: QTime) -> Result<Self, Self::Error> {
225        chrono::NaiveTime::from_hms_milli_opt(
226            value.hour() as u32,
227            value.minute() as u32,
228            value.second() as u32,
229            value.msec() as u32,
230        )
231        .ok_or("invalid hour, minute, second and/or millisecond")
232    }
233}
234
235#[cfg(feature = "time")]
236impl From<time::Time> for QTime {
237    fn from(value: time::Time) -> Self {
238        QTime::new(
239            value.hour() as i32,
240            value.minute() as i32,
241            value.second() as i32,
242            value.millisecond() as i32,
243        )
244    }
245}
246
247#[cfg(feature = "time")]
248impl TryFrom<QTime> for time::Time {
249    type Error = time::error::ComponentRange;
250
251    fn try_from(value: QTime) -> Result<Self, Self::Error> {
252        time::Time::from_hms_milli(
253            value.hour() as u8,
254            value.minute() as u8,
255            value.second() as u8,
256            value.msec() as u16,
257        )
258    }
259}
260
261// Safety:
262//
263// Static checks on the C++ side ensure that QTime is trivial.
264unsafe impl ExternType for QTime {
265    type Id = type_id!("QTime");
266    type Kind = cxx::kind::Trivial;
267}
268
269#[cfg(test)]
270#[cfg(feature = "chrono")]
271mod test_chrono {
272    use super::*;
273
274    #[test]
275    fn qtime_from_chrono_naive() {
276        let naive = chrono::NaiveTime::from_hms_milli_opt(1, 2, 3, 4).unwrap();
277        let qtime = QTime::new(1, 2, 3, 4);
278        assert_eq!(QTime::try_from(naive).unwrap(), qtime);
279    }
280
281    #[test]
282    fn qtime_from_chrono_naive_leap_second() {
283        let naive = chrono::NaiveTime::from_hms_milli_opt(1, 2, 59, 1_999).unwrap();
284        assert!(QTime::try_from(naive).is_err());
285    }
286
287    #[test]
288    fn qtime_to_chrono_naive() {
289        let naive = chrono::NaiveTime::from_hms_milli_opt(1, 2, 3, 4).unwrap();
290        let qtime = QTime::new(1, 2, 3, 4);
291        assert_eq!(chrono::NaiveTime::try_from(qtime).unwrap(), naive);
292    }
293}
294
295#[cfg(test)]
296#[cfg(feature = "time")]
297mod test_time {
298    use super::*;
299
300    #[test]
301    fn qtime_from_time_time() {
302        let time_time = time::Time::from_hms_milli(1, 2, 3, 4).unwrap();
303        let qtime = QTime::new(1, 2, 3, 4);
304        assert_eq!(QTime::from(time_time), qtime);
305    }
306
307    #[test]
308    fn qtime_to_time_time() {
309        let time_time = time::Time::from_hms_milli(1, 2, 3, 4).unwrap();
310        let qtime = QTime::new(1, 2, 3, 4);
311        assert_eq!(time::Time::try_from(qtime).unwrap(), time_time);
312    }
313}