diff options
Diffstat (limited to 'tests/auto/corelib/time/qdate')
-rw-r--r-- | tests/auto/corelib/time/qdate/CMakeLists.txt | 12 | ||||
-rw-r--r-- | tests/auto/corelib/time/qdate/tst_qdate.cpp | 895 |
2 files changed, 520 insertions, 387 deletions
diff --git a/tests/auto/corelib/time/qdate/CMakeLists.txt b/tests/auto/corelib/time/qdate/CMakeLists.txt index 647be9a6f8..4d0f04a967 100644 --- a/tests/auto/corelib/time/qdate/CMakeLists.txt +++ b/tests/auto/corelib/time/qdate/CMakeLists.txt @@ -1,15 +1,23 @@ -# Generated from qdate.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qdate Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qdate LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qdate SOURCES tst_qdate.cpp DEFINES QT_NO_FOREACH QT_NO_KEYWORDS - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate + Qt::TestPrivate ) diff --git a/tests/auto/corelib/time/qdate/tst_qdate.cpp b/tests/auto/corelib/time/qdate/tst_qdate.cpp index b14903f92f..5b715e8c55 100644 --- a/tests/auto/corelib/time/qdate/tst_qdate.cpp +++ b/tests/auto/corelib/time/qdate/tst_qdate.cpp @@ -1,37 +1,27 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2016 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only -#include <private/qglobal_p.h> // for the icu feature test +#include <QDateTime> #include <QTest> + +#include <QLocale> +#include <QMap> #include <QTimeZone> -#include <qdatetime.h> -#include <qlocale.h> + +#include <private/qglobal_p.h> // for the icu feature test +#include <private/qcomparisontesthelper_p.h> +#include <private/qdatetime_p.h> +#if !QT_CONFIG(timezone) +# include <private/qtenvironmentvariables_p.h> // for qTzName() +#endif + +using namespace QtPrivate::DateTimeConstants; +using namespace Qt::StringLiterals; + +#if defined(Q_OS_WIN) && !QT_CONFIG(icu) +# define USING_WIN_TZ +#endif class tst_QDate : public QObject { @@ -56,10 +46,8 @@ private Q_SLOTS: void weekNumber_invalid(); void weekNumber_data(); void weekNumber(); -#if QT_CONFIG(timezone) void startOfDay_endOfDay_data(); void startOfDay_endOfDay(); -#endif void startOfDay_endOfDay_fixed_data(); void startOfDay_endOfDay_fixed(); void startOfDay_endOfDay_bounds(); @@ -71,12 +59,12 @@ private Q_SLOTS: void addYears_data(); void addYears(); void daysTo(); + void orderingCompiles(); void operator_eq_eq_data(); void operator_eq_eq(); - void operator_lt(); - void operator_gt(); - void operator_lt_eq(); - void operator_gt_eq(); + void ordering_data(); + void ordering(); + void ordering_chrono_types(); void operator_insert_extract_data(); void operator_insert_extract(); #if QT_CONFIG(datestring) @@ -102,6 +90,15 @@ private Q_SLOTS: void qdebug() const; private: QDate defDate() const { return QDate(1900, 1, 1); } + + QDate epochDate() const { + using namespace QtPrivate::DateTimeConstants; + Q_ASSERT(JULIAN_DAY_FOR_EPOCH == QDate(1970, 1, 1).toJulianDay()); + return QDate::fromJulianDay(JULIAN_DAY_FOR_EPOCH); + } + + static constexpr qint64 minJd = JulianDayMin; + static constexpr qint64 maxJd = JulianDayMax; QDate invalidDate() const { return QDate(); } }; @@ -112,9 +109,6 @@ void tst_QDate::isNull_data() QTest::addColumn<qint64>("jd"); QTest::addColumn<bool>("null"); - qint64 minJd = Q_INT64_C(-784350574879); - qint64 maxJd = Q_INT64_C( 784354017364); - QTest::newRow("qint64 min") << std::numeric_limits<qint64>::min() << true; QTest::newRow("minJd - 1") << minJd - 1 << true; QTest::newRow("minJd") << minJd << false; @@ -198,6 +192,32 @@ void tst_QDate::isValid_data() QTest::newRow("jd latest formula") << 1400000 << 12 << 31 << qint64(513060925) << true; } +#if __cpp_lib_chrono >= 201907L +// QDate has a bigger range than year_month_date. The tests use this bigger +// range. However building a year_month_time with "out of range" data has +// unspecified results, so don't do that. See [time.cal.year], +// [time.cal.month], [time.cal.day]. Also, std::chrono::year has a year 0, so +// take that into account. +static std::optional<std::chrono::year_month_day> convertToStdYearMonthDay(int y, int m, int d) +{ + using namespace std::chrono; + + if (y >= int((year::min)()) + && y <= int((year::max)()) + && m >= 0 + && m <= 255 + && d >= 0 + && d <= 255) + { + if (y < 0) + ++y; + return std::make_optional(year(y) / m / d); + } + + return std::nullopt; +} +#endif + void tst_QDate::isValid() { QFETCH(int, year); @@ -217,6 +237,19 @@ void tst_QDate::isValid() QCOMPARE(d.year(), year); QCOMPARE(d.month(), month); QCOMPARE(d.day(), day); +#if __cpp_lib_chrono >= 201907L + std::optional<std::chrono::year_month_day> ymd = convertToStdYearMonthDay(year, month, day); + if (ymd) { + QDate d = *ymd; + QCOMPARE(d.year(), year); + QCOMPARE(d.month(), month); + QCOMPARE(d.day(), day); + + const std::chrono::sys_days qdateSysDays = d.toStdSysDays(); + const std::chrono::sys_days ymdSysDays = *ymd; + QCOMPARE(qdateSysDays, ymdSysDays); + } +#endif } else { QCOMPARE(d.year(), 0); QCOMPARE(d.month(), 0); @@ -463,123 +496,178 @@ void tst_QDate::weekNumber_invalid() QCOMPARE( dt.weekNumber( &yearNumber ), 0 ); } -#if QT_CONFIG(timezone) +/* The MS backend tends to lack data for historical transitions. So some of the + transition-based tests will get wrong results, that we can't do anything + about, when using that backend. Rather than complicating the #if-ery more, + overtly record, in a flags column, which we need to ignore and merely make + the testing of these flags subject to #if-ery. + + Android appears to lack at least one other. +*/ +enum BackendKludge { IgnoreStart = 1, IgnoreEnd = 2, }; +Q_DECLARE_FLAGS(BackendKludges, BackendKludge) +Q_DECLARE_OPERATORS_FOR_FLAGS(BackendKludges) + void tst_QDate::startOfDay_endOfDay_data() { QTest::addColumn<QDate>("date"); // Typically a spring-forward. // A zone in which that date's start and end are worth checking: - QTest::addColumn<QByteArray>("zoneName"); + QTest::addColumn<QTimeZone>("zone"); // The start and end times in that zone: QTest::addColumn<QTime>("start"); QTest::addColumn<QTime>("end"); + // Ignored for backends that don't need it: + QTest::addColumn<BackendKludges>("kludge"); + + const QTime early(0, 0), late(23, 59, 59, 999), invalid(QDateTime().time()); + constexpr BackendKludges Clean = {}; + constexpr BackendKludges IgnoreBoth = IgnoreStart | IgnoreEnd; +#ifdef USING_WIN_TZ + constexpr BackendKludges MsNoStart = IgnoreStart; + constexpr BackendKludges MsNoBoth = IgnoreBoth; +#else + constexpr BackendKludges MsNoStart = Clean; + constexpr BackendKludges MsNoBoth = Clean; + // And use IgnoreBoth directly for the one transition Android lacks. +#endif + const QTimeZone UTC(QTimeZone::UTC); - const QTime initial(0, 0), final(23, 59, 59, 999), invalid(QDateTime().time()); + using Bound = std::numeric_limits<qint64>; + const auto dateAtMillis = [UTC](qint64 millis) { + return QDateTime::fromMSecsSinceEpoch(millis, UTC).date(); + }; - // UTC is always a valid zone. - QTest::newRow("epoch") - << QDate(1970, 1, 1) << QByteArray("UTC") - << initial << final; - if (QTimeZone("America/Sao_Paulo").isValid()) { - QTest::newRow("Brazil") - << QDate(2008, 10, 19) << QByteArray("America/Sao_Paulo") - << QTime(1, 0) << final; - } -#if QT_CONFIG(icu) || !defined(Q_OS_WIN) // MS's TZ APIs lack data - if (QTimeZone("Europe/Sofia").isValid()) { - QTest::newRow("Sofia") - << QDate(1994, 3, 27) << QByteArray("Europe/Sofia") - << QTime(1, 0) << final; - } -#endif - if (QTimeZone("Pacific/Kiritimati").isValid()) { - QTest::newRow("Kiritimati") - << QDate(1994, 12, 31) << QByteArray("Pacific/Kiritimati") - << invalid << invalid; + // UTC and fixed offset are always available and predictable: + QTest::newRow("epoch") << epochDate() << UTC << early << late << Clean; + + // First and last days in QDateTime's supported range: + QTest::newRow("earliest") + << dateAtMillis(Bound::min()) << UTC << invalid << late << Clean; + QTest::newRow("latest") + << dateAtMillis(Bound::max()) << UTC << early << invalid << Clean; + + const struct { + const char *test; + const char *zone; + const QDate day; + const QTime start; + const QTime end; + const BackendKludges msOpt; + } transitions[] = { + // The western Mexico time-zones skipped the first hour of 1970. + { "BajaMexico", "America/Hermosillo", QDate(1970, 1, 1), QTime(1, 0), late, MsNoStart }, + + // Compare tst_QDateTime::fromStringDateFormat(ISO 24:00 in DST). + { "Brazil", "America/Sao_Paulo", QDate(2008, 10, 19), QTime(1, 0), late, Clean }, + + // Several southern zones within EET (but not the northern ones) spent + // part of the 1990s using midnight as spring transition. + { "Sofia", "Europe/Sofia", QDate(1994, 3, 27), QTime(1, 0), late, MsNoStart }, + + // Two Pacific zones skipped days to get on the west of the + // International Date Line; those days have neither start nor end. + { "Kiritimati", "Pacific/Kiritimati", QDate(1994, 12, 31), invalid, invalid, IgnoreBoth }, + { "Samoa", "Pacific/Apia", QDate(2011, 12, 30), invalid, invalid, MsNoBoth }, + + // TODO: find other zones with transitions at/crossing midnight. + }; + const QTimeZone local = QTimeZone::LocalTime; + +#if QT_CONFIG(timezone) + const QTimeZone sys = QTimeZone::systemTimeZone(); + QVERIFY2(sys.isValid(), "Test depends on properly configured system"); + for (const auto &tran : transitions) { + if (QTimeZone zone(tran.zone); zone.isValid()) { + QTest::newRow(tran.test) + << tran.day << zone << tran.start << tran.end << tran.msOpt; + if (zone == sys) { + QTest::addRow("Local=%s", tran.test) + << tran.day << local << tran.start << tran.end << tran.msOpt; + } + } } - if (QTimeZone("Pacific/Apia").isValid()) { - QTest::newRow("Samoa") - << QDate(2011, 12, 30) << QByteArray("Pacific/Apia") - << invalid << invalid; +#else + const auto isLocalZone = [](const char *zone) { + const QLatin1StringView name(zone); + for (int i = 0; i < 2; ++i) { + if (qTzName(i) == name) + return true; + } + return false; + }; + for (const auto &tran : transitions) { + if (isLocalZone(tran.zone)) { // Might need a different name to match + QTest::addRow("Local=%s", tran.test) + << tran.day << local << tran.start << tran.end << tran.msOpt; + } } - // TODO: find other zones with transitions at/crossing midnight. +#endif // timezone } void tst_QDate::startOfDay_endOfDay() { - QFETCH(QDate, date); - QFETCH(QByteArray, zoneName); - QFETCH(QTime, start); - QFETCH(QTime, end); - const QTimeZone zone(zoneName); + QFETCH(const QDate, date); + QFETCH(const QTimeZone, zone); + QFETCH(const QTime, start); + QFETCH(const QTime, end); +#if defined(USING_WIN_TZ) || defined(Q_OS_ANDROID) // Coping with backend limitations. + QFETCH(const BackendKludges, kludge); +#define UNLESSKLUDGE(flag) if (!kludge.testFlag(flag)) +#else +#define UNLESSKLUDGE(flag) +#endif QVERIFY(zone.isValid()); - const bool isSystem = QTimeZone::systemTimeZone() == zone; + QDateTime front(date.startOfDay(zone)), back(date.endOfDay(zone)); if (end.isValid()) QCOMPARE(date.addDays(1).startOfDay(zone).addMSecs(-1), back); if (start.isValid()) QCOMPARE(date.addDays(-1).endOfDay(zone).addMSecs(1), front); - do { // Avoids duplicating these tests for local-time when it *is* zone: - if (start.isValid()) { - QCOMPARE(front.date(), date); - QCOMPARE(front.time(), start); - } - if (end.isValid()) { - QCOMPARE(back.date(), date); - QCOMPARE(back.time(), end); - } - if (front.timeSpec() == Qt::LocalTime) - break; - front = date.startOfDay(Qt::LocalTime); - back = date.endOfDay(Qt::LocalTime); - } while (isSystem); - if (end.isValid()) - QCOMPARE(date.addDays(1).startOfDay(Qt::LocalTime).addMSecs(-1), back); - if (start.isValid()) - QCOMPARE(date.addDays(-1).endOfDay(Qt::LocalTime).addMSecs(1), front); - if (!isSystem) { - // These might fail if system zone coincides with zone; but only if it - // did something similarly unusual on the date picked for this test. - if (start.isValid()) { - QCOMPARE(front.date(), date); - QCOMPARE(front.time(), QTime(0, 0)); - } - if (end.isValid()) { - QCOMPARE(back.date(), date); - QCOMPARE(back.time(), QTime(23, 59, 59, 999)); - } + + if (start.isValid()) { + QVERIFY(front.isValid()); + QCOMPARE(front.date(), date); + UNLESSKLUDGE(IgnoreStart) QCOMPARE(front.time(), start); + } else UNLESSKLUDGE(IgnoreStart) { + auto report = qScopeGuard([front]() { qDebug() << "Start of day:" << front; }); + QVERIFY(!front.isValid()); + report.dismiss(); + } + if (end.isValid()) { + QVERIFY(back.isValid()); + QCOMPARE(back.date(), date); + UNLESSKLUDGE(IgnoreEnd) QCOMPARE(back.time(), end); + } else UNLESSKLUDGE(IgnoreEnd) { + auto report = qScopeGuard([back]() { qDebug() << "End of day:" << back; }); + QVERIFY(!back.isValid()); + report.dismiss(); } +#undef UNLESSKLUDGE } -#endif // timezone void tst_QDate::startOfDay_endOfDay_fixed_data() { + QTest::addColumn<QDate>("date"); + const qint64 kilo(1000); using Bounds = std::numeric_limits<qint64>; - const QDateTime - first(QDateTime::fromMSecsSinceEpoch(Bounds::min() + 1, Qt::UTC)), - start32sign(QDateTime::fromMSecsSinceEpoch(-0x80000000L * kilo, Qt::UTC)), - end32sign(QDateTime::fromMSecsSinceEpoch(0x80000000L * kilo, Qt::UTC)), - end32unsign(QDateTime::fromMSecsSinceEpoch(0x100000000L * kilo, Qt::UTC)), - last(QDateTime::fromMSecsSinceEpoch(Bounds::max(), Qt::UTC)); - - const struct { - const char *name; - QDate date; - } data[] = { - { "epoch", QDate(1970, 1, 1) }, - { "y2k-leap-day", QDate(2000, 2, 29) }, - // Just outside the start and end of 32-bit time_t: - { "pre-sign32", QDate(start32sign.date().year(), 1, 1) }, - { "post-sign32", QDate(end32sign.date().year(), 12, 31) }, - { "post-uint32", QDate(end32unsign.date().year(), 12, 31) }, - // Just inside the start and end of QDateTime's range: - { "first-full", first.date().addDays(1) }, - { "last-full", last.date().addDays(-1) } - }; - - QTest::addColumn<QDate>("date"); - for (const auto &r : data) - QTest::newRow(r.name) << r.date; + const auto UTC = QTimeZone::UTC; + const QDateTime first(QDateTime::fromMSecsSinceEpoch(Bounds::min() + 1, UTC)); + const QDateTime start32sign(QDateTime::fromMSecsSinceEpoch(Q_INT64_C(-0x80000000) * kilo, UTC)); + const QDateTime end32sign(QDateTime::fromMSecsSinceEpoch(Q_INT64_C(0x80000000) * kilo, UTC)); + const QDateTime end32unsign(QDateTime::fromMSecsSinceEpoch(Q_INT64_C(0x100000000) * kilo, UTC)); + const QDateTime last(QDateTime::fromMSecsSinceEpoch(Bounds::max(), UTC)); + + QTest::newRow("epoch") << epochDate(); + QTest::newRow("y2k-leap-day") << QDate(2000, 2, 29); + QTest::newRow("start-1900") << QDate(1900, 1, 1); // QTBUG-99747 + // Just outside the start and end of 32-bit time_t: + QTest::newRow("pre-sign32") << QDate(start32sign.date().year(), 1, 1); + QTest::newRow("post-sign32") << QDate(end32sign.date().year(), 12, 31); + QTest::newRow("post-uint32") << QDate(end32unsign.date().year(), 12, 31); + // Just inside the start and end of QDateTime's range: + QTest::newRow("first-full") << first.date().addDays(1); + QTest::newRow("last-full") << last.date().addDays(-1); } void tst_QDate::startOfDay_endOfDay_fixed() @@ -587,56 +675,79 @@ void tst_QDate::startOfDay_endOfDay_fixed() const QTime early(0, 0), late(23, 59, 59, 999); QFETCH(QDate, date); - QDateTime start(date.startOfDay(Qt::UTC)); - QDateTime end(date.endOfDay(Qt::UTC)); + QDateTime start(date.startOfDay(QTimeZone::UTC)); + QDateTime end(date.endOfDay(QTimeZone::UTC)); QCOMPARE(start.date(), date); QCOMPARE(end.date(), date); QCOMPARE(start.time(), early); QCOMPARE(end.time(), late); - QCOMPARE(date.addDays(1).startOfDay(Qt::UTC).addMSecs(-1), end); - QCOMPARE(date.addDays(-1).endOfDay(Qt::UTC).addMSecs(1), start); + QCOMPARE(date.addDays(1).startOfDay(QTimeZone::UTC).addMSecs(-1), end); + QCOMPARE(date.addDays(-1).endOfDay(QTimeZone::UTC).addMSecs(1), start); for (int offset = -60 * 16; offset <= 60 * 16; offset += 65) { - start = date.startOfDay(Qt::OffsetFromUTC, offset); - end = date.endOfDay(Qt::OffsetFromUTC, offset); + const auto zone = QTimeZone::fromSecondsAheadOfUtc(offset); + start = date.startOfDay(zone); + end = date.endOfDay(zone); QCOMPARE(start.date(), date); QCOMPARE(end.date(), date); QCOMPARE(start.time(), early); QCOMPARE(end.time(), late); - QCOMPARE(date.addDays(1).startOfDay(Qt::OffsetFromUTC, offset).addMSecs(-1), end); - QCOMPARE(date.addDays(-1).endOfDay(Qt::OffsetFromUTC, offset).addMSecs(1), start); + QCOMPARE(date.addDays(1).startOfDay(zone).addMSecs(-1), end); + QCOMPARE(date.addDays(-1).endOfDay(zone).addMSecs(1), start); + } + + // Minimal testing for LocalTime and TimeZone + QCOMPARE(date.startOfDay().date(), date); + QCOMPARE(date.endOfDay().date(), date); +#if QT_CONFIG(timezone) + const QTimeZone cet("Europe/Oslo"); + if (cet.isValid()) { + QCOMPARE(date.startOfDay(cet).date(), date); + QCOMPARE(date.endOfDay(cet).date(), date); } +#endif } void tst_QDate::startOfDay_endOfDay_bounds() { // Check the days in which QDateTime's range starts and ends: using Bounds = std::numeric_limits<qint64>; + const auto UTC = QTimeZone::UTC; const QDateTime - first(QDateTime::fromMSecsSinceEpoch(Bounds::min(), Qt::UTC)), - last(QDateTime::fromMSecsSinceEpoch(Bounds::max(), Qt::UTC)), - epoch(QDateTime::fromMSecsSinceEpoch(0, Qt::UTC)); + first(QDateTime::fromMSecsSinceEpoch(Bounds::min(), UTC)), + last(QDateTime::fromMSecsSinceEpoch(Bounds::max(), UTC)), + epoch(QDateTime::fromMSecsSinceEpoch(0, UTC)); // First, check these *are* the start and end of QDateTime's range: QVERIFY(first.isValid()); QVERIFY(last.isValid()); QVERIFY(first < epoch); QVERIFY(last > epoch); - // QDateTime's addMSecs doesn't check against {und,ov}erflow ... QVERIFY(!first.addMSecs(-1).isValid() || first.addMSecs(-1) > first); QVERIFY(!last.addMSecs(1).isValid() || last.addMSecs(1) < last); // Now test start/end methods with them: - QCOMPARE(first.date().endOfDay(Qt::UTC).time(), QTime(23, 59, 59, 999)); - QCOMPARE(last.date().startOfDay(Qt::UTC).time(), QTime(0, 0)); - QVERIFY(!first.date().startOfDay(Qt::UTC).isValid()); - QVERIFY(!last.date().endOfDay(Qt::UTC).isValid()); + QCOMPARE(first.date().endOfDay(UTC).time(), QTime(23, 59, 59, 999)); + QCOMPARE(last.date().startOfDay(UTC).time(), QTime(0, 0)); + QVERIFY(!first.date().startOfDay(UTC).isValid()); + QVERIFY(!last.date().endOfDay(UTC).isValid()); + + // Test for QTBUG-100873, shouldn't assert: + const QDate qdteMin(1752, 9, 14); // Used by QDateTimeEdit + QCOMPARE(qdteMin.startOfDay(UTC).date(), qdteMin); + QCOMPARE(qdteMin.startOfDay().date(), qdteMin); +#if QT_CONFIG(timezone) + const QTimeZone sys = QTimeZone::systemTimeZone(); + QVERIFY2(sys.isValid(), "Test depends on properly configured system"); + QCOMPARE(qdteMin.startOfDay(sys).date(), qdteMin); + QTimeZone berlin("Europe/Berlin"); + if (berlin.isValid()) + QCOMPARE(qdteMin.startOfDay(berlin).date(), qdteMin); +#endif } void tst_QDate::julianDaysLimits() { qint64 min = std::numeric_limits<qint64>::min(); qint64 max = std::numeric_limits<qint64>::max(); - qint64 minJd = Q_INT64_C(-784350574879); - qint64 maxJd = Q_INT64_C( 784354017364); QDate maxDate = QDate::fromJulianDay(maxJd); QDate minDate = QDate::fromJulianDay(minJd); @@ -704,11 +815,19 @@ void tst_QDate::addDays() QFETCH( int, expectedDay ); QDate dt( year, month, day ); - dt = dt.addDays( amountToAdd ); + QDate dt2 = dt.addDays( amountToAdd ); - QCOMPARE( dt.year(), expectedYear ); - QCOMPARE( dt.month(), expectedMonth ); - QCOMPARE( dt.day(), expectedDay ); + QCOMPARE( dt2.year(), expectedYear ); + QCOMPARE( dt2.month(), expectedMonth ); + QCOMPARE( dt2.day(), expectedDay ); + +#if __cpp_lib_chrono >= 201907L + QDate dt3 = dt.addDuration( std::chrono::days( amountToAdd ) ); + + QCOMPARE( dt3.year(), expectedYear ); + QCOMPARE( dt3.month(), expectedMonth ); + QCOMPARE( dt3.day(), expectedDay ); +#endif } void tst_QDate::addDays_data() @@ -851,9 +970,6 @@ void tst_QDate::addYears_data() void tst_QDate::daysTo() { - qint64 minJd = Q_INT64_C(-784350574879); - qint64 maxJd = Q_INT64_C( 784354017364); - QDate dt1(2000, 1, 1); QDate dt2(2000, 1, 5); QCOMPARE(dt1.daysTo(dt2), (qint64) 4); @@ -878,6 +994,17 @@ void tst_QDate::daysTo() QCOMPARE(zeroDate.daysTo(minDate), minJd); } +void tst_QDate::orderingCompiles() +{ + QTestPrivate::testAllComparisonOperatorsCompile<QDate>(); +#if __cpp_lib_chrono >= 201907L + QTestPrivate::testAllComparisonOperatorsCompile<QDate, std::chrono::year_month_day>(); + QTestPrivate::testAllComparisonOperatorsCompile<QDate, std::chrono::year_month_day_last>(); + QTestPrivate::testAllComparisonOperatorsCompile<QDate, std::chrono::year_month_weekday>(); + QTestPrivate::testAllComparisonOperatorsCompile<QDate, std::chrono::year_month_weekday_last>(); +#endif +} + void tst_QDate::operator_eq_eq_data() { QTest::addColumn<QDate>("d1"); @@ -914,137 +1041,83 @@ void tst_QDate::operator_eq_eq() QFETCH(QDate, d2); QFETCH(bool, expectEqual); - bool equal = d1 == d2; - QCOMPARE(equal, expectEqual); - bool notEqual = d1 != d2; - QCOMPARE(notEqual, !expectEqual); + QT_TEST_EQUALITY_OPS(d1, d2, expectEqual); - if (equal) + if (expectEqual) QVERIFY(qHash(d1) == qHash(d2)); } -void tst_QDate::operator_lt() +void tst_QDate::ordering_data() { - QDate d1(2000,1,2); - QDate d2(2000,1,2); - QVERIFY( !(d1 < d2) ); - - d1 = QDate(2001,12,4); - d2 = QDate(2001,12,5); - QVERIFY( d1 < d2 ); - - d1 = QDate(2001,11,5); - d2 = QDate(2001,12,5); - QVERIFY( d1 < d2 ); - - d1 = QDate(2000,12,5); - d2 = QDate(2001,12,5); - QVERIFY( d1 < d2 ); - - d1 = QDate(2002,12,5); - d2 = QDate(2001,12,5); - QVERIFY( !(d1 < d2) ); - - d1 = QDate(2001,12,5); - d2 = QDate(2001,11,5); - QVERIFY( !(d1 < d2) ); - - d1 = QDate(2001,12,6); - d2 = QDate(2001,12,5); - QVERIFY( !(d1 < d2) ); -} - -void tst_QDate::operator_gt() -{ - QDate d1(2000,1,2); - QDate d2(2000,1,2); - QVERIFY( !(d1 > d2) ); - - d1 = QDate(2001,12,4); - d2 = QDate(2001,12,5); - QVERIFY( !(d1 > d2) ); - - d1 = QDate(2001,11,5); - d2 = QDate(2001,12,5); - QVERIFY( !(d1 > d2) ); - - d1 = QDate(2000,12,5); - d2 = QDate(2001,12,5); - QVERIFY( !(d1 > d2) ); - - d1 = QDate(2002,12,5); - d2 = QDate(2001,12,5); - QVERIFY( d1 > d2 ); - - d1 = QDate(2001,12,5); - d2 = QDate(2001,11,5); - QVERIFY( d1 > d2 ); - - d1 = QDate(2001,12,6); - d2 = QDate(2001,12,5); - QVERIFY( d1 > d2 ); + QTest::addColumn<QDate>("left"); + QTest::addColumn<QDate>("right"); + QTest::addColumn<Qt::strong_ordering>("expectedOrdering"); + + QTest::newRow("2000-1-2_vs_2000-1-2") + << QDate(2000, 1, 2) << QDate(2000, 1, 2) << Qt::strong_ordering::equivalent; + QTest::newRow("2001-12-4_vs_2001-12-5") + << QDate(2001, 12, 4) << QDate(2001, 12, 5) << Qt::strong_ordering::less; + QTest::newRow("2001-11-5_vs_2001-12-5") + << QDate(2001, 11, 5) << QDate(2001, 12, 5) << Qt::strong_ordering::less; + QTest::newRow("2000-12-5_vs_2001-12-5") + << QDate(2000, 12, 5) << QDate(2001, 12, 5) << Qt::strong_ordering::less; + QTest::newRow("2002-12-5_vs_2001-12-5") + << QDate(2002, 12, 5) << QDate(2001, 12, 5) << Qt::strong_ordering::greater; + QTest::newRow("2001-12-5_vs_2001-11-5") + << QDate(2001, 12, 5) << QDate(2001, 11, 5) << Qt::strong_ordering::greater; + QTest::newRow("2001-12-6_vs_2001-12-5") + << QDate(2001, 12, 6) << QDate(2001, 12, 5) << Qt::strong_ordering::greater; } -void tst_QDate::operator_lt_eq() +void tst_QDate::ordering() { - QDate d1(2000,1,2); - QDate d2(2000,1,2); - QVERIFY( d1 <= d2 ); - - d1 = QDate(2001,12,4); - d2 = QDate(2001,12,5); - QVERIFY( d1 <= d2 ); - - d1 = QDate(2001,11,5); - d2 = QDate(2001,12,5); - QVERIFY( d1 <= d2 ); - - d1 = QDate(2000,12,5); - d2 = QDate(2001,12,5); - QVERIFY( d1 <= d2 ); - - d1 = QDate(2002,12,5); - d2 = QDate(2001,12,5); - QVERIFY( !(d1 <= d2) ); + QFETCH(QDate, left); + QFETCH(QDate, right); + QFETCH(Qt::strong_ordering, expectedOrdering); - d1 = QDate(2001,12,5); - d2 = QDate(2001,11,5); - QVERIFY( !(d1 <= d2) ); - - d1 = QDate(2001,12,6); - d2 = QDate(2001,12,5); - QVERIFY( !(d1 <= d2) ); + QT_TEST_ALL_COMPARISON_OPS(left, right, expectedOrdering); } -void tst_QDate::operator_gt_eq() +void tst_QDate::ordering_chrono_types() { - QDate d1(2000,1,2); - QDate d2(2000,1,2); - QVERIFY( d1 >= d2 ); - - d1 = QDate(2001,12,4); - d2 = QDate(2001,12,5); - QVERIFY( !(d1 >= d2) ); - - d1 = QDate(2001,11,5); - d2 = QDate(2001,12,5); - QVERIFY( !(d1 >= d2) ); - - d1 = QDate(2000,12,5); - d2 = QDate(2001,12,5); - QVERIFY( !(d1 >= d2) ); - - d1 = QDate(2002,12,5); - d2 = QDate(2001,12,5); - QVERIFY( d1 >= d2 ); - - d1 = QDate(2001,12,5); - d2 = QDate(2001,11,5); - QVERIFY( d1 >= d2 ); - - d1 = QDate(2001,12,6); - d2 = QDate(2001,12,5); - QVERIFY( d1 >= d2 ); +#if __cpp_lib_chrono >= 201907L + using namespace std::chrono; + QDate friday(2001, 11, 30); // the 5th Friday of November 2001 + // std::chrono::year_month_day + QT_TEST_ALL_COMPARISON_OPS(friday, year_month_day(2001y, November, 29d), + Qt::strong_ordering::greater); + QT_TEST_ALL_COMPARISON_OPS(friday, year_month_day(2001y, November, 30d), + Qt::strong_ordering::equivalent); + QT_TEST_ALL_COMPARISON_OPS(friday, year_month_day(2001y, December, 1d), + Qt::strong_ordering::less); + + // std::chrono::year_month_day_last + QT_TEST_ALL_COMPARISON_OPS(friday, year_month_day_last(2001y, {October / last}), + Qt::strong_ordering::greater); + QT_TEST_ALL_COMPARISON_OPS(friday, year_month_day_last(2001y, {November / last}), + Qt::strong_ordering::equivalent); + QT_TEST_ALL_COMPARISON_OPS(friday, year_month_day_last(2001y, {December / last}), + Qt::strong_ordering::less); + + // std::chrono::year_month_weekday + QT_TEST_ALL_COMPARISON_OPS(friday, year_month_weekday(2001y, November, Thursday[5]), + Qt::strong_ordering::greater); + QT_TEST_ALL_COMPARISON_OPS(friday, year_month_weekday(2001y, November, Friday[5]), + Qt::strong_ordering::equivalent); + QT_TEST_ALL_COMPARISON_OPS(friday, year_month_weekday(2001y, December, Saturday[1]), + Qt::strong_ordering::less); + + // std::chrono::year_month_weekday_last + QDate thursday(2001, 11, 29); // the last Thursday of November 2001 + QT_TEST_ALL_COMPARISON_OPS(thursday, year_month_weekday_last(2001y, November, Wednesday[last]), + Qt::strong_ordering::greater); + QT_TEST_ALL_COMPARISON_OPS(thursday, year_month_weekday_last(2001y, November, Thursday[last]), + Qt::strong_ordering::equivalent); + QT_TEST_ALL_COMPARISON_OPS(thursday, year_month_weekday_last(2001y, November, Friday[last]), + Qt::strong_ordering::less); +#else + QSKIP("This test requires C++20-level <chrono> support enabled in the standard library."); +#endif // __cpp_lib_chrono >= 201907L } Q_DECLARE_METATYPE(QDataStream::Version) @@ -1106,7 +1179,7 @@ void tst_QDate::operator_insert_extract() QCOMPARE(deserialised, date); } -#if QT_CONFIG(datetimeparser) +#if QT_CONFIG(datestring) void tst_QDate::fromStringDateFormat_data() { QTest::addColumn<QString>("dateStr"); @@ -1148,13 +1221,13 @@ void tst_QDate::fromStringDateFormat_data() << QString::fromLatin1(" 13 Feb 1987 13:24:51 +0100") << Qt::RFC2822Date << QDate(1987, 2, 13); QTest::newRow("RFC 2822 with day") << QString::fromLatin1("Thu, 01 Jan 1970 00:12:34 +0000") - << Qt::RFC2822Date << QDate(1970, 1, 1); + << Qt::RFC2822Date << epochDate(); QTest::newRow("RFC 2822 with day after space") << QString::fromLatin1(" Thu, 01 Jan 1970 00:12:34 +0000") - << Qt::RFC2822Date << QDate(1970, 1, 1); + << Qt::RFC2822Date << epochDate(); // No timezone QTest::newRow("RFC 2822 no timezone") << QString::fromLatin1("01 Jan 1970 00:12:34") - << Qt::RFC2822Date << QDate(1970, 1, 1); + << Qt::RFC2822Date << epochDate(); // No time specified QTest::newRow("RFC 2822 date only") << QString::fromLatin1("01 Nov 2002") << Qt::RFC2822Date << QDate(2002, 11, 1); @@ -1193,7 +1266,7 @@ void tst_QDate::fromStringDateFormat_data() << Qt::RFC2822Date << QDate(1987, 2, 13); // No timezone QTest::newRow("RFC 850 and 1036 no timezone") << QString::fromLatin1("Thu Jan 01 00:12:34 1970") - << Qt::RFC2822Date << QDate(1970, 1, 1); + << Qt::RFC2822Date << epochDate(); // No time specified QTest::newRow("RFC 850 and 1036 date only") << QString::fromLatin1("Fri Nov 01 2002") << Qt::RFC2822Date << QDate(2002, 11, 1); @@ -1230,156 +1303,177 @@ void tst_QDate::fromStringDateFormat() QCOMPARE(QDate::fromString(dateStr, dateFormat), expectedDate); } +# if QT_CONFIG(datetimeparser) void tst_QDate::fromStringFormat_data() { QTest::addColumn<QString>("string"); QTest::addColumn<QString>("format"); + QTest::addColumn<int>("baseYear"); QTest::addColumn<QDate>("expected"); - // Get names: - const QString january = QStringLiteral("January"); - const QString february = QStringLiteral("February"); - const QString march = QStringLiteral("March"); - const QString august = QStringLiteral("August"); - const QString mon = QStringLiteral("Mon"); - const QString monday = QStringLiteral("Monday"); - const QString tuesday = QStringLiteral("Tuesday"); - const QString wednesday = QStringLiteral("Wednesday"); - const QString thursday = QStringLiteral("Thursday"); - const QString friday = QStringLiteral("Friday"); - const QString saturday = QStringLiteral("Saturday"); - const QString sunday = QStringLiteral("Sunday"); - - QTest::newRow("data0") << QString("") << QString("") << defDate(); - QTest::newRow("data1") << QString(" ") << QString("") << invalidDate(); - QTest::newRow("data2") << QString(" ") << QString(" ") << defDate(); - QTest::newRow("data3") << QString("-%$%#") << QString("$*(#@") << invalidDate(); - QTest::newRow("data4") << QString("d") << QString("'d'") << defDate(); - QTest::newRow("data5") << QString("101010") << QString("dMyy") << QDate(1910, 10, 10); - QTest::newRow("data6") << QString("101010b") << QString("dMyy") << invalidDate(); - QTest::newRow("data7") << january << QString("MMMM") << defDate(); - QTest::newRow("data8") << QString("ball") << QString("balle") << invalidDate(); - QTest::newRow("data9") << QString("balleh") << QString("balleh") << defDate(); - QTest::newRow("data10") << QString("10.01.1") << QString("M.dd.d") << QDate(defDate().year(), 10, 1); - QTest::newRow("data11") << QString("-1.01.1") << QString("M.dd.d") << invalidDate(); - QTest::newRow("data12") << QString("11010") << QString("dMMyy") << invalidDate(); - QTest::newRow("data13") << QString("-2") << QString("d") << invalidDate(); - QTest::newRow("data14") << QString("132") << QString("Md") << invalidDate(); - QTest::newRow("data15") << february << QString("MMMM") << QDate(defDate().year(), 2, 1); - - QString date = mon + QLatin1Char(' ') + august + " 8 2005"; - QTest::newRow("data16") << date << QString("ddd MMMM d yyyy") << QDate(2005, 8, 8); - QTest::newRow("data17") << QString("2000:00") << QString("yyyy:yy") << QDate(2000, 1, 1); - QTest::newRow("data18") << QString("1999:99") << QString("yyyy:yy") << QDate(1999, 1, 1); - QTest::newRow("data19") << QString("2099:99") << QString("yyyy:yy") << QDate(2099, 1, 1); - QTest::newRow("data20") << QString("2001:01") << QString("yyyy:yy") << QDate(2001, 1, 1); - QTest::newRow("data21") << QString("99") << QString("yy") << QDate(1999, 1, 1); - QTest::newRow("data22") << QString("01") << QString("yy") << QDate(1901, 1, 1); - - QTest::newRow("data23") << monday << QString("dddd") << QDate(1900, 1, 1); - QTest::newRow("data24") << tuesday << QString("dddd") << QDate(1900, 1, 2); - QTest::newRow("data25") << wednesday << QString("dddd") << QDate(1900, 1, 3); - QTest::newRow("data26") << thursday << QString("dddd") << QDate(1900, 1, 4); - QTest::newRow("data27") << friday << QString("dddd") << QDate(1900, 1, 5); - QTest::newRow("data28") << saturday << QString("dddd") << QDate(1900, 1, 6); - QTest::newRow("data29") << sunday << QString("dddd") << QDate(1900, 1, 7); - - QTest::newRow("data30") << monday + " 2006" << QString("dddd yyyy") << QDate(2006, 1, 2); - QTest::newRow("data31") << tuesday + " 2006" << QString("dddd yyyy") << QDate(2006, 1, 3); - QTest::newRow("data32") << wednesday + " 2006" << QString("dddd yyyy") << QDate(2006, 1, 4); - QTest::newRow("data33") << thursday + " 2006" << QString("dddd yyyy") << QDate(2006, 1, 5); - QTest::newRow("data34") << friday + " 2006" << QString("dddd yyyy") << QDate(2006, 1, 6); - QTest::newRow("data35") << saturday + " 2006" << QString("dddd yyyy") << QDate(2006, 1, 7); - QTest::newRow("data36") << sunday + " 2006" << QString("dddd yyyy") << QDate(2006, 1, 1); - - QTest::newRow("data37") << tuesday + " 2007 " + march << QString("dddd yyyy MMMM") << QDate(2007, 3, 6); - - QTest::newRow("data38") << QString("21052006") << QString("ddMMyyyy") << QDate(2006,5,21); - QTest::newRow("data39") << QString("210506") << QString("ddMMyy") << QDate(1906,5,21); - QTest::newRow("data40") << QString("21/5/2006") << QString("d/M/yyyy") << QDate(2006,5,21); - QTest::newRow("data41") << QString("21/5/06") << QString("d/M/yy") << QDate(1906,5,21); - QTest::newRow("data42") << QString("20060521") << QString("yyyyMMdd") << QDate(2006,5,21); - QTest::newRow("data43") << QString("060521") << QString("yyMMdd") << QDate(1906,5,21); - QTest::newRow("lateMarch") << QString("9999-03-06") << QString("yyyy-MM-dd") << QDate(9999, 3, 6); - QTest::newRow("late") << QString("9999-12-31") << QString("yyyy-MM-dd") << QDate(9999, 12, 31); + QTest::newRow("empty") << u""_s << u""_s << 1900 << defDate(); + QTest::newRow("space-as-empty") << u" "_s << u""_s << 1900 << invalidDate(); + QTest::newRow("space") << u" "_s << u" "_s << 1900 << defDate(); + QTest::newRow("mispunc") << u"-%$%#"_s << u"$*(#@"_s << 1900 << invalidDate(); + QTest::newRow("literal-d") << u"d"_s << u"'d'"_s << 1900 << defDate(); + QTest::newRow("greedy") << u"101010"_s << u"dMyy"_s << 1900 << QDate(1910, 10, 10); + QTest::newRow("greedy-miss") << u"101010b"_s << u"dMyy"_s << 1900 << invalidDate(); + QTest::newRow("January") << u"January"_s << u"MMMM"_s << 1900 << defDate(); + QTest::newRow("mistext") << u"ball"_s << u"balle"_s << 1900 << invalidDate(); + QTest::newRow("text") << u"balleh"_s << u"balleh"_s << 1900 << defDate(); + QTest::newRow("yearless:19") << u"10.01.1"_s << u"M.dd.d"_s << 1900 << QDate(1900, 10, 1); + QTest::newRow("yearless:20") << u"10.01.1"_s << u"M.dd.d"_s << 2000 << QDate(2000, 10, 1); + QTest::newRow("neg-month") << u"-1.01.1"_s << u"M.dd.d"_s << 1900 << invalidDate(); + QTest::newRow("greedy-break") << u"11010"_s << u"dMMyy"_s << 1900 << invalidDate(); + QTest::newRow("neg-day") << u"-2"_s << u"d"_s << 1900 << invalidDate(); + QTest::newRow("Md:132") << u"132"_s << u"Md"_s << 1900 << invalidDate(); + QTest::newRow("February") << u"February"_s << u"MMMM"_s << 1900 << QDate(1900, 2, 1); + + QTest::newRow("mon-aug-8th") + << u"Mon August 8 2005"_s << u"ddd MMMM d yyyy"_s << 1900 << QDate(2005, 8, 8); + QTest::newRow("year-match-20000") << u"2000:00"_s << u"yyyy:yy"_s << 1900 << QDate(2000, 1, 1); + QTest::newRow("year-match-1999") << u"1999:99"_s << u"yyyy:yy"_s << 1900 << QDate(1999, 1, 1); + QTest::newRow("year-match-2099") << u"2099:99"_s << u"yyyy:yy"_s << 1900 << QDate(2099, 1, 1); + QTest::newRow("year-match-2001") << u"2001:01"_s << u"yyyy:yy"_s << 1900 << QDate(2001, 1, 1); + QTest::newRow("just-yy-1999") << u"99"_s << u"yy"_s << 1900 << QDate(1999, 1, 1); + QTest::newRow("just-yy-1901") << u"01"_s << u"yy"_s << 1900 << QDate(1901, 1, 1); + QTest::newRow("just-yy-2001") << u"01"_s << u"yy"_s << 1970 << QDate(2001, 1, 1); + + QTest::newRow("Monday") << u"Monday"_s << u"dddd"_s << 1900 << QDate(1900, 1, 1); + QTest::newRow("Tuesday") << u"Tuesday"_s << u"dddd"_s << 1900 << QDate(1900, 1, 2); + QTest::newRow("Wednesday") << u"Wednesday"_s << u"dddd"_s << 1900 << QDate(1900, 1, 3); + QTest::newRow("Thursday") << u"Thursday"_s << u"dddd"_s << 1900 << QDate(1900, 1, 4); + QTest::newRow("Friday") << u"Friday"_s << u"dddd"_s << 1900 << QDate(1900, 1, 5); + QTest::newRow("Saturday") << u"Saturday"_s << u"dddd"_s << 1900 << QDate(1900, 1, 6); + QTest::newRow("Sunday") << u"Sunday"_s << u"dddd"_s << 1900 << QDate(1900, 1, 7); + + QTest::newRow("Mon06") << u"Monday 2006"_s << u"dddd yyyy"_s << 1900 << QDate(2006, 1, 2); + QTest::newRow("Tues06") << u"Tuesday 2006"_s << u"dddd yyyy"_s << 1900 << QDate(2006, 1, 3); + QTest::newRow("Wed06") << u"Wednesday 2006"_s << u"dddd yyyy"_s << 1900 << QDate(2006, 1, 4); + QTest::newRow("Thu06") << u"Thursday 2006"_s << u"dddd yyyy"_s << 1900 << QDate(2006, 1, 5); + QTest::newRow("Fri06") << u"Friday 2006"_s << u"dddd yyyy"_s << 1900 << QDate(2006, 1, 6); + QTest::newRow("Sat06") << u"Saturday 2006"_s << u"dddd yyyy"_s << 1900 << QDate(2006, 1, 7); + QTest::newRow("Sun06") << u"Sunday 2006"_s << u"dddd yyyy"_s << 1900 << QDate(2006, 1, 1); + QTest::newRow("Tue07Mar") + << u"Tuesday 2007 March"_s << u"dddd yyyy MMMM"_s << 1900 << QDate(2007, 3, 6); + + QTest::newRow("21May2006") + << u"21052006"_s << u"ddMMyyyy"_s << 1900 << QDate(2006, 5, 21); + QTest::newRow("21May06:19") + << u"210506"_s << u"ddMMyy"_s << 1900 << QDate(1906, 5, 21); + QTest::newRow("21May06:20") + << u"210506"_s << u"ddMMyy"_s << 1970 << QDate(2006, 5, 21); + QTest::newRow("21/May/2006") + << u"21/5/2006"_s << u"d/M/yyyy"_s << 1900 << QDate(2006, 5, 21); + QTest::newRow("21/5/06") + << u"21/5/06"_s << u"d/M/yy"_s << 1900 << QDate(1906, 5, 21); + QTest::newRow("21/5/06:19") + << u"21/5/06"_s << u"d/M/yy"_s << 1900 << QDate(1906, 5, 21); + QTest::newRow("21/5/06:20") + << u"21/5/06"_s << u"d/M/yy"_s << 1910 << QDate(2006, 5, 21); + QTest::newRow("2006May21") + << u"20060521"_s << u"yyyyMMdd"_s << 1900 << QDate(2006, 5, 21); + QTest::newRow("06May21:19") + << u"060521"_s << u"yyMMdd"_s << 1900 << QDate(1906, 5, 21); + QTest::newRow("06May21:20") + << u"060521"_s << u"yyMMdd"_s << 1907 << QDate(2006, 5, 21); + QTest::newRow("lateMarch") + << u"9999-03-06"_s << u"yyyy-MM-dd"_s << 1900 << QDate(9999, 3, 6); + QTest::newRow("late") + << u"9999-12-31"_s << u"yyyy-MM-dd"_s << 1900 << QDate(9999, 12, 31); + + QTest::newRow("quoted-dd") + << u"21dd-05-2006"_s << u"dd'dd'-MM-yyyy"_s << 1900 << QDate(2006, 5, 21); + QTest::newRow("quoted-MM") + << u"21-MM05-2006"_s << u"dd-'MM'MM-yyyy"_s << 1900 << QDate(2006, 5, 21); + QTest::newRow("quotes-empty") + << u"21-'05-2006"_s << u"dd-MM-''yy"_s << 1900 << QDate(2006, 5, 21); // Test unicode handling. QTest::newRow("Unicode in format string") - << QString(u8"2020🤣09🤣21") << QString(u8"yyyy🤣MM🤣dd") << QDate(2020, 9, 21); - QTest::newRow("Unicode in quoted format string") - << QString(u8"🤣🤣2020👍09🤣21") << QString(u8"'🤣🤣'yyyy👍MM🤣dd") << QDate(2020, 9, 21); + << QString(u8"2020🤣09🤣21") << QString(u8"yyyy🤣MM🤣dd") << 1900 << QDate(2020, 9, 21); + QTest::newRow("Unicode-in-format-string-quoted-emoji") + << QString(u8"🤣🤣2020👍09🤣21") << QString(u8"'🤣🤣'yyyy👍MM🤣dd") << 1900 + << QDate(2020, 9, 21); + QTest::newRow("Unicode-in-quoted-dd-format-string") + << QString(u8"🤣🤣2020👍09🤣21dd") << QString(u8"🤣🤣yyyy👍MM🤣dd'dd'") << 1900 + << QDate(2020, 9, 21); + QTest::newRow("Unicode-in-all-formats-quoted-string") + << QString(u8"🤣🤣yyyy2020👍MM09🤣21dd") << QString(u8"🤣🤣'yyyy'yyyy👍'MM'MM🤣dd'dd'") + << 1900 << QDate(2020, 9, 21); // QTBUG-84334 QTest::newRow("-ve year: front, nosep") - << QString("-20060521") << QString("yyyyMMdd") << QDate(-2006, 5, 21); + << u"-20060521"_s << u"yyyyMMdd"_s << 1900 << QDate(-2006, 5, 21); QTest::newRow("-ve year: mid, nosep") - << QString("05-200621") << QString("MMyyyydd") << QDate(-2006, 5, 21); + << u"05-200621"_s << u"MMyyyydd"_s << 1900 << QDate(-2006, 5, 21); QTest::newRow("-ve year: back, nosep") - << QString("0521-2006") << QString("MMddyyyy") << QDate(-2006, 5, 21); + << u"0521-2006"_s << u"MMddyyyy"_s << 1900 << QDate(-2006, 5, 21); // - as separator should not interfere with negative year numbers: QTest::newRow("-ve year: front, dash") - << QString("-2006-05-21") << QString("yyyy-MM-dd") << QDate(-2006, 5, 21); + << u"-2006-05-21"_s << u"yyyy-MM-dd"_s << 1900 << QDate(-2006, 5, 21); QTest::newRow("positive year: front, dash") - << QString("-2006-05-21") << QString("-yyyy-MM-dd") << QDate(2006, 5, 21); + << u"-2006-05-21"_s << u"-yyyy-MM-dd"_s << 1900 << QDate(2006, 5, 21); QTest::newRow("-ve year: mid, dash") - << QString("05--2006-21") << QString("MM-yyyy-dd") << QDate(-2006, 5, 21); + << u"05--2006-21"_s << u"MM-yyyy-dd"_s << 1900 << QDate(-2006, 5, 21); QTest::newRow("-ve year: back, dash") - << QString("05-21--2006") << QString("MM-dd-yyyy") << QDate(-2006, 5, 21); + << u"05-21--2006"_s << u"MM-dd-yyyy"_s << 1900 << QDate(-2006, 5, 21); // negative three digit year numbers should be rejected: QTest::newRow("-ve 3digit year: front") - << QString("-206-05-21") << QString("yyyy-MM-dd") << QDate(); + << u"-206-05-21"_s << u"yyyy-MM-dd"_s << 1900 << QDate(); QTest::newRow("-ve 3digit year: mid") - << QString("05--206-21") << QString("MM-yyyy-dd") << QDate(); + << u"05--206-21"_s << u"MM-yyyy-dd"_s << 1900 << QDate(); QTest::newRow("-ve 3digit year: back") - << QString("05-21--206") << QString("MM-dd-yyyy") << QDate(); + << u"05-21--206"_s << u"MM-dd-yyyy"_s << 1900 << QDate(); // negative month numbers should be rejected: QTest::newRow("-ve 2digit month: mid") - << QString("2060--05-21") << QString("yyyy-MM-dd") << QDate(); + << u"2060--05-21"_s << u"yyyy-MM-dd"_s << 1900 << QDate(); QTest::newRow("-ve 2digit month: front") - << QString("-05-2060-21") << QString("MM-yyyy-dd") << QDate(); + << u"-05-2060-21"_s << u"MM-yyyy-dd"_s << 1900 << QDate(); QTest::newRow("-ve 2digit month: back") - << QString("21-2060--05") << QString("dd-yyyy-MM") << QDate(); + << u"21-2060--05"_s << u"dd-yyyy-MM"_s << 1900 << QDate(); // negative single digit month numbers should be rejected: QTest::newRow("-ve 1digit month: mid") - << QString("2060--5-21") << QString("yyyy-MM-dd") << QDate(); + << u"2060--5-21"_s << u"yyyy-MM-dd"_s << 1900 << QDate(); QTest::newRow("-ve 1digit month: front") - << QString("-5-2060-21") << QString("MM-yyyy-dd") << QDate(); + << u"-5-2060-21"_s << u"MM-yyyy-dd"_s << 1900 << QDate(); QTest::newRow("-ve 1digit month: back") - << QString("21-2060--5") << QString("dd-yyyy-MM") << QDate(); + << u"21-2060--5"_s << u"dd-yyyy-MM"_s << 1900 << QDate(); // negative day numbers should be rejected: QTest::newRow("-ve 2digit day: front") - << QString("-21-2060-05") << QString("dd-yyyy-MM") << QDate(); + << u"-21-2060-05"_s << u"dd-yyyy-MM"_s << 1900 << QDate(); QTest::newRow("-ve 2digit day: mid") - << QString("2060--21-05") << QString("yyyy-dd-MM") << QDate(); + << u"2060--21-05"_s << u"yyyy-dd-MM"_s << 1900 << QDate(); QTest::newRow("-ve 2digit day: back") - << QString("05-2060--21") << QString("MM-yyyy-dd") << QDate(); + << u"05-2060--21"_s << u"MM-yyyy-dd"_s << 1900 << QDate(); // negative single digit day numbers should be rejected: QTest::newRow("-ve 1digit day: front") - << QString("-2-2060-05") << QString("dd-yyyy-MM") << QDate(); + << u"-2-2060-05"_s << u"dd-yyyy-MM"_s << 1900 << QDate(); QTest::newRow("-ve 1digit day: mid") - << QString("05--2-2060") << QString("MM-dd-yyyy") << QDate(); + << u"05--2-2060"_s << u"MM-dd-yyyy"_s << 1900 << QDate(); QTest::newRow("-ve 1digit day: back") - << QString("2060-05--2") << QString("yyyy-MM-dd") << QDate(); + << u"2060-05--2"_s << u"yyyy-MM-dd"_s << 1900 << QDate(); // positive three digit year numbers should be rejected: - QTest::newRow("3digit year, front") << QString("206-05-21") << QString("yyyy-MM-dd") << QDate(); - QTest::newRow("3digit year, mid") << QString("05-206-21") << QString("MM-yyyy-dd") << QDate(); - QTest::newRow("3digit year, back") << QString("05-21-206") << QString("MM-dd-yyyy") << QDate(); + QTest::newRow("3digit year, front") << u"206-05-21"_s << u"yyyy-MM-dd"_s << 1900 << QDate(); + QTest::newRow("3digit year, mid") << u"05-206-21"_s << u"MM-yyyy-dd"_s << 1900 << QDate(); + QTest::newRow("3digit year, back") << u"05-21-206"_s << u"MM-dd-yyyy"_s << 1900 << QDate(); // positive five digit year numbers should be rejected: QTest::newRow("5digit year, front") - << QString("00206-05-21") << QString("yyyy-MM-dd") << QDate(); - QTest::newRow("5digit year, mid") << QString("05-00206-21") << QString("MM-yyyy-dd") << QDate(); + << u"00206-05-21"_s << u"yyyy-MM-dd"_s << 1900 << QDate(); + QTest::newRow("5digit year, mid") + << u"05-00206-21"_s << u"MM-yyyy-dd"_s << 1900 << QDate(); QTest::newRow("5digit year, back") - << QString("05-21-00206") << QString("MM-dd-yyyy") << QDate(); + << u"05-21-00206"_s << u"MM-dd-yyyy"_s << 1900 << QDate(); QTest::newRow("dash separator, no year at end") - << QString("05-21-") << QString("dd-MM-yyyy") << QDate(); + << u"05-21-"_s << u"dd-MM-yyyy"_s << 1900 << QDate(); QTest::newRow("slash separator, no year at end") - << QString("11/05/") << QString("d/MM/yyyy") << QDate(); + << u"11/05/"_s << u"d/MM/yyyy"_s << 1900 << QDate(); // QTBUG-84349 - QTest::newRow("+ sign in year field") << QString("+0200322") << QString("yyyyMMdd") << QDate(); - QTest::newRow("+ sign in month field") << QString("2020+322") << QString("yyyyMMdd") << QDate(); - QTest::newRow("+ sign in day field") << QString("202003+1") << QString("yyyyMMdd") << QDate(); + QTest::newRow("+ sign in year field") << u"+0200322"_s << u"yyyyMMdd"_s << 1900 << QDate(); + QTest::newRow("+ sign in month field") << u"2020+322"_s << u"yyyyMMdd"_s << 1900 << QDate(); + QTest::newRow("+ sign in day field") << u"202003+1"_s << u"yyyyMMdd"_s << 1900 << QDate(); } @@ -1387,14 +1481,16 @@ void tst_QDate::fromStringFormat() { QFETCH(QString, string); QFETCH(QString, format); + QFETCH(int, baseYear); QFETCH(QDate, expected); - QDate dt = QDate::fromString(string, format); + QDate dt = QDate::fromString(string, format, baseYear); + QEXPECT_FAIL("quotes-empty", "QTBUG-110669: doubled single-quotes in format mishandled", + Continue); QCOMPARE(dt, expected); } #endif // datetimeparser -#if QT_CONFIG(datestring) void tst_QDate::toStringFormat_data() { QTest::addColumn<QDate>("t"); @@ -1602,9 +1698,6 @@ void tst_QDate::roundtrip() const loopDate = loopDate.addDays(1); } - qint64 minJd = Q_INT64_C(-784350574879); - qint64 maxJd = Q_INT64_C( 784354017364); - // Test Gregorian round trip at top end of conversion range loopDate = QDate::fromJulianDay(maxJd); while (loopDate.toJulianDay() >= maxJd - 146397) { @@ -1620,6 +1713,38 @@ void tst_QDate::roundtrip() const QCOMPARE(loopDate.toJulianDay(), testDate.toJulianDay()); loopDate = loopDate.addDays(1); } + +#if __cpp_lib_chrono >= 201907L + // Test roundtrip for from/to std::chrono conversions. + // Compile-time test, to verify it's all constexpr. + using namespace std::chrono; + { + constexpr sys_days expected{days{minJd}}; + constexpr sys_days actual{QDate::fromStdSysDays(expected).toStdSysDays()}; + static_assert(actual == expected); + } + { + // constexpr year_month_day expected{sys_days{days{maxJd}}}; // Overflow at least on MSVC + constexpr year_month_day expected{1970y, January, 1d}; + constexpr sys_days actual{QDate(expected).toStdSysDays()}; + static_assert(actual == sys_days(expected)); + } + { + constexpr year_month_day_last expected{2001y, {October / last}}; + constexpr sys_days actual{QDate(expected).toStdSysDays()}; + static_assert(actual == sys_days(expected)); + } + { + constexpr year_month_weekday expected{2001y, December, Saturday[1]}; + constexpr sys_days actual{QDate(expected).toStdSysDays()}; + static_assert(actual == sys_days(expected)); + } + { + constexpr year_month_weekday_last expected{2001y, November, Friday[last]}; + constexpr sys_days actual{QDate(expected).toStdSysDays()}; + static_assert(actual == sys_days(expected)); + } +#endif // __cpp_lib_chrono >= 201907L } void tst_QDate::qdebug() const |