Icinga / icinga2

The core of our monitoring platform with a powerful configuration language and REST API.
https://icinga.com/docs/icinga2/latest
GNU General Public License v2.0
2.03k stars 578 forks source link

Timeperiods: fix off by one when calculating n-th last weekday of the month #10107

Closed julianbrost closed 3 months ago

julianbrost commented 3 months ago

A day specification like "monday -1" refers to the last Monday of the month. However, there was an off by one if the first day of the next month is the same day of the week, i.e. a Monday in this example.

LegacyTimePeriod::FindNthWeekday() picks a day to start the search for the day in question. When given a negative n to search for the n-th last day, it wrongly used the first day of the following month as the start and counted it as if it was within the current month. This resulted in a 1/7 chance that the result was one week too late.

This is fixed by using the last day of the current month instead.

fixes #7061

julianbrost commented 3 months ago

Result of the new test case when reverting the change to lib/icinga/legacytimeperiod.cpp (omitted empty lines and many days in the middle, the same pattern repeats 31 times):

test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-01, wday=1, n=1] expected: 2019-03-04 00:00:00 , got: 2019-03-04 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-01, wday=1, n=2] expected: 2019-03-11 00:00:00 , got: 2019-03-11 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-01, wday=1, n=3] expected: 2019-03-18 00:00:00 , got: 2019-03-18 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-01, wday=1, n=4] expected: 2019-03-25 00:00:00 , got: 2019-03-25 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): error: in "icinga_legacytimeperiod/find_nth_weekday": [ref=2019-03-01, wday=1, n=-1] expected: 2019-03-25 00:00:00 , got: 2019-04-01 00:00:00
test/icinga-legacytimeperiod.cpp(701): error: in "icinga_legacytimeperiod/find_nth_weekday": [ref=2019-03-01, wday=1, n=-2] expected: 2019-03-18 00:00:00 , got: 2019-03-25 00:00:00
test/icinga-legacytimeperiod.cpp(701): error: in "icinga_legacytimeperiod/find_nth_weekday": [ref=2019-03-01, wday=1, n=-3] expected: 2019-03-11 00:00:00 , got: 2019-03-18 00:00:00 
test/icinga-legacytimeperiod.cpp(701): error: in "icinga_legacytimeperiod/find_nth_weekday": [ref=2019-03-01, wday=1, n=-4] expected: 2019-03-04 00:00:00 , got: 2019-03-11 00:00:00 
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-01, wday=5, n=1] expected: 2019-03-01 00:00:00 , got: 2019-03-01 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-01, wday=5, n=2] expected: 2019-03-08 00:00:00 , got: 2019-03-08 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-01, wday=5, n=3] expected: 2019-03-15 00:00:00 , got: 2019-03-15 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-01, wday=5, n=4] expected: 2019-03-22 00:00:00 , got: 2019-03-22 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-01, wday=5, n=5] expected: 2019-03-29 00:00:00 , got: 2019-03-29 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-01, wday=5, n=-1] expected: 2019-03-29 00:00:00 , got: 2019-03-29 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-01, wday=5, n=-2] expected: 2019-03-22 00:00:00 , got: 2019-03-22 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-01, wday=5, n=-3] expected: 2019-03-15 00:00:00 , got: 2019-03-15 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-01, wday=5, n=-4] expected: 2019-03-08 00:00:00 , got: 2019-03-08 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-01, wday=5, n=-5] expected: 2019-03-01 00:00:00 , got: 2019-03-01 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-02, wday=1, n=1] expected: 2019-03-04 00:00:00 , got: 2019-03-04 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-02, wday=1, n=2] expected: 2019-03-11 00:00:00 , got: 2019-03-11 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-02, wday=1, n=3] expected: 2019-03-18 00:00:00 , got: 2019-03-18 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-02, wday=1, n=4] expected: 2019-03-25 00:00:00 , got: 2019-03-25 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): error: in "icinga_legacytimeperiod/find_nth_weekday": [ref=2019-03-02, wday=1, n=-1] expected: 2019-03-25 00:00:00 , got: 2019-04-01 00:00:00
test/icinga-legacytimeperiod.cpp(701): error: in "icinga_legacytimeperiod/find_nth_weekday": [ref=2019-03-02, wday=1, n=-2] expected: 2019-03-18 00:00:00 , got: 2019-03-25 00:00:00
test/icinga-legacytimeperiod.cpp(701): error: in "icinga_legacytimeperiod/find_nth_weekday": [ref=2019-03-02, wday=1, n=-3] expected: 2019-03-11 00:00:00 , got: 2019-03-18 00:00:00 
test/icinga-legacytimeperiod.cpp(701): error: in "icinga_legacytimeperiod/find_nth_weekday": [ref=2019-03-02, wday=1, n=-4] expected: 2019-03-04 00:00:00 , got: 2019-03-11 00:00:00 
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-02, wday=5, n=1] expected: 2019-03-01 00:00:00 , got: 2019-03-01 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-02, wday=5, n=2] expected: 2019-03-08 00:00:00 , got: 2019-03-08 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-02, wday=5, n=3] expected: 2019-03-15 00:00:00 , got: 2019-03-15 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-02, wday=5, n=4] expected: 2019-03-22 00:00:00 , got: 2019-03-22 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-02, wday=5, n=5] expected: 2019-03-29 00:00:00 , got: 2019-03-29 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-02, wday=5, n=-1] expected: 2019-03-29 00:00:00 , got: 2019-03-29 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-02, wday=5, n=-2] expected: 2019-03-22 00:00:00 , got: 2019-03-22 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-02, wday=5, n=-3] expected: 2019-03-15 00:00:00 , got: 2019-03-15 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-02, wday=5, n=-4] expected: 2019-03-08 00:00:00 , got: 2019-03-08 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-02, wday=5, n=-5] expected: 2019-03-01 00:00:00 , got: 2019-03-01 00:00:00 ' has passed
[...]
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-31, wday=1, n=1] expected: 2019-03-04 00:00:00 , got: 2019-03-04 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-31, wday=1, n=2] expected: 2019-03-11 00:00:00 , got: 2019-03-11 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-31, wday=1, n=3] expected: 2019-03-18 00:00:00 , got: 2019-03-18 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-31, wday=1, n=4] expected: 2019-03-25 00:00:00 , got: 2019-03-25 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): error: in "icinga_legacytimeperiod/find_nth_weekday": [ref=2019-03-31, wday=1, n=-1] expected: 2019-03-25 00:00:00 , got: 2019-04-01 00:00:00 
test/icinga-legacytimeperiod.cpp(701): error: in "icinga_legacytimeperiod/find_nth_weekday": [ref=2019-03-31, wday=1, n=-2] expected: 2019-03-18 00:00:00 , got: 2019-03-25 00:00:00 
test/icinga-legacytimeperiod.cpp(701): error: in "icinga_legacytimeperiod/find_nth_weekday": [ref=2019-03-31, wday=1, n=-3] expected: 2019-03-11 00:00:00 , got: 2019-03-18 00:00:00 
test/icinga-legacytimeperiod.cpp(701): error: in "icinga_legacytimeperiod/find_nth_weekday": [ref=2019-03-31, wday=1, n=-4] expected: 2019-03-04 00:00:00 , got: 2019-03-11 00:00:00 
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-31, wday=5, n=1] expected: 2019-03-01 00:00:00 , got: 2019-03-01 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-31, wday=5, n=2] expected: 2019-03-08 00:00:00 , got: 2019-03-08 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-31, wday=5, n=3] expected: 2019-03-15 00:00:00 , got: 2019-03-15 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-31, wday=5, n=4] expected: 2019-03-22 00:00:00 , got: 2019-03-22 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-31, wday=5, n=5] expected: 2019-03-29 00:00:00 , got: 2019-03-29 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-31, wday=5, n=-1] expected: 2019-03-29 00:00:00 , got: 2019-03-29 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-31, wday=5, n=-2] expected: 2019-03-22 00:00:00 , got: 2019-03-22 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-31, wday=5, n=-3] expected: 2019-03-15 00:00:00 , got: 2019-03-15 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-31, wday=5, n=-4] expected: 2019-03-08 00:00:00 , got: 2019-03-08 00:00:00 ' has passed
test/icinga-legacytimeperiod.cpp(701): info: check '[ref=2019-03-31, wday=5, n=-5] expected: 2019-03-01 00:00:00 , got: 2019-03-01 00:00:00 ' has passed