diff --git a/src/corelib/time/qtimezoneprivate_tz.cpp b/src/corelib/time/qtimezoneprivate_tz.cpp index 2955a5c957..8f02e3a7d6 100644 --- a/src/corelib/time/qtimezoneprivate_tz.cpp +++ b/src/corelib/time/qtimezoneprivate_tz.cpp @@ -393,19 +393,22 @@ static QDate calculatePosixDate(const QByteArray &dateRule, int year) return calculateDowDate(year, month, dow, week); } } else if (dateRule.at(0) == 'J') { - // Day of Year ignores Feb 29 + // Day of Year 1...365, ignores Feb 29. + // So March always starts on day 60. int doy = dateRule.mid(1).toInt(&ok); if (ok && doy > 0 && doy < 366) { - QDate date = QDate(year, 1, 1).addDays(doy - 1); - if (QDate::isLeapYear(date.year()) && date.month() > 2) - date = date.addDays(-1); - return date; + // Subtract 1 because we're adding days *after* the first of + // January, unless it's after February in a leap year, when the leap + // day cancels that out: + if (!QDate::isLeapYear(year) || doy < 60) + --doy; + return QDate(year, 1, 1).addDays(doy); } } else { - // Day of Year includes Feb 29 + // Day of Year 0...365, includes Feb 29 int doy = dateRule.toInt(&ok); - if (ok && doy > 0 && doy <= 366) - return QDate(year, 1, 1).addDays(doy - 1); + if (ok && doy >= 0 && doy < 366) + return QDate(year, 1, 1).addDays(doy); } return QDate(); } diff --git a/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp b/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp index 2e197cca4a..19b638cce7 100644 --- a/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp +++ b/tests/auto/corelib/time/qtimezone/tst_qtimezone.cpp @@ -1141,6 +1141,18 @@ void tst_QTimeZone::tzTest() QTzTimeZonePrivate tzposix("MET-1METDST-2,M3.5.0/02:00:00,M10.5.0/03:00:00"); QVERIFY(tzposix.isValid()); + // RHEL has been seen with this as Africa/Casablanca's POSIX rule: + QTzTimeZonePrivate permaDst("<+00>0<+01>,0/0,J365/25"); + const QTimeZone utcP1("UTC+01:00"); // Should always have same offset as permaDst + QVERIFY(permaDst.isValid()); + QVERIFY(permaDst.hasDaylightTime()); + QVERIFY(permaDst.isDaylightTime(QDate(2020, 1, 1).startOfDay(utcP1).toMSecsSinceEpoch())); + QVERIFY(permaDst.isDaylightTime(QDate(2020, 12, 31).endOfDay(utcP1).toMSecsSinceEpoch())); + // Note that the final /25 could be misunderstood as putting a fall-back at + // 1am on the next year's Jan 1st; check we don't do that: + QVERIFY(permaDst.isDaylightTime( + QDateTime(QDate(2020, 1, 1), QTime(1, 30), utcP1).toMSecsSinceEpoch())); + QTimeZone tzBrazil("BRT+3"); // parts of Northern Brazil, as a POSIX rule QVERIFY(tzBrazil.isValid()); QCOMPARE(tzBrazil.offsetFromUtc(QDateTime(QDate(1111, 11, 11).startOfDay())), -10800);