cxx_qt_lib/core/
qdatetime.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
5use cxx::{type_id, ExternType};
6use std::mem::MaybeUninit;
7use std::{cmp::Ordering, fmt};
8
9use crate::{QDate, QTime};
10
11#[cxx::bridge]
12mod ffi {
13    #[namespace = "Qt"]
14    unsafe extern "C++" {
15        include!("cxx-qt-lib/qt.h");
16        type TimeSpec = crate::TimeSpec;
17    }
18
19    unsafe extern "C++" {
20        include!("cxx-qt-lib/qdate.h");
21        type QDate = crate::QDate;
22        include!("cxx-qt-lib/qdatetime.h");
23        type QDateTime = super::QDateTime;
24        include!("cxx-qt-lib/qtime.h");
25        type QTime = crate::QTime;
26        include!("cxx-qt-lib/qstring.h");
27        type QString = crate::QString;
28        include!("cxx-qt-lib/qtimezone.h");
29        type QTimeZone = crate::QTimeZone;
30
31        /// Returns a QDateTime object containing a datetime nmonths months later than the datetime of this object (or earlier if nmonths is negative).
32        #[rust_name = "add_months"]
33        fn addMonths(self: &QDateTime, nmonths: i32) -> QDateTime;
34
35        /// Returns a QDateTime object containing a datetime nyears years later than the datetime of this object (or earlier if nyears is negative).
36        #[rust_name = "add_years"]
37        fn addYears(self: &QDateTime, nyears: i32) -> QDateTime;
38
39        /// Returns the date part of the datetime.
40        fn date(self: &QDateTime) -> QDate;
41
42        /// Returns if this datetime falls in Daylight-Saving Time.
43        #[rust_name = "is_daylight_time"]
44        fn isDaylightTime(self: &QDateTime) -> bool;
45
46        /// Returns true if both the date and the time are null; otherwise returns false. A null datetime is invalid.
47        #[rust_name = "is_null"]
48        fn isNull(self: &QDateTime) -> bool;
49
50        /// Returns true if both the date and the time are valid and they are valid in the current Qt::TimeSpec, otherwise returns false.
51        #[rust_name = "is_valid"]
52        fn isValid(self: &QDateTime) -> bool;
53
54        /// Returns this date-time's Offset From UTC in seconds.
55        #[rust_name = "offset_from_utc"]
56        fn offsetFromUtc(self: &QDateTime) -> i32;
57
58        /// Sets the timeSpec() to Qt::OffsetFromUTC and the offset to offsetSeconds.
59        ///
60        /// Note this method is only available with Qt < 6.8
61        #[cfg(not(cxxqt_qt_version_at_least_6_8))]
62        #[rust_name = "set_offset_from_utc"]
63        fn setOffsetFromUtc(self: &mut QDateTime, offset_seconds: i32);
64
65        /// Sets the time specification used in this datetime to spec. The datetime will refer to a different point in time.
66        ///
67        /// Note this method is only available with Qt < 6.8
68        #[cfg(not(cxxqt_qt_version_at_least_6_8))]
69        #[rust_name = "set_time_spec"]
70        fn setTimeSpec(self: &mut QDateTime, spec: TimeSpec);
71
72        /// Returns the time part of the datetime.
73        fn time(self: &QDateTime) -> QTime;
74
75        /// Returns the time specification of the datetime.
76        #[rust_name = "time_spec"]
77        fn timeSpec(self: &QDateTime) -> TimeSpec;
78
79        /// Returns the Time Zone Abbreviation for this datetime.
80        #[rust_name = "time_zone_abbreviation"]
81        fn timeZoneAbbreviation(self: &QDateTime) -> QString;
82
83        /// Returns a datetime containing the date and time information in this datetime, but specified using the Qt::LocalTime definition.
84        #[rust_name = "to_local_time"]
85        fn toLocalTime(self: &QDateTime) -> QDateTime;
86
87        /// Returns a copy of this datetime converted to a spec of Qt::OffsetFromUTC with the given offsetSeconds.
88        #[rust_name = "to_offset_from_utc"]
89        fn toOffsetFromUtc(self: &QDateTime, offset_seconds: i32) -> QDateTime;
90
91        /// Returns a copy of this datetime converted to the given time spec.
92        ///
93        /// Note this method is only available with Qt < 6.8
94        #[cfg(not(cxxqt_qt_version_at_least_6_8))]
95        #[rust_name = "to_time_spec"]
96        fn toTimeSpec(self: &QDateTime, spec: TimeSpec) -> QDateTime;
97
98        /// Returns a copy of this datetime converted to the given timeZone
99        #[rust_name = "to_time_zone"]
100        fn toTimeZone(self: &QDateTime, timeZone: &QTimeZone) -> QDateTime;
101
102        /// Returns a datetime containing the date and time information in this datetime, but specified using the Qt::UTC definition.
103        #[rust_name = "to_utc"]
104        fn toUTC(self: &QDateTime) -> QDateTime;
105    }
106
107    #[namespace = "rust::cxxqtlib1"]
108    unsafe extern "C++" {
109        #[doc(hidden)]
110        #[rust_name = "qdatetime_add_days"]
111        fn qdatetimeAddDays(datetime: &QDateTime, ndays: i64) -> QDateTime;
112        #[doc(hidden)]
113        #[rust_name = "qdatetime_add_msecs"]
114        fn qdatetimeAddMSecs(datetime: &QDateTime, msecs: i64) -> QDateTime;
115        #[doc(hidden)]
116        #[rust_name = "qdatetime_add_secs"]
117        fn qdatetimeAddSecs(datetime: &QDateTime, secs: i64) -> QDateTime;
118        #[doc(hidden)]
119        #[rust_name = "qdatetime_current_date_time"]
120        fn qdatetimeCurrentDateTime() -> QDateTime;
121        #[doc(hidden)]
122        #[rust_name = "qdatetime_current_date_time_utc"]
123        fn qdatetimeCurrentDateTimeUtc() -> QDateTime;
124        #[doc(hidden)]
125        #[rust_name = "qdatetime_current_msecs_since_epoch"]
126        fn qdatetimeCurrentMSecsSinceEpoch() -> i64;
127        #[doc(hidden)]
128        #[rust_name = "qdatetime_current_secs_since_epoch"]
129        fn qdatetimeCurrentSecsSinceEpoch() -> i64;
130        #[doc(hidden)]
131        #[rust_name = "qdatetime_days_to"]
132        fn qdatetimeDaysTo(datetime: &QDateTime, other: &QDateTime) -> i64;
133        #[doc(hidden)]
134        #[rust_name = "qdatetime_from_msecs_since_epoch"]
135        fn qdatetimeFromMSecsSinceEpoch(msecs: i64, time_zone: &QTimeZone) -> QDateTime;
136        #[doc(hidden)]
137        #[rust_name = "qdatetime_from_secs_since_epoch"]
138        fn qdatetimeFromSecsSinceEpoch(secs: i64, time_zone: &QTimeZone) -> QDateTime;
139        #[doc(hidden)]
140        #[rust_name = "qdatetime_msecs_to"]
141        fn qdatetimeMSecsTo(datetime: &QDateTime, other: &QDateTime) -> i64;
142        #[doc(hidden)]
143        #[rust_name = "qdatetime_secs_to"]
144        fn qdatetimeSecsTo(datetime: &QDateTime, other: &QDateTime) -> i64;
145        // Note that Qt 5 takes const-ref and Qt 6 takes by-value
146        // for QDateTime::setDate and QDateTime::setTime
147        //
148        // We want by-value, as that is Rust-idiomatic, so for Qt 5 we create a proxy
149        #[doc(hidden)]
150        #[rust_name = "qdatetime_set_date"]
151        fn qdatetimeSetDate(datetime: &mut QDateTime, date: QDate);
152        #[doc(hidden)]
153        #[rust_name = "qdatetime_set_msecs_since_epoch"]
154        fn qdatetimeSetMSecsSinceEpoch(datetime: &mut QDateTime, msecs: i64);
155        #[doc(hidden)]
156        #[rust_name = "qdatetime_set_secs_since_epoch"]
157        fn qdatetimeSetSecsSinceEpoch(datetime: &mut QDateTime, secs: i64);
158        #[doc(hidden)]
159        #[rust_name = "qdatetime_set_time"]
160        fn qdatetimeSetTime(datetime: &mut QDateTime, time: QTime);
161        #[doc(hidden)]
162        #[rust_name = "qdatetime_time_zone"]
163        fn qdatetimeTimeZone(datetime: &QDateTime) -> UniquePtr<QTimeZone>;
164        #[doc(hidden)]
165        #[rust_name = "qdatetime_to_msecs_since_epoch"]
166        fn qdatetimeToMSecsSinceEpoch(datetime: &QDateTime) -> i64;
167        #[doc(hidden)]
168        #[rust_name = "qdatetime_to_secs_since_epoch"]
169        fn qdatetimeToSecsSinceEpoch(datetime: &QDateTime) -> i64;
170        #[rust_name = "qdatetime_settimezone"]
171        fn qdatetimeSetTimeZone(datetime: &mut QDateTime, time_zone: &QTimeZone);
172    }
173
174    #[namespace = "rust::cxxqtlib1"]
175    unsafe extern "C++" {
176        include!("cxx-qt-lib/common.h");
177
178        #[doc(hidden)]
179        #[rust_name = "qdatetime_drop"]
180        fn drop(datetime: &mut QDateTime);
181        #[doc(hidden)]
182        #[rust_name = "qdatetime_init_default"]
183        fn construct() -> QDateTime;
184        #[doc(hidden)]
185        #[rust_name = "qdatetime_init_from_date_and_time_time_zone"]
186        fn construct(date: &QDate, time: &QTime, time_zone: &QTimeZone) -> QDateTime;
187        #[cfg(not(cxxqt_qt_version_at_least_6_8))]
188        #[doc(hidden)]
189        #[rust_name = "qdatetime_init_from_date_and_time_time_spec"]
190        fn construct(
191            date: &QDate,
192            time: &QTime,
193            time_spec: TimeSpec,
194            offset_seconds: i32,
195        ) -> QDateTime;
196        #[doc(hidden)]
197        #[rust_name = "qdatetime_init_from_qdatetime"]
198        fn construct(datetime: &QDateTime) -> QDateTime;
199
200        #[doc(hidden)]
201        #[rust_name = "qdatetime_eq"]
202        fn operatorEq(a: &QDateTime, b: &QDateTime) -> bool;
203        #[doc(hidden)]
204        #[rust_name = "qdatetime_cmp"]
205        fn operatorCmp(a: &QDateTime, b: &QDateTime) -> i8;
206        #[doc(hidden)]
207        #[rust_name = "qdatetime_to_qstring"]
208        fn toQString(value: &QDateTime) -> QString;
209    }
210}
211
212/// The QDateTime class provides date and time functions.
213#[repr(C)]
214pub struct QDateTime {
215    _space: MaybeUninit<usize>,
216}
217
218impl QDateTime {
219    /// Sets the time zone used in this datetime to toZone. The datetime will refer to a different point in time.
220    pub fn set_time_zone(&mut self, time_zone: &ffi::QTimeZone) {
221        ffi::qdatetime_settimezone(self, time_zone)
222    }
223
224    /// Returns a QDateTime object containing a datetime ndays days later than the datetime of this object (or earlier if ndays is negative).
225    pub fn add_days(&self, ndays: i64) -> Self {
226        ffi::qdatetime_add_days(self, ndays)
227    }
228
229    /// Returns a QDateTime object containing a datetime msecs milliseconds later than the datetime of this object (or earlier if msecs is negative).
230    pub fn add_msecs(&self, msecs: i64) -> Self {
231        ffi::qdatetime_add_msecs(self, msecs)
232    }
233
234    /// Returns a QDateTime object containing a datetime s seconds later than the datetime of this object (or earlier if s is negative).
235    pub fn add_secs(&self, secs: i64) -> Self {
236        ffi::qdatetime_add_secs(self, secs)
237    }
238
239    /// Returns the current datetime, as reported by the system clock, in the local time zone.
240    pub fn current_date_time() -> Self {
241        ffi::qdatetime_current_date_time()
242    }
243
244    /// Returns the current datetime, as reported by the system clock, in UTC.
245    pub fn current_date_time_utc() -> Self {
246        ffi::qdatetime_current_date_time_utc()
247    }
248
249    /// Returns the number of milliseconds since 1970-01-01T00:00:00 Universal Coordinated Time.
250    /// This number is like the POSIX time_t variable, but expressed in milliseconds instead.
251    pub fn current_msecs_since_epoch() -> i64 {
252        ffi::qdatetime_current_msecs_since_epoch()
253    }
254
255    /// Returns the number of seconds since 1970-01-01T00:00:00 Universal Coordinated Time.
256    pub fn current_secs_since_epoch() -> i64 {
257        ffi::qdatetime_current_secs_since_epoch()
258    }
259
260    /// Returns the number of days from this datetime to the other datetime.
261    /// The number of days is counted as the number of times midnight is reached between this datetime to the other datetime.
262    /// This means that a 10 minute difference from 23:55 to 0:05 the next day counts as one day.
263    pub fn days_to(&self, other: &Self) -> i64 {
264        ffi::qdatetime_days_to(self, other)
265    }
266
267    /// Construct a Rust QDateTime from a given QDate, QTime, and QTimeZone
268    pub fn from_date_and_time_time_zone(
269        date: &QDate,
270        time: &QTime,
271        time_zone: &ffi::QTimeZone,
272    ) -> Self {
273        ffi::qdatetime_init_from_date_and_time_time_zone(date, time, time_zone)
274    }
275
276    /// Construct a Rust QDateTime from a given QDate, QTime, Qt::TimeSpec, and offset
277    ///
278    /// Note this method is only available with Qt < 6.8
279    #[cfg(not(cxxqt_qt_version_at_least_6_8))]
280    pub fn from_date_and_time_time_spec(
281        date: &QDate,
282        time: &QTime,
283        time_spec: ffi::TimeSpec,
284        offset_seconds: i32,
285    ) -> Self {
286        ffi::qdatetime_init_from_date_and_time_time_spec(date, time, time_spec, offset_seconds)
287    }
288
289    /// Returns a datetime whose date and time are the number of milliseconds msecs that have passed since 1970-01-01T00:00:00.000,
290    /// Coordinated Universal Time (Qt::UTC) and with the given timeZone.
291    pub fn from_msecs_since_epoch(msecs: i64, time_zone: &ffi::QTimeZone) -> Self {
292        ffi::qdatetime_from_msecs_since_epoch(msecs, time_zone)
293    }
294
295    /// Returns a datetime whose date and time are the number of seconds secs that have passed since 1970-01-01T00:00:00.000,
296    /// Coordinated Universal Time (Qt::UTC) and converted to the given spec.
297    pub fn from_secs_since_epoch(secs: i64, time_zone: &ffi::QTimeZone) -> Self {
298        ffi::qdatetime_from_secs_since_epoch(secs, time_zone)
299    }
300
301    /// Returns the number of milliseconds from this datetime to the other datetime.
302    /// If the other datetime is earlier than this datetime, the value returned is negative.
303    pub fn msecs_to(&self, other: &Self) -> i64 {
304        ffi::qdatetime_msecs_to(self, other)
305    }
306
307    /// Returns the number of seconds from this datetime to the other datetime.
308    /// If the other datetime is earlier than this datetime, the value returned is negative.
309    pub fn secs_to(&self, other: &Self) -> i64 {
310        ffi::qdatetime_secs_to(self, other)
311    }
312
313    /// Sets the date part of this datetime to date. If no time is set yet, it is set to midnight.
314    /// If date is invalid, this QDateTime becomes invalid.
315    pub fn set_date(&mut self, date: QDate) {
316        ffi::qdatetime_set_date(self, date);
317    }
318
319    /// Sets the date and time given the number of milliseconds msecs that have passed since 1970-01-01T00:00:00.000,
320    /// Coordinated Universal Time (Qt::UTC). On systems that do not support time zones this function will behave as if local time were Qt::UTC.
321    pub fn set_msecs_since_epoch(&mut self, msecs: i64) {
322        ffi::qdatetime_set_msecs_since_epoch(self, msecs);
323    }
324
325    /// Sets the date and time given the number of seconds secs that have passed since 1970-01-01T00:00:00.000,
326    /// Coordinated Universal Time (Qt::UTC). On systems that do not support time zones this function will behave as if local time were Qt::UTC.
327    pub fn set_secs_since_epoch(&mut self, secs: i64) {
328        ffi::qdatetime_set_secs_since_epoch(self, secs);
329    }
330
331    /// Sets the time part of this datetime to time. If time is not valid, this function sets it to midnight.
332    /// Therefore, it's possible to clear any set time in a QDateTime by setting it to a default QTime.
333    pub fn set_time(&mut self, time: QTime) {
334        ffi::qdatetime_set_time(self, time);
335    }
336
337    /// Returns the time zone of the datetime.
338    pub fn time_zone(&self) -> cxx::UniquePtr<ffi::QTimeZone> {
339        ffi::qdatetime_time_zone(self)
340    }
341
342    /// Returns the datetime as the number of milliseconds that have passed since 1970-01-01T00:00:00.000, Coordinated Universal Time (Qt::UTC).
343    pub fn to_msecs_since_epoch(&self) -> i64 {
344        ffi::qdatetime_to_msecs_since_epoch(self)
345    }
346
347    /// Returns the datetime as the number of seconds that have passed since 1970-01-01T00:00:00.000, Coordinated Universal Time (Qt::UTC).
348    pub fn to_secs_since_epoch(&self) -> i64 {
349        ffi::qdatetime_to_secs_since_epoch(self)
350    }
351}
352
353impl Clone for QDateTime {
354    /// Constructs a copy of the other datetime.
355    fn clone(&self) -> Self {
356        ffi::qdatetime_init_from_qdatetime(self)
357    }
358}
359
360impl Default for QDateTime {
361    /// Construct a default null QDateTime
362    fn default() -> Self {
363        ffi::qdatetime_init_default()
364    }
365}
366
367impl PartialEq for QDateTime {
368    fn eq(&self, other: &Self) -> bool {
369        ffi::qdatetime_eq(self, other)
370    }
371}
372
373impl Eq for QDateTime {}
374
375impl PartialOrd for QDateTime {
376    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
377        Some(self.cmp(other))
378    }
379}
380
381impl Ord for QDateTime {
382    fn cmp(&self, other: &Self) -> Ordering {
383        ffi::qdatetime_cmp(self, other).cmp(&0)
384    }
385}
386
387impl fmt::Display for QDateTime {
388    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
389        write!(f, "{}", ffi::qdatetime_to_qstring(self))
390    }
391}
392
393impl fmt::Debug for QDateTime {
394    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
395        write!(f, "{self}")
396    }
397}
398
399impl Drop for QDateTime {
400    /// Destroys the datetime.
401    fn drop(&mut self) {
402        ffi::qdatetime_drop(self);
403    }
404}
405
406#[cfg(feature = "chrono")]
407use chrono::Offset;
408
409#[cfg(feature = "chrono")]
410impl<Tz: chrono::TimeZone> TryFrom<chrono::DateTime<Tz>> for QDateTime {
411    type Error = &'static str;
412
413    fn try_from(value: chrono::DateTime<Tz>) -> Result<Self, Self::Error> {
414        let tz = crate::QTimeZone::from_offset_seconds(value.offset().fix().local_minus_utc());
415        Ok(QDateTime::from_date_and_time_time_zone(
416            &QDate::from(value.date_naive()),
417            &QTime::try_from(value.time())?,
418            tz.as_ref().ok_or("Could not construct timezone")?,
419        ))
420    }
421}
422
423#[cfg(feature = "chrono")]
424impl TryFrom<QDateTime> for chrono::DateTime<chrono::FixedOffset> {
425    type Error = &'static str;
426
427    fn try_from(value: QDateTime) -> Result<Self, Self::Error> {
428        let timezone_east = chrono::FixedOffset::east_opt(value.offset_from_utc())
429            .expect("out-of-bound offset secs");
430        let value_utc = value.to_utc();
431        let naivedatetime_east = chrono::NaiveDate::try_from(value_utc.date())?
432            .and_time(chrono::NaiveTime::try_from(value_utc.time())?);
433        Ok(
434            chrono::DateTime::<chrono::FixedOffset>::from_naive_utc_and_offset(
435                naivedatetime_east,
436                timezone_east,
437            ),
438        )
439    }
440}
441
442#[cfg(feature = "chrono")]
443impl TryFrom<QDateTime> for chrono::DateTime<chrono::Utc> {
444    type Error = &'static str;
445
446    fn try_from(value: QDateTime) -> Result<Self, Self::Error> {
447        let value_utc = value.to_utc();
448        let naivedatetime_utc = chrono::NaiveDate::try_from(value_utc.date())?
449            .and_time(chrono::NaiveTime::try_from(value_utc.time())?);
450        Ok(chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
451            naivedatetime_utc,
452            chrono::Utc,
453        ))
454    }
455}
456
457#[cfg(feature = "time")]
458impl From<time::OffsetDateTime> for QDateTime {
459    fn from(value: time::OffsetDateTime) -> Self {
460        let tz = crate::QTimeZone::from_offset_seconds(value.offset().whole_seconds());
461        QDateTime::from_date_and_time_time_zone(
462            &QDate::from(value.date()),
463            &QTime::from(value.time()),
464            tz.as_ref().expect("Could not construct timezone"),
465        )
466    }
467}
468
469#[cfg(feature = "time")]
470impl From<time::PrimitiveDateTime> for QDateTime {
471    fn from(value: time::PrimitiveDateTime) -> Self {
472        let tz = crate::QTimeZone::utc();
473        QDateTime::from_date_and_time_time_zone(
474            &QDate::from(value.date()),
475            &QTime::from(value.time()),
476            tz.as_ref().expect("Could not construct timezone"),
477        )
478    }
479}
480
481#[cfg(feature = "time")]
482impl TryFrom<QDateTime> for time::OffsetDateTime {
483    type Error = time::error::ComponentRange;
484
485    fn try_from(value: QDateTime) -> Result<Self, Self::Error> {
486        Ok(time::Date::try_from(value.date())?
487            .with_time(time::Time::try_from(value.time())?)
488            .assume_offset(time::UtcOffset::from_whole_seconds(
489                value.offset_from_utc(),
490            )?))
491    }
492}
493
494#[cfg(feature = "time")]
495impl TryFrom<QDateTime> for time::PrimitiveDateTime {
496    type Error = time::error::ComponentRange;
497
498    fn try_from(value: QDateTime) -> Result<Self, Self::Error> {
499        let value_utc = value.to_utc();
500        Ok(time::Date::try_from(value_utc.date())?
501            .with_time(time::Time::try_from(value_utc.time())?))
502    }
503}
504
505// Safety:
506//
507// Static checks on the C++ side to ensure the size is the same.
508unsafe impl ExternType for QDateTime {
509    type Id = type_id!("QDateTime");
510    type Kind = cxx::kind::Trivial;
511}
512
513#[cfg(test)]
514mod test {
515    use super::*;
516
517    #[test]
518    fn test_ordering() {
519        let qdatetime_a = QDateTime::from_date_and_time_time_zone(
520            &QDate::new(2023, 1, 1),
521            &QTime::new(1, 1, 1, 1),
522            &ffi::QTimeZone::utc(),
523        );
524        let qdatetime_b = QDateTime::from_date_and_time_time_zone(
525            &QDate::new(2023, 2, 2),
526            &QTime::new(2, 2, 2, 2),
527            &ffi::QTimeZone::utc(),
528        );
529
530        assert!(qdatetime_a < qdatetime_b);
531        assert_eq!(qdatetime_a.cmp(&qdatetime_b), Ordering::Less);
532        assert_eq!(qdatetime_b.cmp(&qdatetime_a), Ordering::Greater);
533        assert_eq!(qdatetime_a.cmp(&qdatetime_a), Ordering::Equal);
534    }
535}
536
537#[cfg(test)]
538#[cfg(feature = "chrono")]
539mod test_chrono {
540    use super::*;
541
542    #[test]
543    fn qdatetime_from_chrono() {
544        let datetime_east = {
545            let timezone_east = chrono::FixedOffset::east_opt(60 * 60).unwrap();
546            let naivedatetime_east = chrono::NaiveDate::from_ymd_opt(2023, 1, 1)
547                .unwrap()
548                .and_hms_milli_opt(1, 2, 3, 4)
549                .unwrap();
550            chrono::DateTime::<chrono::FixedOffset>::from_naive_utc_and_offset(
551                naivedatetime_east,
552                timezone_east,
553            )
554        };
555
556        let qdatetime = QDateTime::from_date_and_time_time_zone(
557            &QDate::new(2023, 1, 1),
558            // Chrono adds the offset to the given time, so add the offset here to match Chrono
559            &QTime::new(1 + 1 /* plus the offset */, 2, 3, 4),
560            &ffi::QTimeZone::from_offset_seconds(60 * 60),
561        );
562        assert_eq!(QDateTime::try_from(datetime_east).unwrap(), qdatetime);
563    }
564
565    #[test]
566    fn qdatetime_to_chrono_fixed_offset() {
567        let datetime_east = {
568            let timezone_east = chrono::FixedOffset::east_opt(60 * 60).unwrap();
569            let naivedatetime_east = chrono::NaiveDate::from_ymd_opt(2023, 1, 1)
570                .unwrap()
571                // Chrono adds the offset to the given time, so minus the offset here to match Qt
572                .and_hms_milli_opt(1 - 1 /* minus the offset */, 2, 3, 4)
573                .unwrap();
574            chrono::DateTime::<chrono::FixedOffset>::from_naive_utc_and_offset(
575                naivedatetime_east,
576                timezone_east,
577            )
578        };
579
580        let qdatetime = QDateTime::from_date_and_time_time_zone(
581            &QDate::new(2023, 1, 1),
582            &QTime::new(1, 2, 3, 4),
583            &ffi::QTimeZone::from_offset_seconds(60 * 60),
584        );
585        assert_eq!(
586            chrono::DateTime::<chrono::FixedOffset>::try_from(qdatetime).unwrap(),
587            datetime_east
588        );
589    }
590
591    #[test]
592    fn qdatetime_to_chrono_utc() {
593        let datetime_utc = {
594            let naivedatetime_utc = chrono::NaiveDate::from_ymd_opt(2023, 1, 1)
595                .unwrap()
596                .and_hms_milli_opt(1, 2, 3, 4)
597                .unwrap();
598            chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
599                naivedatetime_utc,
600                chrono::Utc,
601            )
602        };
603
604        let qdatetime = QDateTime::from_date_and_time_time_zone(
605            &QDate::new(2023, 1, 1),
606            &QTime::new(1, 2, 3, 4),
607            &ffi::QTimeZone::utc(),
608        );
609        assert_eq!(
610            chrono::DateTime::<chrono::Utc>::try_from(qdatetime).unwrap(),
611            datetime_utc
612        );
613    }
614
615    #[test]
616    fn qdatetime_to_chrono_utc_with_offset() {
617        let datetime_utc = {
618            let naivedatetime_utc = chrono::NaiveDate::from_ymd_opt(2023, 1, 1)
619                .unwrap()
620                .and_hms_milli_opt(0, 2, 3, 4)
621                .unwrap();
622            chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
623                naivedatetime_utc,
624                chrono::Utc,
625            )
626        };
627
628        let qdatetime = QDateTime::from_date_and_time_time_zone(
629            &QDate::new(2023, 1, 1),
630            &QTime::new(1, 2, 3, 4),
631            // Should cause one hour offset when in chrono::DateTime
632            &ffi::QTimeZone::from_offset_seconds(60 * 60),
633        );
634        assert_eq!(
635            chrono::DateTime::<chrono::Utc>::try_from(qdatetime).unwrap(),
636            datetime_utc
637        );
638    }
639}
640
641#[cfg(test)]
642#[cfg(feature = "time")]
643mod test_time {
644    use super::*;
645
646    #[test]
647    fn qdatetime_to_time_offsetdatetime() {
648        let time_offsetdatetime = time::Date::from_calendar_date(2023, time::Month::January, 1)
649            .unwrap()
650            .with_hms_milli(1, 2, 3, 4)
651            .unwrap()
652            .assume_offset(time::UtcOffset::from_whole_seconds(60 * 60).unwrap());
653
654        let qdatetime = QDateTime::from_date_and_time_time_zone(
655            &QDate::new(2023, 1, 1),
656            &QTime::new(1, 2, 3, 4),
657            &ffi::QTimeZone::from_offset_seconds(60 * 60),
658        );
659        assert_eq!(
660            time::OffsetDateTime::try_from(qdatetime).unwrap(),
661            time_offsetdatetime
662        );
663    }
664
665    #[test]
666    fn qdatetime_to_time_primitivedatetime() {
667        let time_offsetdatetime = time::Date::from_calendar_date(2023, time::Month::January, 1)
668            .unwrap()
669            .with_hms_milli(1, 2, 3, 4)
670            .unwrap();
671
672        let qdatetime = QDateTime::from_date_and_time_time_zone(
673            &QDate::new(2023, 1, 1),
674            &QTime::new(1, 2, 3, 4),
675            &ffi::QTimeZone::utc(),
676        );
677        assert_eq!(
678            time::PrimitiveDateTime::try_from(qdatetime).unwrap(),
679            time_offsetdatetime
680        );
681    }
682
683    #[test]
684    fn qdatetime_from_time_offsetdatetime() {
685        let time_offsetdatetime = time::Date::from_calendar_date(2023, time::Month::January, 1)
686            .unwrap()
687            .with_hms_milli(1, 2, 3, 4)
688            .unwrap()
689            .assume_offset(time::UtcOffset::from_whole_seconds(60 * 60).unwrap());
690
691        let qdatetime = QDateTime::from_date_and_time_time_zone(
692            &QDate::new(2023, 1, 1),
693            &QTime::new(1, 2, 3, 4),
694            &ffi::QTimeZone::from_offset_seconds(60 * 60),
695        );
696        assert_eq!(QDateTime::from(time_offsetdatetime), qdatetime);
697    }
698
699    #[test]
700    fn qdatetime_from_time_primitivedatetime() {
701        let time_offsetdatetime = time::Date::from_calendar_date(2023, time::Month::January, 1)
702            .unwrap()
703            .with_hms_milli(1, 2, 3, 4)
704            .unwrap();
705
706        let qdatetime = QDateTime::from_date_and_time_time_zone(
707            &QDate::new(2023, 1, 1),
708            &QTime::new(1, 2, 3, 4),
709            &ffi::QTimeZone::utc(),
710        );
711        assert_eq!(QDateTime::from(time_offsetdatetime), qdatetime);
712    }
713}