aboutsummaryrefslogtreecommitdiffstats
path: root/src/recur.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/recur.c')
-rw-r--r--src/recur.c167
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;
}