docs: fix yearly recurrence format and clarify sort/limitations

- Fix `REM Mon DD` → `REM MMM DD` in yearly recurrence table
- Clarify `--sort original` preserves processing order in CLI reference
- Update `RELATED=END` trigger behaviour (treated as `RELATED=START`)
- Remove unsupported EXDATE/RDATE+override limitation (now handled)
- Fix `--sort none` → `--sort original` in CLI help text
- Remove EXDATE/RDATE/override guard from `simple_recurrence`
This commit is contained in:
2026-05-24 18:15:38 +02:00
parent be3abcb09a
commit 8bb075153d
3 changed files with 28 additions and 34 deletions

View File

@@ -14,7 +14,7 @@ Full RFC 5545 coverage is intentionally out of scope — see [Limitations](#limi
| All-day single | `REM YYYY-MM-DD MSG …` | | All-day single | `REM YYYY-MM-DD MSG …` |
| All-day multi-day | `REM date THROUGH date MSG …` | | All-day multi-day | `REM date THROUGH date MSG …` |
| Timed event (UTC, local, TZID) | `REM date AT HH:MM DURATION HH:MM MSG …` | | Timed event (UTC, local, TZID) | `REM date AT HH:MM DURATION HH:MM MSG …` |
| Yearly recurrence (`FREQ=YEARLY`) | `REM Mon DD MSG …` | | Yearly recurrence (`FREQ=YEARLY`) | `REM MMM DD MSG …` |
| Weekly recurrence (`FREQ=WEEKLY`) | `REM Mon Wed FROM … UNTIL … MSG …` | | Weekly recurrence (`FREQ=WEEKLY`) | `REM Mon Wed FROM … UNTIL … MSG …` |
| Daily recurrence (`FREQ=DAILY`) | `REM date *N UNTIL … MSG …` | | Daily recurrence (`FREQ=DAILY`) | `REM date *N UNTIL … MSG …` |
| Monthly by day-of-month (`BYMONTHDAY`) | `REM N FROM … UNTIL … MSG …` | | Monthly by day-of-month (`BYMONTHDAY`) | `REM N FROM … UNTIL … MSG …` |
@@ -115,7 +115,7 @@ ical2rem --verbose personal.ics > personal.rem
|---|---| |---|---|
| `FILE…` | One or more `.ics` files to convert | | `FILE…` | One or more `.ics` files to convert |
| `-z`, `--timezone TZ` | Target timezone for output (default: local) | | `-z`, `--timezone TZ` | Target timezone for output (default: local) |
| `--sort asc\|desc\|original` | Sort order by date (default: `desc`) | | `--sort asc\|desc\|original` | Sort order by date (default: `desc`); `original` preserves processing order (sorted by UID within each file, last file first) |
| `--source NAME` | Override calendar name (single file only) | | `--source NAME` | Override calendar name (single file only) |
| `-v`, `--verbose` | Print diagnostic messages on stderr | | `-v`, `--verbose` | Print diagnostic messages on stderr |
| `--no-uuid` | Omit `INFO "UID: …"` lines | | `--no-uuid` | Omit `INFO "UID: …"` lines |
@@ -184,14 +184,13 @@ REM \
- `BYSETPOS`: **not supported**. - `BYSETPOS`: **not supported**.
- `FREQ=YEARLY` with `BYMONTH`/`BYDAY` variants: **not supported**, only simple yearly (same day every year). - `FREQ=YEARLY` with `BYMONTH`/`BYDAY` variants: **not supported**, only simple yearly (same day every year).
- `RDATE` (additional isolated dates): **not supported**, warning emitted. - `RDATE` (additional isolated dates): **not supported**, warning emitted.
- Recurring events that have both EXDATE/RDATE and override occurrences (`RECURRENCE-ID`): **not supported**, event skipped with warning.
### Alarms (`VALARM`) ### Alarms (`VALARM`)
- `ACTION:EMAIL`: ignored silently. - `ACTION:EMAIL`: ignored silently.
- `TRIGGER;VALUE=DATE-TIME` (absolute datetime trigger): ignored silently. - `TRIGGER;VALUE=DATE-TIME` (absolute datetime trigger): ignored silently.
- Positive triggers (after the event): ignored silently. - Positive triggers (after the event): ignored silently.
- `RELATED=END` triggers: **not implemented**. - `RELATED=END` triggers: the offset is applied as if it were `RELATED=START` (no warning emitted).
- `REPEAT`/`DURATION` (repeating alarms) on all-day events: ignored. - `REPEAT`/`DURATION` (repeating alarms) on all-day events: ignored.
### Other ### Other

View File

@@ -43,7 +43,7 @@ type sort_order = Asc | Desc | Original
let sort_order_enum = [ ("asc", Asc); ("desc", Desc); ("original", Original) ] let sort_order_enum = [ ("asc", Asc); ("desc", Desc); ("original", Original) ]
let sort = let sort =
let doc = "Output sort order by date: $(b,desc) (default), $(b,asc), or $(b,none) (file order)." in let doc = "Output sort order by date: $(b,desc) (default), $(b,asc), or $(b,original) (processing order)." in
Arg.(value & opt (enum sort_order_enum) Desc & info [ "sort" ] ~docv:"ORDER" ~doc) Arg.(value & opt (enum sort_order_enum) Desc & info [ "sort" ] ~docv:"ORDER" ~doc)
let source = let source =

View File

@@ -126,35 +126,30 @@ let simple_recurrence rem ev : (Remind.rem, error) result =
match ev.rrule with match ev.rrule with
| Some (_, (`Yearly, None, None, [])) -> Ok rem (* handled in yearly_simple_date *) | Some (_, (`Yearly, None, None, [])) -> Ok rem (* handled in yearly_simple_date *)
| Some (_, ((`Weekly as freq), count_or_until, interval, recurs)) | Some (_, ((`Weekly as freq), count_or_until, interval, recurs))
| Some (_, ((`Daily as freq), count_or_until, interval, recurs)) -> | Some (_, ((`Daily as freq), count_or_until, interval, recurs)) -> begin
begin if List.length rem.recurring > 0 then ( let days =
Utils.warn "Warning: complex recurrence with EXDATE/RDATE/overrides not supported, skipping (UID: %s)\n" ListLabels.filter_map recurs ~f:(function
(Utils.get_uid ev); | `Byday days -> begin List.map (fun (_n, weekday) -> weekday) days |> Option.some end
skip) | _ -> None)
else |> List.flatten
let days = in
ListLabels.filter_map recurs ~f:(function let week_start =
| `Byday days -> begin List.map (fun (_n, weekday) -> weekday) days |> Option.some end ListLabels.find_map recurs ~f:(function
| _ -> None) | `Weekday `Sunday -> Some `Sunday
|> List.flatten | `Weekday `Monday -> Some `Monday
in | _ -> None)
let week_start = in
ListLabels.find_map recurs ~f:(function match freq with
| `Weekday `Sunday -> Some `Sunday | `Daily -> Ok { rem with Remind.weekly = None; Remind.daily = Some { count_or_until; interval; week_start } }
| `Weekday `Monday -> Some `Monday | `Weekly ->
| _ -> None) let days = if days = [] then [ timedesc_wd_to_ical (Timedesc.Date.weekday rem.date) ] else days in
in Ok
match freq with {
| `Daily -> Ok { rem with Remind.weekly = None; Remind.daily = Some { count_or_until; interval; week_start } } rem with
| `Weekly -> Remind.daily = None;
let days = if days = [] then [ timedesc_wd_to_ical (Timedesc.Date.weekday rem.date) ] else days in Remind.weekly = Some { count_or_until; interval; byday = days; week_start };
Ok }
{ end
rem with
Remind.daily = None;
Remind.weekly = Some { count_or_until; interval; byday = days; week_start };
}
end
| Some (_, (`Monthly, count_or_until, interval, recurs)) -> | Some (_, (`Monthly, count_or_until, interval, recurs)) ->
begin match interval with begin match interval with
| Some n when n > 1 -> | Some n when n > 1 ->