Extend import of recurrence rules
Support has been implemented for recurrence rule parts BYMONTH, BYMONTHDAY and BYDAY. A new test has been added. Signed-off-by: Lars Henriksen <LarsHenriksen@get2net.dk> Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
This commit is contained in:
parent
d2791b046a
commit
382e60ba69
213
src/ical.c
213
src/ical.c
@ -68,6 +68,9 @@ typedef struct {
|
|||||||
unsigned freq;
|
unsigned freq;
|
||||||
time_t until;
|
time_t until;
|
||||||
unsigned count;
|
unsigned count;
|
||||||
|
llist_t bymonth;
|
||||||
|
llist_t bywday;
|
||||||
|
llist_t bymonthday;
|
||||||
} ical_rpt_t;
|
} ical_rpt_t;
|
||||||
|
|
||||||
static void ical_export_header(FILE *);
|
static void ical_export_header(FILE *);
|
||||||
@ -377,7 +380,7 @@ static void ical_store_todo(int priority, int completed, char *mesg,
|
|||||||
* Calcurse limitation: events are one-day (all-day), and all multi-day events
|
* Calcurse limitation: events are one-day (all-day), and all multi-day events
|
||||||
* are turned into one-day events; a note has been added by ical_read_event().
|
* are turned into one-day events; a note has been added by ical_read_event().
|
||||||
*/
|
*/
|
||||||
static void
|
static int
|
||||||
ical_store_event(char *mesg, char *note, time_t day, time_t end,
|
ical_store_event(char *mesg, char *note, time_t day, time_t end,
|
||||||
ical_rpt_t *irpt, llist_t *exc, const char *fmt_ev,
|
ical_rpt_t *irpt, llist_t *exc, const char *fmt_ev,
|
||||||
const char *fmt_rev)
|
const char *fmt_rev)
|
||||||
@ -395,12 +398,14 @@ ical_store_event(char *mesg, char *note, time_t day, time_t end,
|
|||||||
rpt.type = irpt->type;
|
rpt.type = irpt->type;
|
||||||
rpt.freq = irpt->freq;
|
rpt.freq = irpt->freq;
|
||||||
rpt.until = irpt->until;
|
rpt.until = irpt->until;
|
||||||
LLIST_INIT(&rpt.bymonth);
|
rpt.bymonth = irpt->bymonth;
|
||||||
LLIST_INIT(&rpt.bywday);
|
rpt.bywday = irpt->bywday;
|
||||||
LLIST_INIT(&rpt.bymonthday);
|
rpt.bymonthday = irpt->bymonthday;
|
||||||
rpt.exc = *exc;
|
rpt.exc = *exc;
|
||||||
rev = recur_event_new(mesg, note, day, EVENTID, &rpt);
|
if (!recur_item_find_occurrence(day, -1, &rpt, NULL, day, NULL))
|
||||||
|
return 0;
|
||||||
mem_free(irpt);
|
mem_free(irpt);
|
||||||
|
rev = recur_event_new(mesg, note, day, EVENTID, &rpt);
|
||||||
if (fmt_rev)
|
if (fmt_rev)
|
||||||
print_recur_event(fmt_rev, day, rev);
|
print_recur_event(fmt_rev, day, rev);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
@ -434,9 +439,10 @@ ical_store_event(char *mesg, char *note, time_t day, time_t end,
|
|||||||
cleanup:
|
cleanup:
|
||||||
mem_free(mesg);
|
mem_free(mesg);
|
||||||
erase_note(¬e);
|
erase_note(¬e);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static int
|
||||||
ical_store_apoint(char *mesg, char *note, time_t start, long dur,
|
ical_store_apoint(char *mesg, char *note, time_t start, long dur,
|
||||||
ical_rpt_t * irpt, llist_t * exc, int has_alarm,
|
ical_rpt_t * irpt, llist_t * exc, int has_alarm,
|
||||||
const char *fmt_apt, const char *fmt_rapt)
|
const char *fmt_apt, const char *fmt_rapt)
|
||||||
@ -446,6 +452,7 @@ ical_store_apoint(char *mesg, char *note, time_t start, long dur,
|
|||||||
struct recur_apoint *rapt;
|
struct recur_apoint *rapt;
|
||||||
time_t day;
|
time_t day;
|
||||||
|
|
||||||
|
day = update_time_in_date(start, 0, 0);
|
||||||
if (has_alarm)
|
if (has_alarm)
|
||||||
state |= APOINT_NOTIFY;
|
state |= APOINT_NOTIFY;
|
||||||
if (irpt) {
|
if (irpt) {
|
||||||
@ -453,10 +460,13 @@ ical_store_apoint(char *mesg, char *note, time_t start, long dur,
|
|||||||
rpt.type = irpt->type;
|
rpt.type = irpt->type;
|
||||||
rpt.freq = irpt->freq;
|
rpt.freq = irpt->freq;
|
||||||
rpt.until = irpt->until;
|
rpt.until = irpt->until;
|
||||||
LLIST_INIT(&rpt.bymonth);
|
rpt.bymonth = irpt->bymonth;
|
||||||
LLIST_INIT(&rpt.bywday);
|
rpt.bywday = irpt->bywday;
|
||||||
LLIST_INIT(&rpt.bymonthday);
|
rpt.bymonthday = irpt->bymonthday;
|
||||||
rpt.exc = *exc;
|
rpt.exc = *exc;
|
||||||
|
if (!recur_item_find_occurrence(start, dur, &rpt, NULL,
|
||||||
|
day, NULL))
|
||||||
|
return 0;
|
||||||
/*
|
/*
|
||||||
* In calcurse, "until" is interpreted as a day (DATE) - hours,
|
* In calcurse, "until" is interpreted as a day (DATE) - hours,
|
||||||
* minutes and seconds are ignored - whereas in iCal the full
|
* minutes and seconds are ignored - whereas in iCal the full
|
||||||
@ -485,6 +495,7 @@ ical_store_apoint(char *mesg, char *note, time_t start, long dur,
|
|||||||
}
|
}
|
||||||
mem_free(mesg);
|
mem_free(mesg);
|
||||||
erase_note(¬e);
|
erase_note(¬e);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -790,9 +801,9 @@ static void ical_count2until(time_t s, long d, ical_rpt_t *i, llist_t *e,
|
|||||||
rpt.type = i->type;
|
rpt.type = i->type;
|
||||||
rpt.freq = i->freq;
|
rpt.freq = i->freq;
|
||||||
rpt.until = 0;
|
rpt.until = 0;
|
||||||
LLIST_INIT(&rpt.bymonth);
|
rpt.bymonth = i->bymonth;
|
||||||
LLIST_INIT(&rpt.bywday);
|
rpt.bywday = i->bywday;
|
||||||
LLIST_INIT(&rpt.bymonthday);
|
rpt.bymonthday = i->bymonthday;
|
||||||
recur_nth_occurrence(s, d, &rpt, e, i->count, &i->until);
|
recur_nth_occurrence(s, d, &rpt, e, i->count, &i->until);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -813,9 +824,108 @@ static char *ical_get_value(char *p)
|
|||||||
return p + 1;
|
return p + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fill in the bymonth linked list from a comma-separated list of
|
||||||
|
* unsigned integers terminated by a space or end of string.
|
||||||
|
*/
|
||||||
|
static int ical_bymonth(llist_t *ll, char *cl)
|
||||||
|
{
|
||||||
|
unsigned mon;
|
||||||
|
int *i, n;
|
||||||
|
|
||||||
|
while (!(*cl == ' ' || *cl == '\0')) {
|
||||||
|
if (!(sscanf(cl, "%u%n", &mon, &n) == 1))
|
||||||
|
return 0;
|
||||||
|
i = mem_malloc(sizeof(int));
|
||||||
|
*i = mon;
|
||||||
|
LLIST_ADD(ll, i);
|
||||||
|
cl += n;
|
||||||
|
cl += (*cl == ',');
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fill in the bymonthday linked list from a comma-separated list of
|
||||||
|
* (signed) integers terminated by a space or end of string.
|
||||||
|
*/
|
||||||
|
static int ical_bymonthday(llist_t *ll, char *cl)
|
||||||
|
{
|
||||||
|
int mday;
|
||||||
|
int *i, n;
|
||||||
|
|
||||||
|
while (!(*cl == ' ' || *cl == '\0')) {
|
||||||
|
if (!(sscanf(cl, "%d%n", &mday, &n) == 1))
|
||||||
|
return 0;
|
||||||
|
i = mem_malloc(sizeof(int));
|
||||||
|
*i = mday;
|
||||||
|
LLIST_ADD(ll, i);
|
||||||
|
cl += n;
|
||||||
|
cl += (*cl == ',');
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fill in the bywday linked list from a comma-separated list of (ordered)
|
||||||
|
* weekday names (+1SU, MO, -5SA, 25TU, etc.) terminated by a space or end of
|
||||||
|
* string.
|
||||||
|
*/
|
||||||
|
static int ical_bywday(llist_t *ll, char *cl)
|
||||||
|
{
|
||||||
|
int sign, order, wday, n, *i;
|
||||||
|
char *owd;
|
||||||
|
|
||||||
|
while (!(*cl == ' ' || *cl == '\0')) {
|
||||||
|
/* find list separator */
|
||||||
|
for (owd = cl; !(*cl == ',' || *cl == ' ' || *cl == '\0'); cl++)
|
||||||
|
;
|
||||||
|
cl += (*cl == ',');
|
||||||
|
|
||||||
|
if (!(sscanf(owd, "%d%n", &order, &n) == 1))
|
||||||
|
order = n = 0;
|
||||||
|
sign = (order < 0) ? -1 : 1;
|
||||||
|
order *= sign;
|
||||||
|
owd += n;
|
||||||
|
if (starts_with(owd, "SU"))
|
||||||
|
wday = 0;
|
||||||
|
else if (starts_with(owd, "MO"))
|
||||||
|
wday = 1;
|
||||||
|
else if (starts_with(owd, "TU"))
|
||||||
|
wday = 2;
|
||||||
|
else if (starts_with(owd, "WE"))
|
||||||
|
wday = 3;
|
||||||
|
else if (starts_with(owd, "TH"))
|
||||||
|
wday = 4;
|
||||||
|
else if (starts_with(owd, "FR"))
|
||||||
|
wday = 5;
|
||||||
|
else if (starts_with(owd, "SA"))
|
||||||
|
wday = 6;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
wday = sign * (wday + order * WEEKINDAYS);
|
||||||
|
i = mem_malloc(sizeof(int));
|
||||||
|
*i = wday;
|
||||||
|
LLIST_ADD(ll, i);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read a recurrence rule from an iCalendar RRULE string.
|
* Read a recurrence rule from an iCalendar RRULE string.
|
||||||
*
|
*
|
||||||
|
* RFC 5545, section 3.8.5.3:
|
||||||
|
*
|
||||||
|
* Property Name: RRULE
|
||||||
|
*
|
||||||
|
* Purpose: This property defines a rule or repeating pattern for
|
||||||
|
* recurring events, to-dos, journal entries, or time zone definitions.
|
||||||
|
*
|
||||||
|
* Value Type: RECUR
|
||||||
|
*
|
||||||
|
* RFC 5545, section 3.3.10:
|
||||||
|
*
|
||||||
* Value Name: RECUR
|
* Value Name: RECUR
|
||||||
*
|
*
|
||||||
* Purpose: This value type is used to identify properties that contain
|
* Purpose: This value type is used to identify properties that contain
|
||||||
@ -855,7 +965,8 @@ static char *ical_get_value(char *p)
|
|||||||
static ical_rpt_t *ical_read_rrule(FILE *log, char *rrulestr,
|
static ical_rpt_t *ical_read_rrule(FILE *log, char *rrulestr,
|
||||||
unsigned *noskipped,
|
unsigned *noskipped,
|
||||||
const int itemline,
|
const int itemline,
|
||||||
ical_vevent_e type)
|
ical_vevent_e type,
|
||||||
|
time_t start)
|
||||||
{
|
{
|
||||||
char freqstr[8];
|
char freqstr[8];
|
||||||
ical_rpt_t *rpt;
|
ical_rpt_t *rpt;
|
||||||
@ -880,6 +991,9 @@ static ical_rpt_t *ical_read_rrule(FILE *log, char *rrulestr,
|
|||||||
|
|
||||||
rpt = mem_malloc(sizeof(ical_rpt_t));
|
rpt = mem_malloc(sizeof(ical_rpt_t));
|
||||||
memset(rpt, 0, sizeof(ical_rpt_t));
|
memset(rpt, 0, sizeof(ical_rpt_t));
|
||||||
|
LLIST_INIT(&rpt->bymonth);
|
||||||
|
LLIST_INIT(&rpt->bywday);
|
||||||
|
LLIST_INIT(&rpt->bymonthday);
|
||||||
|
|
||||||
/* FREQ rule part */
|
/* FREQ rule part */
|
||||||
if ((p = strstr(rrulestr, "FREQ="))) {
|
if ((p = strstr(rrulestr, "FREQ="))) {
|
||||||
@ -960,6 +1074,42 @@ static ical_rpt_t *ical_read_rrule(FILE *log, char *rrulestr,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* BYMONTH rule part */
|
||||||
|
if ((p = strstr(rrulestr, "BYMONTH="))) {
|
||||||
|
p = strchr(p, '=') + 1;
|
||||||
|
if (!ical_bymonth(&rpt->bymonth, p)) {
|
||||||
|
ical_log(log, ICAL_VEVENT, itemline,
|
||||||
|
_("invalid bymonth list."));
|
||||||
|
(*noskipped)++;
|
||||||
|
mem_free(rpt);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BYMONTHDAY rule part */
|
||||||
|
if ((p = strstr(rrulestr, "BYMONTHDAY="))) {
|
||||||
|
p = strchr(p, '=') + 1;
|
||||||
|
if (!ical_bymonthday(&rpt->bymonthday, p)) {
|
||||||
|
ical_log(log, ICAL_VEVENT, itemline,
|
||||||
|
_("invalid bymonthday list."));
|
||||||
|
(*noskipped)++;
|
||||||
|
mem_free(rpt);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BYDAY rule part */
|
||||||
|
if ((p = strstr(rrulestr, "BYDAY="))) {
|
||||||
|
p = strchr(p, '=') + 1;
|
||||||
|
if (!ical_bywday(&rpt->bywday, p)) {
|
||||||
|
ical_log(log, ICAL_VEVENT, itemline,
|
||||||
|
_("invalid byday list."));
|
||||||
|
(*noskipped)++;
|
||||||
|
mem_free(rpt);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return rpt;
|
return rpt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1214,21 +1364,38 @@ ical_read_event(FILE * fdi, FILE * log, unsigned *noevents,
|
|||||||
}
|
}
|
||||||
vevent.note = generate_note(string_buf(&s));
|
vevent.note = generate_note(string_buf(&s));
|
||||||
mem_free(s.buf);
|
mem_free(s.buf);
|
||||||
|
/*
|
||||||
|
* Necessary to prevent double-free if item
|
||||||
|
* creation fails below.
|
||||||
|
*/
|
||||||
|
vevent.desc = vevent.loc = vevent.comm =
|
||||||
|
vevent.stat = vevent.imp = NULL;
|
||||||
}
|
}
|
||||||
|
char *msg = _("rrule does not match start day (%s).");
|
||||||
switch (vevent_type) {
|
switch (vevent_type) {
|
||||||
case APPOINTMENT:
|
case APPOINTMENT:
|
||||||
ical_store_apoint(vevent.mesg, vevent.note,
|
if (!ical_store_apoint(vevent.mesg, vevent.note,
|
||||||
vevent.start, vevent.dur,
|
vevent.start, vevent.dur,
|
||||||
vevent.rpt, &vevent.exc,
|
vevent.rpt, &vevent.exc,
|
||||||
vevent.has_alarm, fmt_apt,
|
vevent.has_alarm,
|
||||||
fmt_rapt);
|
fmt_apt, fmt_rapt)) {
|
||||||
|
char *l = day_ins(&msg, vevent.start);
|
||||||
|
ical_log(log, ICAL_VEVENT, ITEMLINE, l);
|
||||||
|
mem_free(l);
|
||||||
|
goto skip;
|
||||||
|
}
|
||||||
(*noapoints)++;
|
(*noapoints)++;
|
||||||
break;
|
break;
|
||||||
case EVENT:
|
case EVENT:
|
||||||
ical_store_event(vevent.mesg, vevent.note,
|
if (!ical_store_event(vevent.mesg, vevent.note,
|
||||||
vevent.start, vevent.end,
|
vevent.start, vevent.end,
|
||||||
vevent.rpt, &vevent.exc,
|
vevent.rpt, &vevent.exc,
|
||||||
fmt_ev, fmt_rev);
|
fmt_ev, fmt_rev)) {
|
||||||
|
char *l = day_ins(&msg, vevent.start);
|
||||||
|
ical_log(log, ICAL_VEVENT, ITEMLINE, l);
|
||||||
|
mem_free(l);
|
||||||
|
goto skip;
|
||||||
|
}
|
||||||
(*noevents)++;
|
(*noevents)++;
|
||||||
break;
|
break;
|
||||||
case UNDEFINED:
|
case UNDEFINED:
|
||||||
@ -1342,7 +1509,7 @@ ical_read_event(FILE * fdi, FILE * log, unsigned *noevents,
|
|||||||
}
|
}
|
||||||
} else if (starts_with_ci(buf, "RRULE")) {
|
} else if (starts_with_ci(buf, "RRULE")) {
|
||||||
vevent.rpt = ical_read_rrule(log, buf, noskipped,
|
vevent.rpt = ical_read_rrule(log, buf, noskipped,
|
||||||
ITEMLINE, vevent_type);
|
ITEMLINE, vevent_type, vevent.start);
|
||||||
if (!vevent.rpt)
|
if (!vevent.rpt)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
} else if (starts_with_ci(buf, "EXDATE")) {
|
} else if (starts_with_ci(buf, "EXDATE")) {
|
||||||
|
@ -1828,7 +1828,7 @@ int recur_next_occurrence(time_t s, long d, struct rpt *r, llist_t *e,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Finds the nth occurrence of a recurrence rule (s, d, r, e) (incl. the start)
|
* Finds the nth occurrence (incl. start) of a recurrence rule (s, d, r, e)
|
||||||
* and returns it in the provided buffer.
|
* and returns it in the provided buffer.
|
||||||
*/
|
*/
|
||||||
int recur_nth_occurrence(time_t s, long d, struct rpt *r, llist_t *e, int n,
|
int recur_nth_occurrence(time_t s, long d, struct rpt *r, llist_t *e, int n,
|
||||||
|
@ -434,9 +434,9 @@ time_t tzdate2sec(struct date day, unsigned hour, unsigned min, char *tznew)
|
|||||||
tzold = getenv("TZ");
|
tzold = getenv("TZ");
|
||||||
if (tzold)
|
if (tzold)
|
||||||
tzold = mem_strdup(tzold);
|
tzold = mem_strdup(tzold);
|
||||||
|
|
||||||
setenv("TZ", tznew, 1);
|
setenv("TZ", tznew, 1);
|
||||||
tzset();
|
tzset();
|
||||||
|
|
||||||
t = date2sec(day, hour, min);
|
t = date2sec(day, hour, min);
|
||||||
|
|
||||||
if (tzold) {
|
if (tzold) {
|
||||||
|
@ -60,6 +60,7 @@ TESTS = \
|
|||||||
ical-010.sh \
|
ical-010.sh \
|
||||||
ical-011.sh \
|
ical-011.sh \
|
||||||
ical-012.sh \
|
ical-012.sh \
|
||||||
|
ical-013.sh \
|
||||||
next-001.sh \
|
next-001.sh \
|
||||||
next-002.sh \
|
next-002.sh \
|
||||||
next-003.sh \
|
next-003.sh \
|
||||||
@ -138,6 +139,7 @@ EXTRA_DIST = \
|
|||||||
data/ical-008.ical \
|
data/ical-008.ical \
|
||||||
data/ical-009.ical \
|
data/ical-009.ical \
|
||||||
data/ical-012.ical \
|
data/ical-012.ical \
|
||||||
|
data/rfc5545.ical \
|
||||||
data/rfc5545 \
|
data/rfc5545 \
|
||||||
data/todo \
|
data/todo \
|
||||||
data/todo-export
|
data/todo-export
|
||||||
|
@ -46,7 +46,25 @@ DTSTAMP:
|
|||||||
UID:
|
UID:
|
||||||
DTSTART;VALUE=DATE:20200502
|
DTSTART;VALUE=DATE:20200502
|
||||||
DTEND;VALUE=DATE:20200504
|
DTEND;VALUE=DATE:20200504
|
||||||
DESCRIPTION:The first weekend in May is a two-day event.
|
DESCRIPTION:The first weekend in May is a two-day event.\nNon-repeating event.
|
||||||
SUMMARY:First weekend in May
|
SUMMARY:First weekend in May
|
||||||
END:VEVENT
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;VALUE=DATE:20200502
|
||||||
|
DTEND;VALUE=DATE:20200504
|
||||||
|
DESCRIPTION:First weekend in May is a two-day event!\nRepeating event\, three years.
|
||||||
|
SUMMARY:First weekend in May
|
||||||
|
RRULE:FREQ=YEARLY;BYDAY=1SA;BYMONTH=5;COUNT=3
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART:20200502T000000
|
||||||
|
DTEND:20200504T000000
|
||||||
|
DESCRIPTION:First weekend in May is a two-day event!\nRepeating appointment.
|
||||||
|
SUMMARY:First weekend in May
|
||||||
|
RRULE:FREQ=YEARLY;BYDAY=1SA;BYMONTH=5;COUNT=3
|
||||||
|
END:VEVENT
|
||||||
END:VCALENDAR
|
END:VCALENDAR
|
||||||
|
238
test/data/rfc5545.ical
Normal file
238
test/data/rfc5545.ical
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
BEGIN:VCALENDAR
|
||||||
|
VERSION:2.0
|
||||||
|
PRODID:
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19970901T090000
|
||||||
|
SUMMARY:Every other week on Monday\, Wednesday\, and Friday until December 24\, 1997\, starting on Monday\, September 1\, 1997
|
||||||
|
RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19970902T090000
|
||||||
|
SUMMARY:Every other week on Tuesday and Thursday\, for 8 occurrences
|
||||||
|
RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=8;WKST=SU;BYDAY=TU,TH
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19970905T090000
|
||||||
|
SUMMARY:Monthly on the first Friday for 10 occurrences
|
||||||
|
RRULE:FREQ=MONTHLY;COUNT=10;BYDAY=1FR
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19970905T090000
|
||||||
|
SUMMARY:Monthly on the first Friday until December 24\, 1997
|
||||||
|
RRULE:FREQ=MONTHLY;UNTIL=19971224T000000Z;BYDAY=1FR
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19970907T090000
|
||||||
|
SUMMARY:Every other month on the first and last Sunday of the month for 10 occurrences
|
||||||
|
RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19970922T090000
|
||||||
|
SUMMARY:Monthly on the second-to-last Monday of the month for 6 months
|
||||||
|
RRULE:FREQ=MONTHLY;COUNT=6;BYDAY=-2MO
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19970928T090000
|
||||||
|
SUMMARY:Monthly on the third-to-the-last day of the month\, forever
|
||||||
|
RRULE:FREQ=MONTHLY;BYMONTHDAY=-3
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19970902T090000
|
||||||
|
SUMMARY:Monthly on the 2nd and 15th of the month for 10 occurrences
|
||||||
|
RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19970930T090000
|
||||||
|
SUMMARY:Monthly on the first and last day of the month for 10 occurrences
|
||||||
|
RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19970910T090000
|
||||||
|
SUMMARY:Every 18 months on the 10th thru 15th of the month for 10 occurrences
|
||||||
|
RRULE:FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,15
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19970902T090000
|
||||||
|
DURATION:PT1H
|
||||||
|
SUMMARY:Daily for 10 occurrences
|
||||||
|
RRULE:FREQ=DAILY;COUNT=10
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19970902T090000
|
||||||
|
SUMMARY:Every Tuesday\, every other month
|
||||||
|
RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19970610T090000
|
||||||
|
SUMMARY:Yearly in June and July for 10 occurrences
|
||||||
|
RRULE:FREQ=YEARLY;COUNT=10;BYMONTH=6,7
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19970310T090000
|
||||||
|
SUMMARY:Every other year on January\, February\, and March for 10 occurrences
|
||||||
|
RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19970519T090000
|
||||||
|
SUMMARY:Every 20th Monday of the year\, forever
|
||||||
|
RRULE:FREQ=YEARLY;BYDAY=20MO
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19970313T090000
|
||||||
|
SUMMARY:Every Thursday in March\, forever
|
||||||
|
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19970605T090000
|
||||||
|
SUMMARY:Every Thursday\, but only during June\, July\, and August\, forever
|
||||||
|
RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19980213T090000
|
||||||
|
SUMMARY:Every Friday the 13th\, forever
|
||||||
|
RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19970913T090000
|
||||||
|
SUMMARY:The first Saturday that follows the first Sunday of the month\, forever
|
||||||
|
RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19961105T090000
|
||||||
|
SUMMARY:Every 4 years\, the first Tuesday after a Monday in November\, forever (U.S. Presidential Election day)
|
||||||
|
RRULE:FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19970805T090000
|
||||||
|
SUMMARY:An example where the days generated makes a difference because of WKST
|
||||||
|
RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=MO
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19970805T090000
|
||||||
|
SUMMARY:changing only WKST from MO to SU\, yields different results...
|
||||||
|
RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=SU
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19970902T090000
|
||||||
|
DURATION:PT30M
|
||||||
|
SUMMARY:Daily until December 24\, 1997
|
||||||
|
RRULE:FREQ=DAILY;UNTIL=19971224T000000Z
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:20070115T090000
|
||||||
|
SUMMARY:An example where an invalid date (i.e.\, February 30) is ignored
|
||||||
|
RRULE:FREQ=MONTHLY;BYMONTHDAY=15,30;COUNT=5
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19970902T090000
|
||||||
|
DURATION:PT5M
|
||||||
|
SUMMARY:Every other day - forever
|
||||||
|
RRULE:FREQ=DAILY;INTERVAL=2
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19970902T090000
|
||||||
|
SUMMARY:Every 10 days\, 5 occurrences:
|
||||||
|
RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19980101T090000
|
||||||
|
SUMMARY:(1) Every day in January\, for 3 years:
|
||||||
|
RRULE:FREQ=YEARLY;UNTIL=20000131T140000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19980101T090000
|
||||||
|
SUMMARY:(2) Every day in January\, for 3 years:
|
||||||
|
RRULE:FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19970902T090000
|
||||||
|
SUMMARY:Weekly for 10 occurrences
|
||||||
|
RRULE:FREQ=WEEKLY;COUNT=10
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19970902T090000
|
||||||
|
SUMMARY:Weekly until December 24\, 1997
|
||||||
|
RRULE:FREQ=WEEKLY;UNTIL=19971224T000000Z
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19970902T090000
|
||||||
|
SUMMARY:Every other week - forever
|
||||||
|
RRULE:FREQ=WEEKLY;INTERVAL=2;WKST=SU
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19970902T090000
|
||||||
|
SUMMARY:Weekly on Tuesday and Thursday for five weeks (UNTIL)
|
||||||
|
RRULE:FREQ=WEEKLY;UNTIL=19971007T000000Z;WKST=SU;BYDAY=TU,TH
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTAMP:
|
||||||
|
UID:
|
||||||
|
DTSTART;TZID=America/New_York:19970902T090000
|
||||||
|
SUMMARY:Weekly on Tuesday and Thursday for five weeks (COUNT)
|
||||||
|
RRULE:FREQ=WEEKLY;COUNT=10;WKST=SU;BYDAY=TU,TH
|
||||||
|
END:VEVENT
|
||||||
|
END:VCALENDAR
|
@ -8,13 +8,13 @@ if [ "$1" = 'actual' ]; then
|
|||||||
cp "$DATA_DIR/conf" .calcurse || exit 1
|
cp "$DATA_DIR/conf" .calcurse || exit 1
|
||||||
"$CALCURSE" -D "$PWD/.calcurse" -i "$DATA_DIR/ical-003.ical"
|
"$CALCURSE" -D "$PWD/.calcurse" -i "$DATA_DIR/ical-003.ical"
|
||||||
"$CALCURSE" -D "$PWD/.calcurse" -s01/01/2000 -r365
|
"$CALCURSE" -D "$PWD/.calcurse" -s01/01/2000 -r365
|
||||||
"$CALCURSE" -D "$PWD/.calcurse" -s05/01/2020 --to 01/01/2022
|
"$CALCURSE" -D "$PWD/.calcurse" -s05/01/2020 --to 01/01/2023
|
||||||
cat "$PWD/.calcurse/notes"/*
|
cat "$PWD/.calcurse/notes"/*
|
||||||
rm -rf .calcurse || exit 1
|
rm -rf .calcurse || exit 1
|
||||||
elif [ "$1" = 'expected' ]; then
|
elif [ "$1" = 'expected' ]; then
|
||||||
cat <<EOD
|
cat <<EOD
|
||||||
Import process report: 0052 lines read
|
Import process report: 0070 lines read
|
||||||
6 apps / 1 event / 0 todos / 0 skipped
|
7 apps / 2 events / 0 todos / 0 skipped
|
||||||
01/01/00:
|
01/01/00:
|
||||||
- 00:00 -> 01:30
|
- 00:00 -> 01:30
|
||||||
Recurring appointment
|
Recurring appointment
|
||||||
@ -136,9 +136,14 @@ Import process report: 0052 lines read
|
|||||||
Recurring appointment
|
Recurring appointment
|
||||||
05/02/20:
|
05/02/20:
|
||||||
* First weekend in May
|
* First weekend in May
|
||||||
|
* First weekend in May
|
||||||
|
- 00:00 -> ..:..
|
||||||
|
First weekend in May
|
||||||
|
|
||||||
05/03/20:
|
05/03/20:
|
||||||
* First weekend in May
|
* First weekend in May
|
||||||
|
- ..:.. -> 00:00
|
||||||
|
First weekend in May
|
||||||
|
|
||||||
05/26/20:
|
05/26/20:
|
||||||
- 12:00 -> 13:17
|
- 12:00 -> 13:17
|
||||||
@ -182,6 +187,15 @@ Import process report: 0052 lines read
|
|||||||
- 21:45 -> 22:00
|
- 21:45 -> 22:00
|
||||||
monthly on 31th, count 10, exceptions 31/7/2020 and 31/1/2021
|
monthly on 31th, count 10, exceptions 31/7/2020 and 31/1/2021
|
||||||
|
|
||||||
|
05/01/21:
|
||||||
|
* First weekend in May
|
||||||
|
- 00:00 -> ..:..
|
||||||
|
First weekend in May
|
||||||
|
|
||||||
|
05/02/21:
|
||||||
|
- ..:.. -> 00:00
|
||||||
|
First weekend in May
|
||||||
|
|
||||||
05/31/21:
|
05/31/21:
|
||||||
- 21:45 -> 22:00
|
- 21:45 -> 22:00
|
||||||
monthly on 31th, count 10, exceptions 31/7/2020 and 31/1/2021
|
monthly on 31th, count 10, exceptions 31/7/2020 and 31/1/2021
|
||||||
@ -201,7 +215,23 @@ Import process report: 0052 lines read
|
|||||||
12/31/21:
|
12/31/21:
|
||||||
- 21:45 -> 22:00
|
- 21:45 -> 22:00
|
||||||
monthly on 31th, count 10, exceptions 31/7/2020 and 31/1/2021
|
monthly on 31th, count 10, exceptions 31/7/2020 and 31/1/2021
|
||||||
|
|
||||||
|
05/07/22:
|
||||||
|
* First weekend in May
|
||||||
|
- 00:00 -> ..:..
|
||||||
|
First weekend in May
|
||||||
|
|
||||||
|
05/08/22:
|
||||||
|
- ..:.. -> 00:00
|
||||||
|
First weekend in May
|
||||||
|
First weekend in May is a two-day event!
|
||||||
|
Repeating appointment.
|
||||||
The first weekend in May is a two-day event.
|
The first weekend in May is a two-day event.
|
||||||
|
Non-repeating event.
|
||||||
|
--
|
||||||
|
Import: multi-day event changed to one-day event
|
||||||
|
First weekend in May is a two-day event!
|
||||||
|
Repeating event, three years.
|
||||||
--
|
--
|
||||||
Import: multi-day event changed to one-day event
|
Import: multi-day event changed to one-day event
|
||||||
EOD
|
EOD
|
||||||
|
1800
test/ical-013.sh
Executable file
1800
test/ical-013.sh
Executable file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user