mirror of
https://github.com/crystalidea/qt6windows7.git
synced 2025-07-06 17:25:24 +08:00
qt 6.6.0 clean
This commit is contained in:
@ -368,13 +368,12 @@ void tst_QCalendar::gregory()
|
||||
// dateToJulianDay() and weekDayOfJulian():
|
||||
if (!year) // No year zero.
|
||||
continue;
|
||||
qint64 first, last;
|
||||
QVERIFY2(QGregorianCalendar::julianFromParts(year, 1, 1, &first),
|
||||
"Only year zero should lack a first day");
|
||||
const auto first = QGregorianCalendar::julianFromParts(year, 1, 1);
|
||||
QVERIFY2(first, "Only year zero should lack a first day");
|
||||
QCOMPARE(QGregorianCalendar::yearStartWeekDay(year),
|
||||
QGregorianCalendar::weekDayOfJulian(first));
|
||||
QVERIFY2(QGregorianCalendar::julianFromParts(year, 12, 31, &last),
|
||||
"Only year zero should lack a last day");
|
||||
QGregorianCalendar::weekDayOfJulian(*first));
|
||||
const auto last = QGregorianCalendar::julianFromParts(year, 12, 31);
|
||||
QVERIFY2(last, "Only year zero should lack a last day");
|
||||
|
||||
const int lastTwo = (year + (year < 0 ? 1 : 0)) % 100 + (year < -1 ? 100 : 0);
|
||||
const QDate probe(year, lastTwo && lastTwo <= 12 ? lastTwo : 8,
|
||||
@ -392,13 +391,14 @@ void tst_QCalendar::gregory()
|
||||
if (year > 0 && lastTwo > 31)
|
||||
QCOMPARE(match % 100, lastTwo);
|
||||
// Its first and last days of the year match those of year:
|
||||
qint64 day;
|
||||
QVERIFY(QGregorianCalendar::julianFromParts(match, 1, 1, &day));
|
||||
QCOMPARE(QGregorianCalendar::weekDayOfJulian(day),
|
||||
QGregorianCalendar::weekDayOfJulian(first));
|
||||
QVERIFY(QGregorianCalendar::julianFromParts(match, 12, 31, &day));
|
||||
QCOMPARE(QGregorianCalendar::weekDayOfJulian(day),
|
||||
QGregorianCalendar::weekDayOfJulian(last));
|
||||
auto day = QGregorianCalendar::julianFromParts(match, 1, 1);
|
||||
QVERIFY(day);
|
||||
QCOMPARE(QGregorianCalendar::weekDayOfJulian(*day),
|
||||
QGregorianCalendar::weekDayOfJulian(*first));
|
||||
day = QGregorianCalendar::julianFromParts(match, 12, 31);
|
||||
QVERIFY(day);
|
||||
QCOMPARE(QGregorianCalendar::weekDayOfJulian(*day),
|
||||
QGregorianCalendar::weekDayOfJulian(*last));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,16 @@
|
||||
#include <QTimeZone>
|
||||
|
||||
#include <private/qglobal_p.h> // for the icu feature test
|
||||
#include <private/qdatetime_p.h>
|
||||
#if !QT_CONFIG(timezone)
|
||||
# include <private/qtenvironmentvariables_p.h> // for qTzName()
|
||||
#endif
|
||||
|
||||
using namespace QtPrivate::DateTimeConstants;
|
||||
|
||||
#if defined(Q_OS_WIN) && !QT_CONFIG(icu)
|
||||
# define USING_WIN_TZ
|
||||
#endif
|
||||
|
||||
class tst_QDate : public QObject
|
||||
{
|
||||
@ -34,10 +44,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();
|
||||
@ -80,6 +88,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(); }
|
||||
};
|
||||
|
||||
@ -90,9 +107,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;
|
||||
@ -480,125 +494,155 @@ 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.
|
||||
*/
|
||||
enum MsKludge { IgnoreStart = 1, IgnoreEnd = 2, };
|
||||
Q_DECLARE_FLAGS(MsKludges, MsKludge)
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(MsKludges)
|
||||
|
||||
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");
|
||||
QTest::addColumn<MsKludges>("msKludge");
|
||||
|
||||
const QTime initial(0, 0), final(23, 59, 59, 999), invalid(QDateTime().time());
|
||||
const QTime early(0, 0), late(23, 59, 59, 999), invalid(QDateTime().time());
|
||||
constexpr MsKludges IgnoreBoth = IgnoreStart | IgnoreEnd;
|
||||
const QTimeZone UTC(QTimeZone::UTC);
|
||||
|
||||
// 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;
|
||||
using Bound = std::numeric_limits<qint64>;
|
||||
const auto dateAtMillis = [UTC](qint64 millis) {
|
||||
return QDateTime::fromMSecsSinceEpoch(millis, UTC).date();
|
||||
};
|
||||
|
||||
// UTC and fixed offset are always available and predictable:
|
||||
QTest::newRow("epoch") << epochDate() << UTC << early << late << MsKludges{};
|
||||
|
||||
// First and last days in QDateTime's supported range:
|
||||
QTest::newRow("earliest")
|
||||
<< dateAtMillis(Bound::min()) << UTC << invalid << late << MsKludges{};
|
||||
QTest::newRow("latest")
|
||||
<< dateAtMillis(Bound::max()) << UTC << early << invalid << MsKludges{};
|
||||
|
||||
const struct {
|
||||
const char *test;
|
||||
const char *zone;
|
||||
const QDate day;
|
||||
const QTime start;
|
||||
const QTime end;
|
||||
const MsKludges msOpt;
|
||||
} transitions[] = {
|
||||
// The western Mexico time-zones skipped the first hour of 1970.
|
||||
{ "BajaMexico", "America/Hermosillo", QDate(1970, 1, 1), QTime(1, 0), late, IgnoreStart },
|
||||
|
||||
// Compare tst_QDateTime::fromStringDateFormat(ISO 24:00 in DST).
|
||||
{ "Brazil", "America/Sao_Paulo", QDate(2008, 10, 19), QTime(1, 0), late, MsKludges{} },
|
||||
|
||||
// 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, IgnoreStart },
|
||||
|
||||
// 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, IgnoreBoth },
|
||||
|
||||
// TODO: find other zones with transitions at/crossing midnight.
|
||||
};
|
||||
const QTimeZone local = QTimeZone::LocalTime;
|
||||
|
||||
#if QT_CONFIG(timezone)
|
||||
const QTimeZone sys = QTimeZone::systemTimeZone();
|
||||
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 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;
|
||||
#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;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (QTimeZone("Pacific/Kiritimati").isValid()) {
|
||||
QTest::newRow("Kiritimati")
|
||||
<< QDate(1994, 12, 31) << QByteArray("Pacific/Kiritimati")
|
||||
<< invalid << invalid;
|
||||
}
|
||||
if (QTimeZone("Pacific/Apia").isValid()) {
|
||||
QTest::newRow("Samoa")
|
||||
<< QDate(2011, 12, 30) << QByteArray("Pacific/Apia")
|
||||
<< invalid << invalid;
|
||||
}
|
||||
// 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 auto zone = QTimeZone(zoneName);
|
||||
QFETCH(const QDate, date);
|
||||
QFETCH(const QTimeZone, zone);
|
||||
QFETCH(const QTime, start);
|
||||
QFETCH(const QTime, end);
|
||||
#ifdef USING_WIN_TZ // Coping with MS limitations.
|
||||
QFETCH(const MsKludges, msKludge);
|
||||
#define UNLESSMS(flag) if (!msKludge.testFlag(flag))
|
||||
#else
|
||||
#define UNLESSMS(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();
|
||||
back = date.endOfDay();
|
||||
} while (isSystem);
|
||||
if (end.isValid())
|
||||
QCOMPARE(date.addDays(1).startOfDay().addMSecs(-1), back);
|
||||
if (start.isValid())
|
||||
QCOMPARE(date.addDays(-1).endOfDay().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()) {
|
||||
QCOMPARE(front.date(), date);
|
||||
UNLESSMS(IgnoreStart) QCOMPARE(front.time(), start);
|
||||
}
|
||||
if (end.isValid()) {
|
||||
QCOMPARE(back.date(), date);
|
||||
UNLESSMS(IgnoreEnd) QCOMPARE(back.time(), end);
|
||||
}
|
||||
#undef UNLESSMS
|
||||
}
|
||||
#endif // timezone
|
||||
|
||||
void tst_QDate::startOfDay_endOfDay_fixed_data()
|
||||
{
|
||||
QTest::addColumn<QDate>("date");
|
||||
|
||||
const qint64 kilo(1000);
|
||||
using Bounds = std::numeric_limits<qint64>;
|
||||
const auto UTC = QTimeZone::UTC;
|
||||
const QDateTime
|
||||
first(QDateTime::fromMSecsSinceEpoch(Bounds::min() + 1, UTC)),
|
||||
start32sign(QDateTime::fromMSecsSinceEpoch(Q_INT64_C(-0x80000000) * kilo, UTC)),
|
||||
end32sign(QDateTime::fromMSecsSinceEpoch(Q_INT64_C(0x80000000) * kilo, UTC)),
|
||||
end32unsign(QDateTime::fromMSecsSinceEpoch(Q_INT64_C(0x100000000) * kilo, UTC)),
|
||||
last(QDateTime::fromMSecsSinceEpoch(Bounds::max(), 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));
|
||||
|
||||
const struct {
|
||||
const char *name;
|
||||
QDate date;
|
||||
} data[] = {
|
||||
{ "epoch", QDate(1970, 1, 1) },
|
||||
{ "y2k-leap-day", QDate(2000, 2, 29) },
|
||||
{ "start-1900", QDate(1900, 1, 1) }, // QTBUG-99747
|
||||
// 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;
|
||||
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()
|
||||
@ -652,7 +696,6 @@ void tst_QDate::startOfDay_endOfDay_bounds()
|
||||
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);
|
||||
|
||||
@ -678,8 +721,6 @@ 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);
|
||||
@ -902,9 +943,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);
|
||||
@ -1199,13 +1237,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);
|
||||
@ -1244,7 +1282,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);
|
||||
@ -1354,11 +1392,23 @@ void tst_QDate::fromStringFormat_data()
|
||||
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("quoted-dd") << QString("21dd-05-2006") << QString("dd'dd'-MM-yyyy")
|
||||
<< QDate(2006, 5, 21);
|
||||
QTest::newRow("quoted-MM") << QString("21-MM05-2006") << QString("dd-'MM'MM-yyyy")
|
||||
<< QDate(2006, 5, 21);
|
||||
QTest::newRow("quotes-empty") << QString("21-'05-2006") << QString("dd-MM-''yy")
|
||||
<< 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")
|
||||
QTest::newRow("Unicode-in-format-string-quoted-emoji")
|
||||
<< QString(u8"🤣🤣2020👍09🤣21") << QString(u8"'🤣🤣'yyyy👍MM🤣dd") << QDate(2020, 9, 21);
|
||||
QTest::newRow("Unicode-in-quoted-dd-format-string")
|
||||
<< QString(u8"🤣🤣2020👍09🤣21dd") << QString(u8"🤣🤣yyyy👍MM🤣dd'dd'") << 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'")
|
||||
<< QDate(2020, 9, 21);
|
||||
|
||||
// QTBUG-84334
|
||||
QTest::newRow("-ve year: front, nosep")
|
||||
@ -1441,6 +1491,8 @@ void tst_QDate::fromStringFormat()
|
||||
QFETCH(QDate, expected);
|
||||
|
||||
QDate dt = QDate::fromString(string, format);
|
||||
QEXPECT_FAIL("quotes-empty", "QTBUG-110669: doubled single-quotes in format mishandled",
|
||||
Continue);
|
||||
QCOMPARE(dt, expected);
|
||||
}
|
||||
#endif // datetimeparser
|
||||
@ -1653,9 +1705,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) {
|
||||
|
@ -7,8 +7,8 @@
|
||||
|
||||
#include <QTimeZone>
|
||||
#include <private/qdatetime_p.h>
|
||||
#include <private/qtenvironmentvariables_p.h> // for qTzSet(), qTzName()
|
||||
|
||||
#include <time.h>
|
||||
#ifdef Q_OS_WIN
|
||||
# include <qt_windows.h>
|
||||
#endif
|
||||
@ -151,11 +151,34 @@ private Q_SLOTS:
|
||||
#endif
|
||||
|
||||
private:
|
||||
enum { LocalTimeIsUtc = 0, LocalTimeAheadOfUtc = 1, LocalTimeBehindUtc = -1} localTimeType;
|
||||
/*
|
||||
Various zones close to UTC (notably Iceland, the WET zones and several in
|
||||
West Africa) or nominally assigned to it historically (north Canada, the
|
||||
Antarctic) and those that have crossed the international date-line (by
|
||||
skipping or repeating a day) don't have a consistent answer to "which side
|
||||
of UTC is it ?" So the various LocalTimeType members may be different.
|
||||
*/
|
||||
enum LocalTimeType { LocalTimeIsUtc = 0, LocalTimeAheadOfUtc = 1, LocalTimeBehindUtc = -1};
|
||||
const LocalTimeType solarMeanType, epochTimeType, futureTimeType, distantTimeType;
|
||||
static constexpr auto UTC = QTimeZone::UTC;
|
||||
static constexpr qint64 epochJd = Q_INT64_C(2440588);
|
||||
int preZoneFix;
|
||||
bool zoneIsCET;
|
||||
|
||||
static LocalTimeType timeTypeFor(qint64 jand, qint64 juld)
|
||||
{
|
||||
constexpr uint day = 24 * 3600; // in seconds
|
||||
QDateTime jan = QDateTime::fromSecsSinceEpoch(jand * day);
|
||||
QDateTime jul = QDateTime::fromSecsSinceEpoch(juld * day);
|
||||
if (jan.date().toJulianDay() < jand + epochJd || jul.date().toJulianDay() < juld + epochJd)
|
||||
return LocalTimeBehindUtc;
|
||||
if (jan.date().toJulianDay() > jand + epochJd || jul.date().toJulianDay() > juld + epochJd
|
||||
|| jan.time().hour() > 0 || jul.time().hour() > 0) {
|
||||
return LocalTimeAheadOfUtc;
|
||||
}
|
||||
return LocalTimeIsUtc;
|
||||
}
|
||||
|
||||
class TimeZoneRollback
|
||||
{
|
||||
const QByteArray prior;
|
||||
@ -183,7 +206,18 @@ private:
|
||||
Q_DECLARE_METATYPE(Qt::TimeSpec)
|
||||
Q_DECLARE_METATYPE(Qt::DateFormat)
|
||||
|
||||
tst_QDateTime::tst_QDateTime()
|
||||
tst_QDateTime::tst_QDateTime() :
|
||||
// UTC starts of January and July in the commented years:
|
||||
solarMeanType(timeTypeFor(-62091, -61910)), // 1800
|
||||
epochTimeType(timeTypeFor(0, 181)), // 1970
|
||||
// Use stable future, to which current rule is extrapolated, as surrogate for variable current:
|
||||
futureTimeType(timeTypeFor(24837, 25018)), // 2038
|
||||
// The glibc functions only handle DST as far as a 32-bit signed day-count
|
||||
// from some date in 1970 reaches; the future extreme of that is in the
|
||||
// second half of 5'881'580 CE. Beyond 5'881'581 CE it treats all zones as
|
||||
// being in their January state, regardless of time of year. So use data for
|
||||
// this later year for tests of QDateTime's upper bound.
|
||||
distantTimeType(timeTypeFor(0x800000adLL, 0x80000162LL))
|
||||
{
|
||||
/*
|
||||
Due to some jurisdictions changing their zones and rules, it's possible
|
||||
@ -197,7 +231,6 @@ tst_QDateTime::tst_QDateTime()
|
||||
might not be properly handled by our work-arounds for the MS backend and
|
||||
32-bit time_t; so don't probe them here.
|
||||
*/
|
||||
const uint day = 24 * 3600; // in seconds
|
||||
zoneIsCET = (QDateTime(QDate(2038, 1, 19), QTime(4, 14, 7)).toSecsSinceEpoch() == 0x7fffffff
|
||||
// Entries a year apart robustly differ by multiples of day.
|
||||
&& QDate(2015, 7, 1).startOfDay().toSecsSinceEpoch() == 1435701600
|
||||
@ -227,31 +260,6 @@ tst_QDateTime::tst_QDateTime()
|
||||
Q_ASSERT(preZoneFix > -7200 && preZoneFix < 7200);
|
||||
// So it's OK to add it to a QTime() between 02:00 and 22:00, but otherwise
|
||||
// we must add it to the QDateTime constructed from it.
|
||||
|
||||
/*
|
||||
Again, rule changes can cause a TZ to look like UTC at some sample dates
|
||||
but deviate at some date relevant to a test using localTimeType. These
|
||||
tests mostly use years outside the 1970--2037 range, for which we trust
|
||||
our TZ data, so we can't helpfully be exhaustive. Instead, scan a sample
|
||||
of years' starts and middles.
|
||||
*/
|
||||
const int sampled = 3;
|
||||
// UTC starts of months in 2004, 2038 and 1970:
|
||||
qint64 jans[sampled] = { 12418 * day, 24837 * day, 0 };
|
||||
qint64 juls[sampled] = { 12600 * day, 25018 * day, 181 * day };
|
||||
localTimeType = LocalTimeIsUtc;
|
||||
for (int i = sampled; i-- > 0; ) {
|
||||
QDateTime jan = QDateTime::fromSecsSinceEpoch(jans[i]);
|
||||
QDateTime jul = QDateTime::fromSecsSinceEpoch(juls[i]);
|
||||
if (jan.date().year() < 1970 || jul.date().month() < 7) {
|
||||
localTimeType = LocalTimeBehindUtc;
|
||||
break;
|
||||
} else if (jan.time().hour() > 0 || jul.time().hour() > 0
|
||||
|| jan.date().day() > 1 || jul.date().day() > 1) {
|
||||
localTimeType = LocalTimeAheadOfUtc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QDateTime::initTestCase()
|
||||
@ -259,7 +267,7 @@ void tst_QDateTime::initTestCase()
|
||||
// Never construct a message like this in an i18n context...
|
||||
const char *typemsg1 = "exactly";
|
||||
const char *typemsg2 = "and therefore not";
|
||||
switch (localTimeType) {
|
||||
switch (futureTimeType) {
|
||||
case LocalTimeIsUtc:
|
||||
break;
|
||||
case LocalTimeBehindUtc:
|
||||
@ -326,7 +334,7 @@ void tst_QDateTime::ctor()
|
||||
|
||||
void tst_QDateTime::operator_eq()
|
||||
{
|
||||
QVERIFY(QDateTime() != QDateTime(QDate(1970, 1, 1), QTime(0, 0))); // QTBUG-79006
|
||||
QVERIFY(QDateTime() != QDate(1970, 1, 1).startOfDay()); // QTBUG-79006
|
||||
QDateTime dt1(QDate(2004, 3, 24), QTime(23, 45, 57), UTC);
|
||||
QDateTime dt2(QDate(2005, 3, 11), QTime(0, 0), UTC);
|
||||
dt2 = dt1;
|
||||
@ -756,6 +764,7 @@ void tst_QDateTime::setMSecsSinceEpoch()
|
||||
QFETCH(qint64, msecs);
|
||||
QFETCH(QDateTime, utc);
|
||||
QFETCH(QDateTime, cet);
|
||||
using Bound = std::numeric_limits<qint64>;
|
||||
|
||||
QDateTime dt;
|
||||
dt.setTimeZone(UTC);
|
||||
@ -789,10 +798,10 @@ void tst_QDateTime::setMSecsSinceEpoch()
|
||||
QCOMPARE(dt1.timeSpec(), Qt::UTC);
|
||||
}
|
||||
|
||||
if (zoneIsCET && (msecs == std::numeric_limits<qint64>::max()
|
||||
if (zoneIsCET && (msecs == Bound::max()
|
||||
// LocalTime will also overflow for min in a CET zone west
|
||||
// of Greenwich (Europe/Madrid):
|
||||
|| (preZoneFix < -3600 && msecs == std::numeric_limits<qint64>::min()))) {
|
||||
|| (preZoneFix < -3600 && msecs == Bound::min()))) {
|
||||
QVERIFY(!cet.isValid()); // overflows
|
||||
} else if (zoneIsCET) {
|
||||
QVERIFY(cet.isValid());
|
||||
@ -834,18 +843,22 @@ void tst_QDateTime::setMSecsSinceEpoch()
|
||||
QCOMPARE(dt, reference.addMSecs(msecs));
|
||||
|
||||
// Tests that we correctly recognize when we fall off the extremities:
|
||||
if (msecs == std::numeric_limits<qint64>::max()) {
|
||||
if (msecs == Bound::max()) {
|
||||
QDateTime off(QDate(1970, 1, 1).startOfDay(QTimeZone::fromSecondsAheadOfUtc(1)));
|
||||
off.setMSecsSinceEpoch(msecs);
|
||||
QVERIFY(!off.isValid());
|
||||
} else if (msecs == std::numeric_limits<qint64>::min()) {
|
||||
} else if (msecs == Bound::min()) {
|
||||
QDateTime off(QDate(1970, 1, 1).startOfDay(QTimeZone::fromSecondsAheadOfUtc(-1)));
|
||||
off.setMSecsSinceEpoch(msecs);
|
||||
QVERIFY(!off.isValid());
|
||||
}
|
||||
|
||||
if ((localTimeType == LocalTimeAheadOfUtc && msecs == std::numeric_limits<qint64>::max())
|
||||
|| (localTimeType == LocalTimeBehindUtc && msecs == std::numeric_limits<qint64>::min())) {
|
||||
// Check overflow; only robust if local time is the same at epoch as relevant bound.
|
||||
// See setting of LocalTimeType values for details.
|
||||
if (epochTimeType == LocalTimeAheadOfUtc
|
||||
? distantTimeType == LocalTimeAheadOfUtc && msecs == Bound::max()
|
||||
: (solarMeanType == LocalTimeBehindUtc && msecs == Bound::min()
|
||||
&& epochTimeType == LocalTimeBehindUtc)) {
|
||||
QDateTime curt = QDate(1970, 1, 1).startOfDay(); // initially in short-form
|
||||
curt.setMSecsSinceEpoch(msecs); // Overflows due to offset
|
||||
QVERIFY(!curt.isValid());
|
||||
@ -867,10 +880,10 @@ void tst_QDateTime::fromMSecsSinceEpoch()
|
||||
// you're East or West of Greenwich. In UTC, we won't overflow. If we're
|
||||
// actually west of Greenwich but (e.g. Europe/Madrid) our zone claims east,
|
||||
// "min" can also overflow (case only caught if local time is CET).
|
||||
const bool localOverflow = (localTimeType == LocalTimeAheadOfUtc
|
||||
? msecs == Bound::max() || preZoneFix < -3600
|
||||
: localTimeType == LocalTimeBehindUtc && msecs == Bound::min());
|
||||
if (!localOverflow)
|
||||
const bool localOverflow =
|
||||
(distantTimeType == LocalTimeAheadOfUtc && (msecs == Bound::max() || preZoneFix < -3600))
|
||||
|| (solarMeanType == LocalTimeBehindUtc && msecs == Bound::min());
|
||||
if (!localOverflow) // Can fail if offset changes sign, e.g. Alaska, Philippines.
|
||||
QCOMPARE(dtLocal, utc);
|
||||
|
||||
QCOMPARE(dtUtc, utc);
|
||||
@ -925,7 +938,22 @@ void tst_QDateTime::fromSecsSinceEpoch()
|
||||
QVERIFY(!QDateTime::fromSecsSinceEpoch(-maxSeconds - 1, UTC).isValid());
|
||||
|
||||
// Local time: need to adjust for its zone offset
|
||||
const qint64 last = maxSeconds - qMax(late.addYears(-1).toLocalTime().offsetFromUtc(), 0);
|
||||
const int lateOffset = late.addYears(-1).toLocalTime().offsetFromUtc();
|
||||
#if QT_CONFIG(timezone)
|
||||
// Check what system zone believes in, as it's used as fall-back to cope
|
||||
// with times outside the system time_t functions' range, or overflow on the
|
||||
// results of using those functions. (It seems glibc's handling of
|
||||
// Australasian zones parts company with the IANA DB after about 5881580 CE,
|
||||
// leaving NZ in permanent DST after that, for example.) Of course, if
|
||||
// that's less than lateOffset (as it is for glibc's similar handling of
|
||||
// MET), the fall-back code will also fail when the primary code fails, so
|
||||
// use the lesser of these late offsets.
|
||||
const int lateZone = qMin(QTimeZone::systemTimeZone().offsetFromUtc(late), lateOffset);
|
||||
#else
|
||||
const int lateZone = lateOffset;
|
||||
#endif
|
||||
|
||||
const qint64 last = maxSeconds - qMax(lateZone, 0);
|
||||
QVERIFY(QDateTime::fromSecsSinceEpoch(last).isValid());
|
||||
QVERIFY(!QDateTime::fromSecsSinceEpoch(last + 1).isValid());
|
||||
const qint64 first = -maxSeconds - qMin(early.addYears(1).toLocalTime().offsetFromUtc(), 0);
|
||||
@ -1263,13 +1291,17 @@ void tst_QDateTime::addDays()
|
||||
}
|
||||
#endif
|
||||
|
||||
// Test last UTC second of 1969 *is* valid (despite being time_t(-1))
|
||||
dt1 = QDateTime(QDate(1969, 12, 30), QTime(23, 59, 59), UTC).toLocalTime().addDays(1);
|
||||
QVERIFY(dt1.isValid());
|
||||
QCOMPARE(dt1.toSecsSinceEpoch(), -1);
|
||||
dt2 = QDateTime(QDate(1970, 1, 1), QTime(23, 59, 59), UTC).toLocalTime().addDays(-1);
|
||||
QVERIFY(dt2.isValid());
|
||||
QCOMPARE(dt2.toSecsSinceEpoch(), -1);
|
||||
// Baja Mexico has a transition at the epoch, see fromStringDateFormat_data().
|
||||
if (QDateTime(QDate(1969, 12, 30), QTime(0, 0)).secsTo(
|
||||
QDateTime(QDate(1970, 1, 2), QTime(0, 0))) == 3 * 24 * 60 * 60) {
|
||||
// Test last UTC second of 1969 *is* valid (despite being time_t(-1))
|
||||
dt1 = QDateTime(QDate(1969, 12, 30), QTime(23, 59, 59), UTC).toLocalTime().addDays(1);
|
||||
QVERIFY(dt1.isValid());
|
||||
QCOMPARE(dt1.toSecsSinceEpoch(), -1);
|
||||
dt2 = QDateTime(QDate(1970, 1, 1), QTime(23, 59, 59), UTC).toLocalTime().addDays(-1);
|
||||
QVERIFY(dt2.isValid());
|
||||
QCOMPARE(dt2.toSecsSinceEpoch(), -1);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QDateTime::addInvalid()
|
||||
@ -1525,10 +1557,10 @@ void tst_QDateTime::addMSecs_data()
|
||||
<< QDateTime(QDate(2013, 1, 1), QTime(2, 2, 3), QTimeZone::fromSecondsAheadOfUtc(60 * 60));
|
||||
// Check last second of 1969
|
||||
QTest::newRow("epoch-1s-utc")
|
||||
<< QDateTime(QDate(1970, 1, 1), QTime(0, 0), UTC) << qint64(-1)
|
||||
<< QDate(1970, 1, 1).startOfDay(UTC) << qint64(-1)
|
||||
<< QDateTime(QDate(1969, 12, 31), QTime(23, 59, 59), UTC);
|
||||
QTest::newRow("epoch-1s-local")
|
||||
<< QDateTime(QDate(1970, 1, 1), QTime(0, 0)) << qint64(-1)
|
||||
<< QDate(1970, 1, 1).startOfDay() << qint64(-1)
|
||||
<< QDateTime(QDate(1969, 12, 31), QTime(23, 59, 59));
|
||||
QTest::newRow("epoch-1s-utc-as-local")
|
||||
<< QDate(1970, 1, 1).startOfDay(UTC).toLocalTime() << qint64(-1)
|
||||
@ -2142,12 +2174,13 @@ void tst_QDateTime::springForward_data()
|
||||
document any such conflicts, if discovered.
|
||||
|
||||
See http://www.timeanddate.com/time/zones/ for data on more candidates to
|
||||
test.
|
||||
*/
|
||||
test. Note, however, that the IANA DB disagrees with it for some zones,
|
||||
and is authoritative.
|
||||
*/
|
||||
|
||||
QTimeZone local(QTimeZone::LocalTime);
|
||||
uint winter = QDate(2015, 1, 1).startOfDay(local).toSecsSinceEpoch();
|
||||
uint summer = QDate(2015, 7, 1).startOfDay(local).toSecsSinceEpoch();
|
||||
const QTimeZone local(QTimeZone::LocalTime);
|
||||
const uint winter = QDate(2015, 1, 1).startOfDay(local).toSecsSinceEpoch();
|
||||
const uint summer = QDate(2015, 7, 1).startOfDay(local).toSecsSinceEpoch();
|
||||
|
||||
if (winter == 1420066800 && summer == 1435701600) {
|
||||
QTest::newRow("Local (CET) from day before")
|
||||
@ -2155,11 +2188,28 @@ void tst_QDateTime::springForward_data()
|
||||
QTest::newRow("Local (CET) from day after")
|
||||
<< local << QDate(2015, 3, 29) << QTime(2, 30) << -1 << 120;
|
||||
} else if (winter == 1420063200 && summer == 1435698000) {
|
||||
// e.g. Finland, where our CI runs ...
|
||||
// EET: but there's some variation in the date and time.
|
||||
// Asia/{Amman,Beirut,Gaza,Hebron}, Europe/Chisinau and Israel: at start of
|
||||
QDate date(2015, 3, 29); // Sunday by default.
|
||||
QTime time(0, 30);
|
||||
if (auto thursday = QDate(2015, 3, 26); thursday.startOfDay(local).time() > time) {
|
||||
// Asia/Damascus: start of March 26th.
|
||||
date = thursday;
|
||||
} else if (auto friday = QDate(2015, 3, 27); friday.startOfDay(local).time() > time) {
|
||||
// Israel, Asia/{Jerusalem,Tel_Aviv}: start of March 27th (IANA DB).
|
||||
date = friday;
|
||||
} else if (friday.startOfDay(local).addSecs(2 * 60 * 60).time() == QTime(3, 0)) {
|
||||
// Israel, Asia/{Jerusalem,Tel_Aviv} according to glibc at 02:00 on March 27th.
|
||||
date = friday;
|
||||
time = QTime(2, 30);
|
||||
} else if (date.startOfDay(local).time() < time) {
|
||||
// Most of Europeean EET, e.g. Finland.
|
||||
time = QTime(3, 30);
|
||||
}
|
||||
QTest::newRow("Local (EET) from day before")
|
||||
<< local << QDate(2015, 3, 29) << QTime(3, 30) << 1 << 120;
|
||||
<< local << date << time << 1 << 120;
|
||||
QTest::newRow("Local (EET) from day after")
|
||||
<< local << QDate(2015, 3, 29) << QTime(3, 30) << -1 << 180;
|
||||
<< local << date << time << -1 << 180;
|
||||
} else if (winter == 1420070400 && summer == 1435705200) {
|
||||
// Western European Time, WET/WEST; a.k.a. GMT/BST
|
||||
QTest::newRow("Local (WET) from day before")
|
||||
@ -2168,16 +2218,23 @@ void tst_QDateTime::springForward_data()
|
||||
<< local << QDate(2015, 3, 29) << QTime(1, 30) << -1 << 60;
|
||||
} else if (winter == 1420099200 && summer == 1435734000) {
|
||||
// Western USA, Canada: Pacific Time (e.g. US/Pacific)
|
||||
QDate date(2015, 3, 8);
|
||||
// America/Ensenada did its transition on April 5th, like the rest of Mexico.
|
||||
if (QDate(2015, 4, 1).startOfDay().toSecsSinceEpoch() == 1427875200)
|
||||
date = QDate(2015, 4, 5);
|
||||
QTest::newRow("Local (PT) from day before")
|
||||
<< local << QDate(2015, 3, 8) << QTime(2, 30) << 1 << -480;
|
||||
<< local << date << QTime(2, 30) << 1 << -480;
|
||||
QTest::newRow("Local (PT) from day after")
|
||||
<< local << QDate(2015, 3, 8) << QTime(2, 30) << -1 << -420;
|
||||
<< local << date << QTime(2, 30) << -1 << -420;
|
||||
} else if (winter == 1420088400 && summer == 1435723200) {
|
||||
// Eastern USA, Canada: Eastern Time (e.g. US/Eastern)
|
||||
// Havana matches offset and date, but at midnight.
|
||||
const QTime start = QDate(2015, 3, 8).startOfDay(local).time();
|
||||
const QTime when = start == QTime(0, 0) ? QTime(2, 30) : QTime(0, 30);
|
||||
QTest::newRow("Local(ET) from day before")
|
||||
<< local << QDate(2015, 3, 8) << QTime(2, 30) << 1 << -300;
|
||||
<< local << QDate(2015, 3, 8) << when << 1 << -300;
|
||||
QTest::newRow("Local(ET) from day after")
|
||||
<< local << QDate(2015, 3, 8) << QTime(2, 30) << -1 << -240;
|
||||
<< local << QDate(2015, 3, 8) << when << -1 << -240;
|
||||
#if !QT_CONFIG(timezone)
|
||||
} else {
|
||||
// Includes the numbers you need to test for your zone, as above:
|
||||
@ -2233,15 +2290,19 @@ void tst_QDateTime::springForward()
|
||||
QCOMPARE(direct.date(), day);
|
||||
QCOMPARE(direct.time().minute(), time.minute());
|
||||
QCOMPARE(direct.time().second(), time.second());
|
||||
int off = direct.time().hour() - time.hour();
|
||||
QVERIFY(off == 1 || off == -1);
|
||||
// Note: function doc claims always +1, but this should be reviewed !
|
||||
int off = direct.time().hour() - time.hour();
|
||||
auto report = qScopeGuard([off]() {
|
||||
qDebug("Offset %d found where 1 or -1 expected", off);
|
||||
});
|
||||
QVERIFY(off == 1 || off == -1);
|
||||
report.dismiss();
|
||||
}
|
||||
|
||||
// Repeat, but getting there via .toTimeZone():
|
||||
QDateTime detour = QDateTime(day.addDays(-step),
|
||||
time.addSecs(-60 * adjust),
|
||||
UTC).toTimeZone(zone);
|
||||
// Repeat, but getting there via .toTimeZone(). Apply adjust to datetime,
|
||||
// not time, as the time wraps round if the adjustment crosses midnight.
|
||||
QDateTime detour = QDateTime(day.addDays(-step), time,
|
||||
UTC).addSecs(-60 * adjust).toTimeZone(zone);
|
||||
QCOMPARE(detour.time(), time);
|
||||
detour = detour.addDays(step);
|
||||
// Insist on consistency:
|
||||
@ -2285,7 +2346,7 @@ void tst_QDateTime::operator_eqeq_data()
|
||||
QTest::newRow("data10") << dateTime3 << dateTime3c << true << false;
|
||||
QTest::newRow("data11") << dateTime3 << dateTime3d << true << false;
|
||||
QTest::newRow("data12") << dateTime3c << dateTime3d << true << false;
|
||||
if (localTimeType == LocalTimeIsUtc)
|
||||
if (epochTimeType == LocalTimeIsUtc)
|
||||
QTest::newRow("data13") << dateTime3 << dateTime3e << true << false;
|
||||
// ... but a zone (sometimes) ahead of or behind UTC (e.g. Europe/London)
|
||||
// might agree with UTC about the epoch, all the same.
|
||||
@ -2478,6 +2539,10 @@ void tst_QDateTime::fromStringDateFormat_data()
|
||||
QTest::addColumn<Qt::DateFormat>("dateFormat");
|
||||
QTest::addColumn<QDateTime>("expected");
|
||||
|
||||
// Fails 1970 start dates in western Mexico
|
||||
// due to changing from PST to MST at the start of 1970.
|
||||
const bool goodEpochStart = QDateTime(QDate(1970, 1, 1), QTime(0, 0)).isValid();
|
||||
|
||||
// Test Qt::TextDate format.
|
||||
QTest::newRow("text date") << QString::fromLatin1("Tue Jun 17 08:00:10 2003")
|
||||
<< Qt::TextDate << QDateTime(QDate(2003, 6, 17), QTime(8, 0, 10));
|
||||
@ -2489,22 +2554,25 @@ void tst_QDateTime::fromStringDateFormat_data()
|
||||
<< Qt::TextDate << QDateTime(QDate(12345, 6, 17), QTime(8, 0, 10));
|
||||
QTest::newRow("text date Year -4712") << QString::fromLatin1("Tue Jan 1 00:01:02 -4712")
|
||||
<< Qt::TextDate << QDateTime(QDate(-4712, 1, 1), QTime(0, 1, 2));
|
||||
QTest::newRow("text epoch")
|
||||
<< QString::fromLatin1("Thu Jan 1 00:00:00 1970") << Qt::TextDate
|
||||
<< QDate(1970, 1, 1).startOfDay();
|
||||
QTest::newRow("text data1") << QString::fromLatin1("Thu Jan 2 12:34 1970")
|
||||
<< Qt::TextDate << QDateTime(QDate(1970, 1, 2), QTime(12, 34));
|
||||
if (goodEpochStart) {
|
||||
QTest::newRow("text epoch year after time")
|
||||
<< QString::fromLatin1("Thu Jan 1 00:00:00 1970") << Qt::TextDate
|
||||
<< QDate(1970, 1, 1).startOfDay();
|
||||
QTest::newRow("text epoch spaced")
|
||||
<< QString::fromLatin1(" Thu Jan 1 00:00:00 1970 ")
|
||||
<< Qt::TextDate << QDate(1970, 1, 1).startOfDay();
|
||||
QTest::newRow("text epoch time after year")
|
||||
<< QString::fromLatin1("Thu Jan 1 1970 00:00:00")
|
||||
<< Qt::TextDate << QDate(1970, 1, 1).startOfDay();
|
||||
}
|
||||
QTest::newRow("text epoch terse")
|
||||
<< QString::fromLatin1("Thu Jan 1 00 1970") << Qt::TextDate << QDateTime();
|
||||
QTest::newRow("text epoch stray :00")
|
||||
<< QString::fromLatin1("Thu Jan 1 00:00:00:00 1970") << Qt::TextDate << QDateTime();
|
||||
QTest::newRow("text epoch spaced")
|
||||
<< QString::fromLatin1(" Thu Jan 1 00:00:00 1970 ")
|
||||
<< Qt::TextDate << QDate(1970, 1, 1).startOfDay();
|
||||
QTest::newRow("text data6") << QString::fromLatin1("Thu Jan 1 00:00:00")
|
||||
<< Qt::TextDate << QDateTime();
|
||||
QTest::newRow("text data7") << QString::fromLatin1("Thu Jan 1 1970 00:00:00")
|
||||
<< Qt::TextDate << QDate(1970, 1, 1).startOfDay();
|
||||
QTest::newRow("text bad offset") << QString::fromLatin1("Thu Jan 1 00:12:34 1970 UTC+foo")
|
||||
<< Qt::TextDate << QDateTime();
|
||||
QTest::newRow("text UTC early") << QString::fromLatin1("Thu Jan 1 00:12:34 1970 UTC")
|
||||
@ -2885,34 +2953,35 @@ void tst_QDateTime::fromStringStringFormat_data()
|
||||
QTest::addColumn<QDateTime>("expected");
|
||||
|
||||
const QDate defDate(1900, 1, 1);
|
||||
QTest::newRow("data0")
|
||||
<< QString("101010") << QString("dMyy") << QDate(1910, 10, 10).startOfDay();
|
||||
// Indian/Cocos had a transition at the start of 1900, so its Jan 1st starts
|
||||
// at 00:02:20 on that day; this leads to perverse results. QTBUG-77948.
|
||||
if (defDate.startOfDay().time() == QTime(0, 0)) {
|
||||
QTest::newRow("dMyy-only")
|
||||
<< QString("101010") << QString("dMyy") << QDate(1910, 10, 10).startOfDay();
|
||||
QTest::newRow("secs-repeat-valid")
|
||||
<< QString("1010") << QString("sss") << QDateTime(defDate, QTime(0, 0, 10));
|
||||
QTest::newRow("pm-only")
|
||||
<< QString("pm") << QString("ap") << QDateTime(defDate, QTime(12, 0));
|
||||
QTest::newRow("date-only")
|
||||
<< QString("10 Oct 10") << QString("dd MMM yy") << QDate(1910, 10, 10).startOfDay();
|
||||
QTest::newRow("dow-date-only")
|
||||
<< QString("Fri December 3 2004") << QString("ddd MMMM d yyyy")
|
||||
<< QDate(2004, 12, 3).startOfDay();
|
||||
QTest::newRow("dow-mon-yr-only")
|
||||
<< QString("Thu January 2004") << QString("ddd MMMM yyyy")
|
||||
<< QDate(2004, 1, 1).startOfDay();
|
||||
}
|
||||
QTest::newRow("data1") << QString("1020") << QString("sss") << QDateTime();
|
||||
QTest::newRow("data2")
|
||||
<< QString("1010") << QString("sss") << QDateTime(defDate, QTime(0, 0, 10));
|
||||
QTest::newRow("data3") << QString("10hello20") << QString("ss'hello'ss") << QDateTime();
|
||||
QTest::newRow("data4") << QString("10") << QString("''") << QDateTime();
|
||||
QTest::newRow("data5") << QString("10") << QString("'") << QDateTime();
|
||||
QTest::newRow("data6") << QString("pm") << QString("ap") << QDateTime(defDate, QTime(12, 0));
|
||||
QTest::newRow("data7") << QString("foo") << QString("ap") << QDateTime();
|
||||
// Day non-conflict should not hide earlier year conflict (1963-03-01 was a
|
||||
// Friday; asking for Thursday moves this, without conflict, to the 7th):
|
||||
QTest::newRow("data8")
|
||||
<< QString("77 03 1963 Thu") << QString("yy MM yyyy ddd") << QDateTime();
|
||||
QTest::newRow("data9")
|
||||
<< QString("101010") << QString("dMyy") << QDate(1910, 10, 10).startOfDay();
|
||||
QTest::newRow("data10")
|
||||
<< QString("101010") << QString("dMyy") << QDate(1910, 10, 10).startOfDay();
|
||||
QTest::newRow("data11")
|
||||
<< QString("10 Oct 10") << QString("dd MMM yy") << QDate(1910, 10, 10).startOfDay();
|
||||
QTest::newRow("data12")
|
||||
<< QString("Fri December 3 2004") << QString("ddd MMMM d yyyy")
|
||||
<< QDate(2004, 12, 3).startOfDay();
|
||||
QTest::newRow("data13") << QString("30.02.2004") << QString("dd.MM.yyyy") << QDateTime();
|
||||
QTest::newRow("data14") << QString("32.01.2004") << QString("dd.MM.yyyy") << QDateTime();
|
||||
QTest::newRow("data15")
|
||||
<< QString("Thu January 2004") << QString("ddd MMMM yyyy")
|
||||
<< QDate(2004, 1, 1).startOfDay();
|
||||
QTest::newRow("zulu-time-with-z-centisec")
|
||||
<< QString("2005-06-28T07:57:30.01Z") << QString("yyyy-MM-ddThh:mm:ss.zt")
|
||||
<< QDateTime(QDate(2005, 06, 28), QTime(07, 57, 30, 10), UTC);
|
||||
@ -3188,7 +3257,7 @@ void tst_QDateTime::fromStringStringFormat_localTimeZone_data()
|
||||
lacksRows = false;
|
||||
const bool fullyLocal = ([]() {
|
||||
TimeZoneRollback useZone("GMT");
|
||||
return QDateTime::currentDateTime().timeZoneAbbreviation() == u"GMT"_s;
|
||||
return qTzName(0) == u"GMT"_s;
|
||||
})();
|
||||
QTest::newRow("local-timezone-with-offset:GMT")
|
||||
<< QByteArrayLiteral("GMT")
|
||||
@ -3453,16 +3522,34 @@ void tst_QDateTime::timeZoneAbbreviation()
|
||||
if (zoneIsCET) {
|
||||
// Time definitely in Standard Time
|
||||
QDateTime dt4 = QDate(2013, 1, 1).startOfDay();
|
||||
/* Note that MET is functionally an alias for CET (their zoneinfo files
|
||||
differ only in the first letter of the abbreviations), unlike the
|
||||
various zones that give CET as their abbreviation.
|
||||
*/
|
||||
{
|
||||
const auto abbrev = dt4.timeZoneAbbreviation();
|
||||
auto reporter = qScopeGuard([abbrev]() {
|
||||
qDebug() << "Unexpected abbreviation" << abbrev;
|
||||
});
|
||||
#ifdef Q_OS_WIN
|
||||
QEXPECT_FAIL("", "Windows only reports long name (QTBUG-32759)", Continue);
|
||||
QEXPECT_FAIL("", "Windows only reports long name (QTBUG-32759)", Continue);
|
||||
#endif
|
||||
QCOMPARE(dt4.timeZoneAbbreviation(), QStringLiteral("CET"));
|
||||
QVERIFY(abbrev == u"CET"_s || abbrev == u"MET"_s);
|
||||
reporter.dismiss();
|
||||
}
|
||||
// Time definitely in Daylight Time
|
||||
QDateTime dt5 = QDate(2013, 6, 1).startOfDay();
|
||||
{
|
||||
const auto abbrev = dt5.timeZoneAbbreviation();
|
||||
auto reporter = qScopeGuard([abbrev]() {
|
||||
qDebug() << "Unexpected abbreviation" << abbrev;
|
||||
});
|
||||
#ifdef Q_OS_WIN
|
||||
QEXPECT_FAIL("", "Windows only reports long name (QTBUG-32759)", Continue);
|
||||
QEXPECT_FAIL("", "Windows only reports long name (QTBUG-32759)", Continue);
|
||||
#endif
|
||||
QCOMPARE(dt5.timeZoneAbbreviation(), QStringLiteral("CEST"));
|
||||
QVERIFY(abbrev == u"CEST"_s || abbrev == u"MEST"_s);
|
||||
reporter.dismiss();
|
||||
}
|
||||
} else {
|
||||
qDebug("(Skipped some CET-only tests)");
|
||||
}
|
||||
@ -4318,7 +4405,7 @@ void tst_QDateTime::range() const
|
||||
|
||||
void tst_QDateTime::macTypes()
|
||||
{
|
||||
#ifndef Q_OS_MAC
|
||||
#ifndef Q_OS_DARWIN
|
||||
QSKIP("This is a Apple-only test");
|
||||
#else
|
||||
extern void tst_QDateTime_macTypes(); // in qdatetime_mac.mm
|
||||
|
@ -507,7 +507,7 @@ void tst_QTimeZone::asBackendZone()
|
||||
void tst_QTimeZone::systemZone()
|
||||
{
|
||||
const QTimeZone zone = QTimeZone::systemTimeZone();
|
||||
QVERIFY(zone.isValid());
|
||||
QVERIFY2(zone.isValid(), "Invalid system zone setting, tests are doomed.");
|
||||
QCOMPARE(zone.id(), QTimeZone::systemTimeZoneId());
|
||||
QCOMPARE(zone, QTimeZone(QTimeZone::systemTimeZoneId()));
|
||||
// Check it behaves the same as local-time:
|
||||
@ -777,17 +777,29 @@ void tst_QTimeZone::transitionEachZone()
|
||||
|
||||
void tst_QTimeZone::checkOffset_data()
|
||||
{
|
||||
QTest::addColumn<QByteArray>("zoneName");
|
||||
QTest::addColumn<QTimeZone>("zone");
|
||||
QTest::addColumn<QDateTime>("when");
|
||||
QTest::addColumn<int>("netOffset");
|
||||
QTest::addColumn<int>("stdOffset");
|
||||
QTest::addColumn<int>("dstOffset");
|
||||
|
||||
const QTimeZone UTC = QTimeZone::UTC;
|
||||
QTest::addRow("UTC")
|
||||
<< UTC << QDate(1970, 1, 1).startOfDay(UTC) << 0 << 0 << 0;
|
||||
const auto east = QTimeZone::fromSecondsAheadOfUtc(28'800); // 8 hours
|
||||
QTest::addRow("UTC+8")
|
||||
<< east << QDate(2000, 2, 29).startOfDay(east) << 28'800 << 28'800 << 0;
|
||||
const auto west = QTimeZone::fromDurationAheadOfUtc(std::chrono::hours{-8});
|
||||
QTest::addRow("UTC-8")
|
||||
<< west << QDate(2100, 2, 28).startOfDay(west) << -28'800 << -28'800 << 0;
|
||||
|
||||
struct {
|
||||
const char *zone, *nick;
|
||||
int year, month, day, hour, min, sec;
|
||||
int std, dst;
|
||||
} table[] = {
|
||||
// Exercise the UTC-backend:
|
||||
{ "UTC", "epoch", 1970, 1, 1, 0, 0, 0, 0, 0 },
|
||||
// Zone with no transitions (QTBUG-74614, QTBUG-74666, when TZ backend uses minimal data)
|
||||
{ "Etc/UTC", "epoch", 1970, 1, 1, 0, 0, 0, 0, 0 },
|
||||
{ "Etc/UTC", "pre_int32", 1901, 12, 13, 20, 45, 51, 0, 0 },
|
||||
@ -799,38 +811,40 @@ void tst_QTimeZone::checkOffset_data()
|
||||
{ "Europe/Kyiv", "summer", 2017, 10, 27, 12, 0, 0, 2 * 3600, 3600 },
|
||||
{ "Europe/Kyiv", "winter", 2017, 10, 29, 12, 0, 0, 2 * 3600, 0 }
|
||||
};
|
||||
bool lacksRows = true;
|
||||
for (const auto &entry : table) {
|
||||
QTimeZone zone(entry.zone);
|
||||
if (zone.isValid()) {
|
||||
QTest::addRow("%s@%s", entry.zone, entry.nick)
|
||||
<< QByteArray(entry.zone)
|
||||
<< zone
|
||||
<< QDateTime(QDate(entry.year, entry.month, entry.day),
|
||||
QTime(entry.hour, entry.min, entry.sec), zone)
|
||||
<< entry.dst + entry.std << entry.std << entry.dst;
|
||||
lacksRows = false;
|
||||
} else {
|
||||
qWarning("Skipping %s@%s test as zone is invalid", entry.zone, entry.nick);
|
||||
}
|
||||
}
|
||||
if (lacksRows)
|
||||
QSKIP("No valid zone info found, skipping test");
|
||||
}
|
||||
|
||||
void tst_QTimeZone::checkOffset()
|
||||
{
|
||||
QFETCH(QByteArray, zoneName);
|
||||
QFETCH(QTimeZone, zone);
|
||||
QFETCH(QDateTime, when);
|
||||
QFETCH(int, netOffset);
|
||||
QFETCH(int, stdOffset);
|
||||
QFETCH(int, dstOffset);
|
||||
|
||||
QTimeZone zone(zoneName);
|
||||
QVERIFY(zone.isValid()); // It was when _data() added the row !
|
||||
QCOMPARE(zone.offsetFromUtc(when), netOffset);
|
||||
QCOMPARE(zone.standardTimeOffset(when), stdOffset);
|
||||
QCOMPARE(zone.daylightTimeOffset(when), dstOffset);
|
||||
QCOMPARE(zone.isDaylightTime(when), dstOffset != 0);
|
||||
|
||||
// Also test offsetData(), which gets all this data in one go:
|
||||
const auto data = zone.offsetData(when);
|
||||
QCOMPARE(data.atUtc, when);
|
||||
QCOMPARE(data.offsetFromUtc, netOffset);
|
||||
QCOMPARE(data.standardTimeOffset, stdOffset);
|
||||
QCOMPARE(data.daylightTimeOffset, dstOffset);
|
||||
}
|
||||
|
||||
void tst_QTimeZone::availableTimeZoneIds()
|
||||
@ -1162,9 +1176,9 @@ void tst_QTimeZone::utcTest()
|
||||
QCOMPARE(tz.standardTimeOffset(now), 36000);
|
||||
QCOMPARE(tz.daylightTimeOffset(now), 0);
|
||||
|
||||
// Test invalid UTC offset, must be in range -14 to +14 hours
|
||||
int min = -14*60*60;
|
||||
int max = 14*60*60;
|
||||
// Test validity range of UTC offsets:
|
||||
int min = QTimeZone::MinUtcOffsetSecs;
|
||||
int max = QTimeZone::MaxUtcOffsetSecs;
|
||||
QCOMPARE(QTimeZone(min - 1).isValid(), false);
|
||||
QCOMPARE(QTimeZone(min).isValid(), true);
|
||||
QCOMPARE(QTimeZone(min + 1).isValid(), true);
|
||||
|
Reference in New Issue
Block a user