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:
parent
d9833dea7a
commit
968b9ee66e
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user