diff options
Diffstat (limited to 'src/recur.c')
-rw-r--r-- | src/recur.c | 167 |
1 files changed, 123 insertions, 44 deletions
diff --git a/src/recur.c b/src/recur.c index 997fb15..12f76b8 100644 --- a/src/recur.c +++ b/src/recur.c @@ -1,7 +1,7 @@ /* * Calcurse - text-based organizer * - * Copyright (c) 2004-2020 calcurse Development Team <misc@calcurse.org> + * Copyright (c) 2004-2023 calcurse Development Team <misc@calcurse.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -517,9 +517,10 @@ char *recur_apoint_scan(FILE *f, struct tm start, struct tm end, /* Does it occur on the start day? */ if (!recur_item_find_occurrence(tstart, tend - tstart, rpt, NULL, - update_time_in_date(tstart, 0, 0), - NULL)) - return _("recurrence error: not on start day"); + DAY(tstart), NULL)) { + char *fmt = _("recurrence error: not on start day (%s)"); + return day_ins(&fmt, tstart); + } /* Filter item. */ if (filter) { @@ -588,9 +589,10 @@ char *recur_event_scan(FILE * f, struct tm start, int id, /* Does it occur on the start day? */ if (!recur_item_find_occurrence(tstart, -1, rpt, NULL, - update_time_in_date(tstart, 0, 0), - NULL)) - return _("recurrence error: not on start day"); + DAY(tstart), NULL)) { + char *fmt = _("recurrence error: not on start day (%s)"); + return day_ins(&fmt, tstart); + } /* Filter item. */ if (filter) { @@ -1001,14 +1003,20 @@ static int find_occurrence(time_t start, long dur, struct rpt *rpt, llist_t *exc if (rpt->until && t >= NEXTDAY(rpt->until)) return 0; - /* Does it span the given day? */ - if (t + DUR(t) < day) + /* Does it span the given day? + * + * NOTE: An appointment ending at 00:00 is not considered to span the + * given day, unless the appointment is an appointment without + * specified end time, which is internally treated as appointment with + * duration 0. + */ + if (t + DUR(t) >= day || (t == day && dur == 0)) { + if (occurrence) + *occurrence = t; + return 1; + } else { return 0; - - if (occurrence) - *occurrence = t; - - return 1; + } } #undef DUR @@ -1173,7 +1181,7 @@ static int expand_monthly(time_t start, long dur, struct rpt *rpt, llist_t *exc, LLIST_FOREACH(&rpt->bywday, i) { w = LLIST_GET_DATA(i); - int order, wday; + int order, wday, nbwd; localtime_r(&start, &tm_start); /* @@ -1188,6 +1196,11 @@ static int expand_monthly(time_t start, long dur, struct rpt *rpt, llist_t *exc, */ order = *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; tm_start.tm_mday = 1; tm_start.tm_mon = tm_day.tm_mon; @@ -1200,7 +1213,7 @@ static int expand_monthly(time_t start, long dur, struct rpt *rpt, llist_t *exc, -WEEKINDAYS ); r.until = date_sec_change( - update_time_in_date(nstart, 0, 0), + DAY(nstart), 0, r.freq * WEEKINDAYS ); @@ -1218,11 +1231,12 @@ static int expand_monthly(time_t start, long dur, struct rpt *rpt, llist_t *exc, */ order = -(*w) / WEEKINDAYS; wday = -(*w) % WEEKINDAYS; - r.freq = wday_per_month( - tm_day.tm_mon + 1, - tm_day.tm_year + 1900, - wday - ) - order + 1; + nbwd = wday_per_month(tm_day.tm_mon + 1, + tm_day.tm_year + 1900, + wday); + if (nbwd < order) + return 0; + r.freq = nbwd - order + 1; tm_start.tm_mday = 1; tm_start.tm_mon = tm_day.tm_mon; tm_start.tm_year = tm_day.tm_year; @@ -1233,7 +1247,7 @@ static int expand_monthly(time_t start, long dur, struct rpt *rpt, llist_t *exc, -WEEKINDAYS ); r.until = date_sec_change( - update_time_in_date(nstart, 0, 0), + DAY(nstart), 0, r.freq * WEEKINDAYS ); @@ -1259,7 +1273,7 @@ static int expand_yearly(time_t start, long dur, struct rpt *rpt, llist_t *exc, { struct tm tm_start, tm_day; llist_item_t *i, *j; - int *m, *w, mday, wday, order; + int *m, *w, mday, wday, order, nbwd; time_t nstart; struct rpt r; @@ -1312,6 +1326,19 @@ static int expand_yearly(time_t start, long dur, struct rpt *rpt, llist_t *exc, */ order = *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; tm_start.tm_mday = 1; if (rpt->bymonth.head) @@ -1326,7 +1353,7 @@ static int expand_yearly(time_t start, long dur, struct rpt *rpt, llist_t *exc, -WEEKINDAYS ); r.until = date_sec_change( - update_time_in_date(nstart, 0, 0), + DAY(nstart), 0, r.freq * WEEKINDAYS ); @@ -1344,21 +1371,25 @@ static int expand_yearly(time_t start, long dur, struct rpt *rpt, llist_t *exc, */ order = -(*w) / WEEKINDAYS; wday = -(*w) % WEEKINDAYS; - if (rpt->bymonth.head) { - r.freq = wday_per_month( - tm_day.tm_mon + 1, - tm_day.tm_year + 1900, - wday - ) - order + 1; + 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 = nbwd - order + 1; + tm_start.tm_mday = 1; + if (rpt->bymonth.head) tm_start.tm_mon = tm_day.tm_mon; - } else { - r.freq = wday_per_year( - tm_day.tm_year + 1900, - wday - ) - order + 1; + else tm_start.tm_mon = 0; - } - tm_start.tm_mday = 1; tm_start.tm_year = tm_day.tm_year; tm_start.tm_isdst = -1; nstart = date_sec_change( @@ -1367,7 +1398,7 @@ static int expand_yearly(time_t start, long dur, struct rpt *rpt, llist_t *exc, -WEEKINDAYS ); r.until = date_sec_change( - update_time_in_date(nstart, 0, 0), + DAY(nstart), 0, r.freq * WEEKINDAYS ); @@ -1778,18 +1809,66 @@ void recur_apoint_paste_item(struct recur_apoint *rapt, time_t date) * buffer. Useful for test of a repeated item. */ int recur_next_occurrence(time_t s, long d, struct rpt *r, llist_t *e, - time_t occur, time_t *next) + time_t day, time_t *next) { int ret = 0; - if (r->until && r->until <= occur) + if (r->until && r->until <= day) return ret; - while (!r->until || occur < r->until) { - occur = NEXTDAY(occur); - if (!check_sec(&occur)) + while (!r->until || day < r->until) { + day = NEXTDAY(day); + if (!check_sec(&day)) + break; + if (recur_item_find_occurrence(s, d, r, e, day, next)) { + /* Multi-day appointment. */ + if (*next < day) + continue; + ret = 1; + break; + } + } + return ret; +} + +/* + * Finds the nth occurrence (incl. start) of a recurrence rule (s, d, r, e) + * and returns it in the provided buffer. + */ +int recur_nth_occurrence(time_t s, long d, struct rpt *r, llist_t *e, int n, + time_t *nth) +{ + time_t day; + + if (n <= 0) + return 0; + + for (n--, *nth = s; n > 0; n--) { + day = DAY(*nth); + if (!recur_next_occurrence(s, d, r, e, day, nth)) break; - if (recur_item_find_occurrence(s, d, r, e, occur, next)) { + } + return !n; +} + +/* + * Finds the previous occurrence - the most recent before day - and returns it + * in the provided buffer. + */ +int recur_prev_occurrence(time_t s, long d, struct rpt *r, llist_t *e, + time_t day, time_t *prev) +{ + int ret = 0; + + if (day <= DAY(s)) + return ret; + + while (DAY(s) < day) { + day = PREVDAY(day); + if (recur_item_find_occurrence(s, d, r, e, day, prev)) { + /* Multi-day appointment. */ + if (d != -1 && *prev < day && day < *prev + d) + continue; ret = 1; break; } |