1
0
mirror of https://github.com/systemd/systemd.git synced 2024-10-30 14:55:37 +03:00

calendarspec: fix possibly skips next elapse

If the time unit changes after adding the repetition value, the
timer may skip the next elapse. This patch reset sub time units
to minimum value when upper unit is changed.

Fixes #22665.
This commit is contained in:
Gibeom Gwon 2022-03-06 09:45:38 +09:00 committed by Zbigniew Jędrzejewski-Szmek
parent cbb6068d0f
commit 1e582ede3b
2 changed files with 22 additions and 7 deletions

View File

@ -1181,6 +1181,7 @@ static int find_matching_component(
static int tm_within_bounds(struct tm *tm, bool utc) {
struct tm t;
int cmp;
assert(tm);
/*
@ -1195,13 +1196,25 @@ static int tm_within_bounds(struct tm *tm, bool utc) {
if (mktime_or_timegm(&t, utc) < 0)
return negative_errno();
/* Did any normalization take place? If so, it was out of bounds before */
int cmp = CMP(t.tm_year, tm->tm_year) ?:
CMP(t.tm_mon, tm->tm_mon) ?:
CMP(t.tm_mday, tm->tm_mday) ?:
CMP(t.tm_hour, tm->tm_hour) ?:
CMP(t.tm_min, tm->tm_min) ?:
CMP(t.tm_sec, tm->tm_sec);
/*
* Did any normalization take place? If so, it was out of bounds before.
* Normalization could skip next elapse, e.g. result of normalizing 3-33
* is 4-2. This skips 4-1. So reset the sub time unit if upper unit was
* out of bounds. Normalization has occurred implies find_matching_component() > 0,
* other sub time units are already reset in find_next().
*/
if ((cmp = CMP(t.tm_year, tm->tm_year)) != 0)
t.tm_mon = 0;
else if ((cmp = CMP(t.tm_mon, tm->tm_mon)) != 0)
t.tm_mday = 1;
else if ((cmp = CMP(t.tm_mday, tm->tm_mday)) != 0)
t.tm_hour = 0;
else if ((cmp = CMP(t.tm_hour, tm->tm_hour)) != 0)
t.tm_min = 0;
else if ((cmp = CMP(t.tm_min, tm->tm_min)) != 0)
t.tm_sec = 0;
else
cmp = CMP(t.tm_sec, tm->tm_sec);
if (cmp < 0)
return -EDEADLK; /* Refuse to go backward */

View File

@ -199,6 +199,8 @@ TEST(calendar_spec_next) {
test_next("2016-02~01 UTC", "", 12345, 1456704000000000);
test_next("Mon 2017-05~01..07 UTC", "", 12345, 1496016000000000);
test_next("Mon 2017-05~07/1 UTC", "", 12345, 1496016000000000);
test_next("*-*-01/5 04:00:00 UTC", "", 1646010000000000, 1646107200000000);
test_next("*-01/7-01 04:00:00 UTC", "", 1664607600000000, 1672545600000000);
test_next("2017-08-06 9,11,13,15,17:00 UTC", "", 1502029800000000, 1502031600000000);
test_next("2017-08-06 9..17/2:00 UTC", "", 1502029800000000, 1502031600000000);
test_next("2016-12-* 3..21/6:00 UTC", "", 1482613200000001, 1482634800000000);