Rework getting normalized dates adjacent to a given one

I originally wrote dateNormalize() because I expected to need it more,
but it turns out to only be needed for two cases of getting the days
before and after a given one. So rename to adjacentDay(), pass the +1
or -1 step and simplify a little.

In the process, fix a mistake in the winding backwards across a year
boundary, where I'd incremented the year instead of decrementing it.

Change-Id: I1bb0a8323fec7c1caffa7f20879f08d3526ba7ea
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Edward Welbourne 2023-09-26 14:38:16 +02:00
parent 44cbd4c315
commit 02d8dc5f8c

View File

@ -195,36 +195,41 @@ struct tm matchYearMonth(struct tm when, const struct tm &base)
}
Q_DECL_COLD_FUNCTION
struct tm dateNormalize(struct tm when)
struct tm adjacentDay(struct tm when, int dayStep)
{
int daysInMonth = 28;
// We have to wind through months one at a time, since their lengths vary.
while (when.tm_mday < 1) {
// Month before's day-count; but tm_mon's value is one less than Qt's
// month numbering so, before we decrement it, it has the value we need,
// unless it's 0.
daysInMonth = when.tm_mon
? QGregorianCalendar::monthLength(when.tm_mon, qYearFromTmYear(when.tm_year))
: QGregorianCalendar::monthLength(12, qYearFromTmYear(when.tm_year - 1));
when.tm_mday += daysInMonth;
if (--when.tm_mon < 0) {
++when.tm_year;
when.tm_mon = 11;
// Before we adjust it, when is a return from timeToTm(), so in normal form.
Q_ASSERT(dayStep * dayStep == 1);
when.tm_mday += dayStep;
// That may have bumped us across a month boundary or even a year one.
// So now we normalize it.
if (dayStep < 0) {
if (when.tm_mday <= 0) {
// Month before's day-count; but tm_mon's value is one less than Qt's
// month numbering so, before we decrement it, it has the value we need,
// unless it's 0.
int daysInMonth = when.tm_mon
? QGregorianCalendar::monthLength(when.tm_mon, qYearFromTmYear(when.tm_year))
: QGregorianCalendar::monthLength(12, qYearFromTmYear(when.tm_year - 1));
when.tm_mday += daysInMonth;
if (--when.tm_mon < 0) {
--when.tm_year;
when.tm_mon = 11;
}
Q_ASSERT(when.tm_mday >= 1);
}
}
// If we came via that loop, we have set daysInMonth and when.tm_mday is <= it.
// Otherwise, daysInMonth is 28, so this serves as a cheap pre-test:
if (when.tm_mday > daysInMonth) {
daysInMonth = QGregorianCalendar::monthLength(
} else if (when.tm_mday > 28) {
// We have to wind through months one at a time, since their lengths vary.
int daysInMonth = QGregorianCalendar::monthLength(
when.tm_mon + 1, qYearFromTmYear(when.tm_year));
while (when.tm_mday > daysInMonth) {
if (when.tm_mday > daysInMonth) {
when.tm_mday -= daysInMonth;
if (++when.tm_mon > 11) {
++when.tm_year;
when.tm_mon = 0;
}
daysInMonth = QGregorianCalendar::monthLength(
when.tm_mon + 1, qYearFromTmYear(when.tm_year));
Q_ASSERT(when.tm_mday <= QGregorianCalendar::monthLength(
when.tm_mon + 1, qYearFromTmYear(when.tm_year)));
}
}
return when;
@ -278,12 +283,8 @@ MkTimeResult resolveRejected(struct tm base, MkTimeResult result,
constexpr time_t twoDaysInSeconds = 2 * 24 * 60 * 60;
// Bracket base, one day each side (in case the zone skipped a whole day):
struct tm adjust = base;
--adjust.tm_mday;
MkTimeResult early(dateNormalize(adjust));
adjust = base;
++adjust.tm_mday;
MkTimeResult later(dateNormalize(adjust));
MkTimeResult early(adjacentDay(base, -1));
MkTimeResult later(adjacentDay(base, +1));
if (!early.good || !later.good) // Assume out of range, rather than gap.
return {};