1
0
mirror of https://github.com/systemd/systemd.git synced 2025-03-21 02:50:18 +03:00

calendarspec: "«ts» and every «timespan»" DST handling

The compensation of daylight saving shifts for repetitions that are
multiples of whole days only. Events occur at the same day-hour of
localtime in that case. The special case is non existing time during
forward jump.
This commit is contained in:
Václav Ovsík 2025-03-09 12:39:45 +01:00
parent d9833dea7a
commit 968b9ee66e

View File

@ -1448,6 +1448,67 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) {
"Infinite loop in calendar calculation: %s", strna(s));
}
static int usec_dstoffset_sec(usec_t usec, time_t *ret_dstoffset) {
struct tm tm;
time_t t = usec / USEC_PER_SEC;
if ( !localtime_r(&t, &tm) )
return -EINVAL;
if ( tm.tm_isdst) {
tm.tm_isdst = 0;
time_t tmp = mktime(&tm);
if ( tmp == -1 )
return -EINVAL;
*ret_dstoffset = tmp - t;
} else {
*ret_dstoffset = 0;
}
return 0;
}
static int calendar_spec_next_usec_period(const CalendarSpec *spec, usec_t usec, usec_t *ret_next) {
usec_t ts;
int r = calendarspec_to_usec_t(spec, &ts);
if (r < 0)
return r;
if (usec < ts) {
/* The timestamp is in the future. The first event of the period. */
*ret_next = ts;
return 0;
}
if ( spec->period % (24 * 3600 * USEC_PER_SEC) ) {
/* The period is not multiple of the whole day.
* The next event is on the multiple of the period duration. */
*ret_next = ts + ( (usec - ts) / spec->period +1 ) * spec->period;
return 0;
}
/* The period is multiple of the whole day. We need to compensate daylight
* saving. The repeating events should be at the same time of day according to
* daylight saving. We need to test up to three points around and detect
* localtime discontinuity on change of DST/Non DST localtime mode. */
time_t dstoffset;
if ( (r = usec_dstoffset_sec(ts, &dstoffset)) )
return r;
const size_t NRT = 3;
size_t i;
usec_t u = ts + ( (usec - ts) / spec->period ) * spec->period;
for(i = 0; i < NRT; i++) {
time_t dstoffset2;
r = usec_dstoffset_sec(u, &dstoffset2);
if (r < 0)
return r;
u += ( dstoffset - dstoffset2 ) * USEC_PER_SEC;
if ( usec < u ) {
*ret_next = u;
return 0;
}
dstoffset = dstoffset2;
u += spec->period;
}
return -EINVAL;
}
static int calendar_spec_next_usec_impl(const CalendarSpec *spec, usec_t usec, usec_t *ret_next) {
usec_t tm_usec;
struct tm tm;
@ -1458,17 +1519,8 @@ static int calendar_spec_next_usec_impl(const CalendarSpec *spec, usec_t usec, u
if (usec > USEC_TIMESTAMP_FORMATTABLE_MAX)
return -EINVAL;
if ( spec->period ) {
usec_t ts;
r = calendarspec_to_usec_t(spec, &ts);
if (r < 0)
return r;
if (usec < ts)
*ret_next = ts;
else
*ret_next = ts + (( (usec - ts) / spec->period ) +1) * spec->period;
return 0;
}
if ( spec->period )
return calendar_spec_next_usec_period(spec, usec, ret_next);
usec++;
r = localtime_or_gmtime_usec(usec, spec->utc, &tm);