From 34287c9eae9398981c7270adb7b9f49842c75c06 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Fri, 5 Nov 2021 19:30:06 +0100 Subject: [PATCH] Add QGregorianCalendar::yearSharingWeekDays() When fixing up problems on system APIs with limited date ranges, we need a year, inside the supported date range, in which Gregorian dates fell on the same days of the week as a given year outside the range. A year with the same last two digits makes handling of two-digit year formats easier. Change-Id: If64ee27e829f9dcfd5504ed8ba51f72c36297242 Reviewed-by: Thiago Macieira --- src/corelib/time/qgregoriancalendar.cpp | 26 +++++++++++++++++++++++++ src/corelib/time/qgregoriancalendar_p.h | 1 + 2 files changed, 27 insertions(+) diff --git a/src/corelib/time/qgregoriancalendar.cpp b/src/corelib/time/qgregoriancalendar.cpp index 2089ea7e97b..9a044d2a261 100644 --- a/src/corelib/time/qgregoriancalendar.cpp +++ b/src/corelib/time/qgregoriancalendar.cpp @@ -146,6 +146,32 @@ int QGregorianCalendar::yearStartWeekDay(int year) return qMod(y + qDiv(y, 4) - qDiv(y, 100) + qDiv(y, 400), 7) + 1; } +int QGregorianCalendar::yearSharingWeekDays(int year) +{ + // Returns a year, in the four-digit post-epoch range, that has the same + // pattern of week-days (in the proleptic Gregorian calendar) as the given + // year. For positive years, the last two decimal digits of the returned + // year shall also match those of the given year. + + // Needed when formatting dates using system APIs with limited year ranges + // and possibly only a two-digit year. Fixing the negative year case is left + // as an exercise for the day a real user actually has a problem with it. + + static_assert((400 * 365 + 97) % 7 == 0); + // A full 400-year cycle of the Gregorian calendar has 97 + 400 * 365 days; + // as 365 is one more than a multiple of seven and 497 is a multiple of + // seven, that full cycle is a whole number of weeks. So adding a multiple + // of four hundred years should get us a result that meets our needs. + + const int res = (year < 1970 + ? 2400 - (2000 - (year < 0 ? year + 1 : year)) % 400 + : year > 9999 ? 2000 + (year - 2000) % 400 : year); + Q_ASSERT(res % 100 == (year > 0 ? year % 100 : year < -1 ? 100 + (year + 1) % 100 : 0)); + Q_ASSERT(QDate(res, 1, 1).dayOfWeek() == QDate(year, 1, 1).dayOfWeek()); + Q_ASSERT(QDate(res, 12, 31).dayOfWeek() == QDate(year, 12, 31).dayOfWeek()); + return res; +} + QCalendar::YearMonthDay QGregorianCalendar::julianDayToDate(qint64 jd) const { return partsFromJulian(jd); diff --git a/src/corelib/time/qgregoriancalendar_p.h b/src/corelib/time/qgregoriancalendar_p.h index 66e15bbc563..e05e4d237fe 100644 --- a/src/corelib/time/qgregoriancalendar_p.h +++ b/src/corelib/time/qgregoriancalendar_p.h @@ -84,6 +84,7 @@ public: static bool julianFromParts(int year, int month, int day, qint64 *jd); // Used internally: static int yearStartWeekDay(int year); + static int yearSharingWeekDays(int year); }; QT_END_NAMESPACE