diff options
author | Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> | 2022-04-02 03:34:07 +0200 |
---|---|---|
committer | Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> | 2022-04-13 18:45:54 +0200 |
commit | 7de83f06c1170a1a12d57b218c4009326444d4db (patch) | |
tree | 88aea0709ad39528a315ce9699362ab1870794e6 /tests/auto | |
parent | a5b9600356cb8941a2d9685975b2454837b5e6ed (diff) |
QDateTime: add conversions for time_point and zoned_time
In C++20, QDateTime is a direct equivalent of a sys_time<milliseconds>
time point. (Before, it might not have been, because system_clock before
C++20 was not guaranteed to be tracking Unix time, AKA UTC time without
leap seconds.) To be specific, sys_time<milliseconds> corresponds to
a QDateTime using the Qt::UTC timespec.
This patch:
1) adds named constructors taking time_points:
* a generic one taking any time_point convertible (via clock_cast) to
a system_clock (this obviously includes system_clock, but also e.g.
utc_clock)
* another couple taking local_time, interpreted as a duration from
1/1/1970 in local time.
2) adds a named constructor from zoned_time (i.e. a sys_time + a
timezone), that we can easily support via QTimeZone.
3) add conversion functions towards sys_time, matching the existing
to(M)SecsSinceEpoch() functions.
[ChangeLog][QtCore][QDateTime] QDateTime can now be constructed
from std::chrono::time_point objects (including local_time), as
well as from std::chrono::zoned_time objects. Moreover, they
can be converted to std::chrono::time_point using system_clock
as their clock.
Change-Id: Ic6409bde43bc3e745d9df6257e0a77157472352d
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Diffstat (limited to 'tests/auto')
-rw-r--r-- | tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp b/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp index 17c153a59e..17f560b35c 100644 --- a/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp +++ b/tests/auto/corelib/time/qdatetime/tst_qdatetime.cpp @@ -152,6 +152,15 @@ private Q_SLOTS: void macTypes(); + void stdCompatibilitySysTime_data(); + void stdCompatibilitySysTime(); + void stdCompatibilityLocalTime_data(); + void stdCompatibilityLocalTime(); +#if QT_CONFIG(timezone) + void stdCompatibilityZonedTime_data(); + void stdCompatibilityZonedTime(); +#endif + private: enum { LocalTimeIsUtc = 0, LocalTimeAheadOfUtc = 1, LocalTimeBehindUtc = -1} localTimeType; int preZoneFix; @@ -4121,5 +4130,223 @@ void tst_QDateTime::macTypes() #endif } +#if __cpp_lib_chrono >= 201907L +using StdSysMillis = std::chrono::sys_time<std::chrono::milliseconds>; +Q_DECLARE_METATYPE(StdSysMillis); +#endif + +void tst_QDateTime::stdCompatibilitySysTime_data() +{ +#if __cpp_lib_chrono >= 201907L + QTest::addColumn<StdSysMillis>("sysTime"); + QTest::addColumn<QDateTime>("expected"); + + using namespace std::chrono; + + QTest::newRow("zero") + << StdSysMillis(0s) + << QDateTime(QDate(1970, 1, 1), QTime(0, 0, 0), Qt::UTC); + QTest::newRow("1s") + << StdSysMillis(1s) + << QDateTime(QDate(1970, 1, 1), QTime(0, 0, 1), Qt::UTC); + QTest::newRow("1ms") + << StdSysMillis(1ms) + << QDateTime(QDate(1970, 1, 1), QTime(0, 0, 0, 1), Qt::UTC); + QTest::newRow("365d") + << StdSysMillis(days(365)) + << QDateTime(QDate(1971, 1, 1), QTime(0, 0, 0), Qt::UTC); + QTest::newRow("-1s") + << StdSysMillis(-1s) + << QDateTime(QDate(1969, 12, 31), QTime(23, 59, 59), Qt::UTC); + QTest::newRow("-1ms") + << StdSysMillis(-1ms) + << QDateTime(QDate(1969, 12, 31), QTime(23, 59, 59, 999), Qt::UTC); + + { + // The first leap second occurred on 30 June 1972 at 23:59:60. + // Check that QDateTime does not take that leap second into account (like sys_time) + const year_month_day firstLeapSecondDate = 1972y/July/1; + const sys_days firstLeapSecondDateAsSysDays = firstLeapSecondDate; + QTest::newRow("first_leap_second") + << StdSysMillis(firstLeapSecondDateAsSysDays) + << QDateTime(QDate(1972, 7, 1), QTime(0, 0, 0), Qt::UTC); + } + + { + // Random date + const sys_days date = 2000y/January/31; + const StdSysMillis dateTime = date + 3h + 10min + 42s; + QTest::newRow("2000-01-31 03:10:42") + << dateTime + << QDateTime(QDate(2000, 1, 31), QTime(3, 10, 42), Qt::UTC); + } +#else + QSKIP("This test requires C++20's <chrono>."); +#endif +} + +void tst_QDateTime::stdCompatibilitySysTime() +{ +#if __cpp_lib_chrono >= 201907L + QFETCH(StdSysMillis, sysTime); + QFETCH(QDateTime, expected); + + using namespace std::chrono; + + // system_clock in milliseconds -> QDateTime + QDateTime dtFromSysTime = QDateTime::fromStdTimePoint(sysTime); + QCOMPARE(dtFromSysTime, expected); + QCOMPARE(dtFromSysTime.timeSpec(), Qt::UTC); + + // QDateTime -> system_clock in milliseconds + StdSysMillis sysTimeFromDt = dtFromSysTime.toStdSysMilliseconds(); + QCOMPARE(sysTimeFromDt, sysTime); + + // system_clock in seconds -> QDateTime + sys_seconds sysTimeSecs = floor<seconds>(sysTime); + QDateTime dtFromSysSeconds = QDateTime::fromStdTimePoint(sysTimeSecs); + QDateTime expectedInSeconds = expected.addMSecs(-expected.time().msec()); // "floor" + QCOMPARE(dtFromSysSeconds, expectedInSeconds); + QCOMPARE(dtFromSysSeconds.timeSpec(), Qt::UTC); + + // QDateTime -> system_clock in seconds + sys_seconds sysTimeFromDtSecs = dtFromSysSeconds.toStdSysSeconds(); + QCOMPARE(sysTimeFromDtSecs, sysTimeSecs); + + // utc_clock in milliseconds -> QDateTime + utc_time<std::chrono::milliseconds> utcTime = utc_clock::from_sys(sysTime); + QDateTime dtFromUtcTime = QDateTime::fromStdTimePoint(utcTime); + QCOMPARE(dtFromUtcTime, expected); + QCOMPARE(dtFromUtcTime.timeSpec(), Qt::UTC); + + // QDateTime -> system_clock in milliseconds + sysTimeFromDt = dtFromUtcTime.toStdSysMilliseconds(); + QCOMPARE(sysTimeFromDt, sysTime); +#else + QSKIP("This test requires C++20's <chrono>."); +#endif +} + +#if __cpp_lib_chrono >= 201907L +using StdLocalMillis = std::chrono::local_time<std::chrono::milliseconds>; +Q_DECLARE_METATYPE(StdLocalMillis); +#endif + +void tst_QDateTime::stdCompatibilityLocalTime_data() +{ +#if __cpp_lib_chrono >= 201907L + QTest::addColumn<StdLocalMillis>("localTime"); + QTest::addColumn<QDateTime>("expected"); + + using namespace std::chrono; + + QTest::newRow("zero") + << StdLocalMillis(0s) + << QDateTime(QDate(1970, 1, 1), QTime(0, 0, 0), Qt::LocalTime); + QTest::newRow("1s") + << StdLocalMillis(1s) + << QDateTime(QDate(1970, 1, 1), QTime(0, 0, 1), Qt::LocalTime); + QTest::newRow("1ms") + << StdLocalMillis(1ms) + << QDateTime(QDate(1970, 1, 1), QTime(0, 0, 0, 1), Qt::LocalTime); + QTest::newRow("365d") + << StdLocalMillis(days(365)) + << QDateTime(QDate(1971, 1, 1), QTime(0, 0, 0), Qt::LocalTime); + QTest::newRow("-1s") + << StdLocalMillis(-1s) + << QDateTime(QDate(1969, 12, 31), QTime(23, 59, 59), Qt::LocalTime); + QTest::newRow("-1ms") + << StdLocalMillis(-1ms) + << QDateTime(QDate(1969, 12, 31), QTime(23, 59, 59, 999), Qt::LocalTime); + { + // Random date + const local_days date = local_days(2000y/January/31); + const StdLocalMillis dateTime = date + 3h + 10min + 42s; + QTest::newRow("2000-01-31 03:10:42") + << dateTime + << QDateTime(QDate(2000, 1, 31), QTime(3, 10, 42), Qt::LocalTime); + } +#else + QSKIP("This test requires C++20's <chrono>."); +#endif +} + +void tst_QDateTime::stdCompatibilityLocalTime() +{ +#if __cpp_lib_chrono >= 201907L + QFETCH(StdLocalMillis, localTime); + QFETCH(QDateTime, expected); + + using namespace std::chrono; + + QDateTime dtFromLocalTime = QDateTime::fromStdLocalTime(localTime); + QCOMPARE(dtFromLocalTime, expected); + QCOMPARE(dtFromLocalTime.timeSpec(), Qt::LocalTime); + + const time_zone *tz = current_zone(); + QVERIFY(tz); + const StdSysMillis sysMillis = tz->to_sys(localTime); + QCOMPARE(dtFromLocalTime.toStdSysMilliseconds(), sysMillis); +#else + QSKIP("This test requires C++20's <chrono>."); +#endif +} + +#if QT_CONFIG(timezone) +#if __cpp_lib_chrono >= 201907L +using StdZonedMillis = std::chrono::zoned_time<std::chrono::milliseconds>; +Q_DECLARE_METATYPE(StdZonedMillis); +#endif + +void tst_QDateTime::stdCompatibilityZonedTime_data() +{ +#if __cpp_lib_chrono >= 201907L + QTest::addColumn<StdZonedMillis>("zonedTime"); + QTest::addColumn<QDateTime>("expected"); + + using namespace std::chrono; + using namespace std::literals; + + const char timeZoneName[] = "Europe/Oslo"; + const QTimeZone timeZone(timeZoneName); + + { + StdZonedMillis zs(timeZoneName, local_days(2021y/1/1)); + QTest::addRow("localTimeOslo") + << zs + << QDateTime(QDate(2021, 1, 1), QTime(0, 0, 0), timeZone); + } + { + StdZonedMillis zs(timeZoneName, sys_days(2021y/1/1)); + QTest::addRow("sysTimeOslo") + << zs + << QDateTime(QDate(2021, 1, 1), QTime(1, 0, 0), timeZone); + } + { + StdZonedMillis zs(timeZoneName, sys_days(2021y/7/1)); + QTest::addRow("sysTimeOslo summer") + << zs + << QDateTime(QDate(2021, 7, 1), QTime(2, 0, 0), timeZone); + } +#else + QSKIP("This test requires C++20's <chrono>."); +#endif +} + +void tst_QDateTime::stdCompatibilityZonedTime() +{ +#if __cpp_lib_chrono >= 201907L + QFETCH(StdZonedMillis, zonedTime); + QFETCH(QDateTime, expected); + + QDateTime dtFromZonedTime = QDateTime::fromStdZonedTime(zonedTime); + QCOMPARE(dtFromZonedTime, expected); + QCOMPARE(dtFromZonedTime.timeSpec(), Qt::TimeZone); +#else + QSKIP("This test requires C++20's <chrono>."); +#endif +} +#endif // QT_CONFIG(timezone) + QTEST_APPLESS_MAIN(tst_QDateTime) #include "tst_qdatetime.moc" |