Fix recurrence rule expansion with ordered weekday
When the order of a weekday in BYDAY rule expansion (like -5SA or 5SU for monthly or 55WE for yearly) exceeds the number of available weekdays in the period (month or year), rule expansion with negative order could result in a floating point exception. The reason: the modified frequency might become zero. Solution. Check order against number of available weekdays and terminate expansion early whenever possible (also for positive orders). Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk> Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
This commit is contained in:
parent
fabacecd16
commit
bd238bfd7c
65
src/recur.c
65
src/recur.c
@ -1173,7 +1173,7 @@ static int expand_monthly(time_t start, long dur, struct rpt *rpt, llist_t *exc,
|
|||||||
LLIST_FOREACH(&rpt->bywday, i) {
|
LLIST_FOREACH(&rpt->bywday, i) {
|
||||||
w = LLIST_GET_DATA(i);
|
w = LLIST_GET_DATA(i);
|
||||||
|
|
||||||
int order, wday;
|
int order, wday, nbwd;
|
||||||
|
|
||||||
localtime_r(&start, &tm_start);
|
localtime_r(&start, &tm_start);
|
||||||
/*
|
/*
|
||||||
@ -1188,6 +1188,11 @@ static int expand_monthly(time_t start, long dur, struct rpt *rpt, llist_t *exc,
|
|||||||
*/
|
*/
|
||||||
order = *w / WEEKINDAYS;
|
order = *w / WEEKINDAYS;
|
||||||
wday = *w % WEEKINDAYS;
|
wday = *w % WEEKINDAYS;
|
||||||
|
nbwd = wday_per_month(tm_day.tm_mon + 1,
|
||||||
|
tm_day.tm_year + 1900,
|
||||||
|
wday);
|
||||||
|
if (nbwd < order)
|
||||||
|
return 0;
|
||||||
r.freq = order;
|
r.freq = order;
|
||||||
tm_start.tm_mday = 1;
|
tm_start.tm_mday = 1;
|
||||||
tm_start.tm_mon = tm_day.tm_mon;
|
tm_start.tm_mon = tm_day.tm_mon;
|
||||||
@ -1218,11 +1223,12 @@ static int expand_monthly(time_t start, long dur, struct rpt *rpt, llist_t *exc,
|
|||||||
*/
|
*/
|
||||||
order = -(*w) / WEEKINDAYS;
|
order = -(*w) / WEEKINDAYS;
|
||||||
wday = -(*w) % WEEKINDAYS;
|
wday = -(*w) % WEEKINDAYS;
|
||||||
r.freq = wday_per_month(
|
nbwd = wday_per_month(tm_day.tm_mon + 1,
|
||||||
tm_day.tm_mon + 1,
|
tm_day.tm_year + 1900,
|
||||||
tm_day.tm_year + 1900,
|
wday);
|
||||||
wday
|
if (nbwd < order)
|
||||||
) - order + 1;
|
return 0;
|
||||||
|
r.freq = nbwd - order + 1;
|
||||||
tm_start.tm_mday = 1;
|
tm_start.tm_mday = 1;
|
||||||
tm_start.tm_mon = tm_day.tm_mon;
|
tm_start.tm_mon = tm_day.tm_mon;
|
||||||
tm_start.tm_year = tm_day.tm_year;
|
tm_start.tm_year = tm_day.tm_year;
|
||||||
@ -1259,7 +1265,7 @@ static int expand_yearly(time_t start, long dur, struct rpt *rpt, llist_t *exc,
|
|||||||
{
|
{
|
||||||
struct tm tm_start, tm_day;
|
struct tm tm_start, tm_day;
|
||||||
llist_item_t *i, *j;
|
llist_item_t *i, *j;
|
||||||
int *m, *w, mday, wday, order;
|
int *m, *w, mday, wday, order, nbwd;
|
||||||
time_t nstart;
|
time_t nstart;
|
||||||
struct rpt r;
|
struct rpt r;
|
||||||
|
|
||||||
@ -1312,6 +1318,19 @@ static int expand_yearly(time_t start, long dur, struct rpt *rpt, llist_t *exc,
|
|||||||
*/
|
*/
|
||||||
order = *w / WEEKINDAYS;
|
order = *w / WEEKINDAYS;
|
||||||
wday = *w % WEEKINDAYS;
|
wday = *w % WEEKINDAYS;
|
||||||
|
if (rpt->bymonth.head)
|
||||||
|
nbwd = wday_per_month(
|
||||||
|
tm_day.tm_mon + 1,
|
||||||
|
tm_day.tm_year + 1900,
|
||||||
|
wday
|
||||||
|
);
|
||||||
|
else
|
||||||
|
nbwd = wday_per_year(
|
||||||
|
tm_day.tm_year + 1900,
|
||||||
|
wday
|
||||||
|
);
|
||||||
|
if (nbwd < order)
|
||||||
|
return 0;
|
||||||
r.freq = order;
|
r.freq = order;
|
||||||
tm_start.tm_mday = 1;
|
tm_start.tm_mday = 1;
|
||||||
if (rpt->bymonth.head)
|
if (rpt->bymonth.head)
|
||||||
@ -1344,21 +1363,25 @@ static int expand_yearly(time_t start, long dur, struct rpt *rpt, llist_t *exc,
|
|||||||
*/
|
*/
|
||||||
order = -(*w) / WEEKINDAYS;
|
order = -(*w) / WEEKINDAYS;
|
||||||
wday = -(*w) % WEEKINDAYS;
|
wday = -(*w) % WEEKINDAYS;
|
||||||
if (rpt->bymonth.head) {
|
if (rpt->bymonth.head)
|
||||||
r.freq = wday_per_month(
|
nbwd = wday_per_month(
|
||||||
tm_day.tm_mon + 1,
|
tm_day.tm_mon + 1,
|
||||||
tm_day.tm_year + 1900,
|
tm_day.tm_year + 1900,
|
||||||
wday
|
wday
|
||||||
) - order + 1;
|
);
|
||||||
tm_start.tm_mon = tm_day.tm_mon;
|
else
|
||||||
} else {
|
nbwd = wday_per_year(
|
||||||
r.freq = wday_per_year(
|
tm_day.tm_year + 1900,
|
||||||
tm_day.tm_year + 1900,
|
wday
|
||||||
wday
|
);
|
||||||
) - order + 1;
|
if (nbwd < order)
|
||||||
tm_start.tm_mon = 0;
|
return 0;
|
||||||
}
|
r.freq = nbwd - order + 1;
|
||||||
tm_start.tm_mday = 1;
|
tm_start.tm_mday = 1;
|
||||||
|
if (rpt->bymonth.head)
|
||||||
|
tm_start.tm_mon = tm_day.tm_mon;
|
||||||
|
else
|
||||||
|
tm_start.tm_mon = 0;
|
||||||
tm_start.tm_year = tm_day.tm_year;
|
tm_start.tm_year = tm_day.tm_year;
|
||||||
tm_start.tm_isdst = -1;
|
tm_start.tm_isdst = -1;
|
||||||
nstart = date_sec_change(
|
nstart = date_sec_change(
|
||||||
|
@ -25,3 +25,4 @@
|
|||||||
12/25/1997 [1] {3Y w-11} Last Thursday of the year every third year, forever (FREQ=YEARLY;INTERVAL=3;BYDAY=-1TH)
|
12/25/1997 [1] {3Y w-11} Last Thursday of the year every third year, forever (FREQ=YEARLY;INTERVAL=3;BYDAY=-1TH)
|
||||||
06/22/1997 [1] {2Y w0 w1} Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
|
06/22/1997 [1] {2Y w0 w1} Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
|
||||||
02/01/1997 [1] {1Y -> 01/31/2005 d1 d29 m2} Every year on February 1 and 29 for eight years (RRULE:FREQ=YEARLY;UNTIL=20050131T000000Z;BYMONTH=2;BYMONTHDAY=1,29)
|
02/01/1997 [1] {1Y -> 01/31/2005 d1 d29 m2} Every year on February 1 and 29 for eight years (RRULE:FREQ=YEARLY;UNTIL=20050131T000000Z;BYMONTH=2;BYMONTHDAY=1,29)
|
||||||
|
08/01/2020 @ 08:41 -> 08/01/2020 @ 10:11 {1M w-41} |negative ordered weekday may not exist
|
||||||
|
@ -6,6 +6,13 @@
|
|||||||
if [ "$1" = 'actual' ]; then
|
if [ "$1" = 'actual' ]; then
|
||||||
"$CALCURSE" --read-only -D "$DATA_DIR"/ -c "$DATA_DIR/rfc5545" \
|
"$CALCURSE" --read-only -D "$DATA_DIR"/ -c "$DATA_DIR/rfc5545" \
|
||||||
-Q --from 1/1/1996 --to 12/31/2007 --filter-type recur
|
-Q --from 1/1/1996 --to 12/31/2007 --filter-type recur
|
||||||
|
echo ""
|
||||||
|
echo "Floating point exception?"
|
||||||
|
"$CALCURSE" --read-only -D "$DATA_DIR"/ -c "$DATA_DIR/rfc5545" \
|
||||||
|
-Q --day 8/1/2020 --filter-type recur &&
|
||||||
|
"$CALCURSE" --read-only -D "$DATA_DIR"/ -c "$DATA_DIR/rfc5545" \
|
||||||
|
-Q --day 11/1/2020 --filter-type recur &&
|
||||||
|
echo "No Floating point exception on November 1, 2020"
|
||||||
elif [ "$1" = 'expected' ]; then
|
elif [ "$1" = 'expected' ]; then
|
||||||
cat <<EOD
|
cat <<EOD
|
||||||
11/05/96:
|
11/05/96:
|
||||||
@ -5323,6 +5330,12 @@ elif [ "$1" = 'expected' ]; then
|
|||||||
|
|
||||||
12/31/07:
|
12/31/07:
|
||||||
* Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
|
* Every Sunday and Monday, every other year (FREQ=YEARLY;INTERVAL=2;BYDAY=SU,MO)
|
||||||
|
|
||||||
|
Floating point exception?
|
||||||
|
08/01/20:
|
||||||
|
- 08:41 -> 10:11
|
||||||
|
negative ordered weekday may not exist
|
||||||
|
No Floating point exception on November 1, 2020
|
||||||
EOD
|
EOD
|
||||||
else
|
else
|
||||||
./run-test "$0"
|
./run-test "$0"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user