From be3abcb09a5cd7890b7be1b03d24de8a89b202a1 Mon Sep 17 00:00:00 2001 From: Paolo Donadeo Date: Sun, 24 May 2026 12:50:16 +0200 Subject: [PATCH] docs: add comprehensive README for ical2rem --- README.md | 202 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 200 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 848df76..de57c72 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,201 @@ -# remind-sync +# ical2rem + +Convert iCalendar (`.ics`) files into [Remind](https://dianne.skoll.ca/projects/remind/) format. + +Designed to work on real-world calendar data (Google Calendar, SOGo, Outlook/Teams exports). +Full RFC 5545 coverage is intentionally out of scope — see [Limitations](#limitations). + +## Features + +### Event types supported + +| iCalendar pattern | Remind output | +|---|---| +| All-day single | `REM YYYY-MM-DD MSG …` | +| All-day multi-day | `REM date THROUGH date MSG …` | +| Timed event (UTC, local, TZID) | `REM date AT HH:MM DURATION HH:MM MSG …` | +| Yearly recurrence (`FREQ=YEARLY`) | `REM Mon DD MSG …` | +| Weekly recurrence (`FREQ=WEEKLY`) | `REM Mon Wed FROM … UNTIL … MSG …` | +| Daily recurrence (`FREQ=DAILY`) | `REM date *N UNTIL … MSG …` | +| Monthly by day-of-month (`BYMONTHDAY`) | `REM N FROM … UNTIL … MSG …` | +| Monthly by nth weekday (`BYDAY=nWD`) | `REM Wd N FROM … UNTIL … MSG …` | +| Exceptions (`EXDATE`) | `PUSH-OMIT-CONTEXT` + `OMIT` + `SKIP` + `POP-OMIT-CONTEXT` | +| Overrides (`RECURRENCE-ID`) | OMIT on original slot + single `REM` with new date/time | +| Cancellations (`STATUS:CANCELLED`) | OMIT only, no REM | +| Duration via `DURATION` instead of `DTEND` | Handled transparently | +| Alarms (`VALARM ACTION:DISPLAY/AUDIO`) | `AT … +n` / `++n` / `SCHED` / `WARN` | +| Conference URL (Google Meet, Teams) | `INFO "Url: …"` | +| Windows timezone names (Outlook) | Resolved to IANA via CLDR table | + +### Metadata lines + +Each reminder can include optional `INFO` lines (all suppressible via flags): + +``` +REM \ + INFO "UID: …" \ + INFO "Calendar: MYCAL" \ + INFO "Location: …" \ + INFO "Description: …" \ + INFO "Url: …" \ + … +``` + +## Installation + +### From source + +Requires [opam](https://opam.ocaml.org/) and OCaml >= 5.0. + +```bash +git clone https://git.donadeo.net/pdonadeo/ical2rem +cd ical2rem +opam install . --deps-only +dune build +dune install +``` + +The binary is installed as `ical2rem`. + +### Dependencies + +- [`icalendar`](https://github.com/roburio/icalendar) — iCal parser +- [`timedesc`](https://github.com/daypack-dev/timere) + `timedesc-tzdb.full` + `timedesc-tzlocal.unix` — date/time + timezone handling +- [`cmdliner`](https://erratique.ch/software/cmdliner) — CLI +- [`ppx_deriving.show`](https://github.com/ocaml-ppx/ppx_deriving) — debug printers + +## Usage + +``` +ical2rem [OPTION]… FILE… +``` + +Output goes to stdout and can be redirected to a `.rem` file. + +### Examples + +Convert a single calendar: +```bash +ical2rem personal.ics > personal.rem +``` + +Convert multiple calendars into one file: +```bash +ical2rem work.ics personal.ics > all.rem +``` + +Sort output chronologically (oldest first): +```bash +ical2rem --sort asc personal.ics > personal.rem +``` + +Run on a server in UTC, output in a specific timezone: +```bash +ical2rem --timezone Europe/Rome personal.ics > personal.rem +``` + +Strip all metadata lines from output: +```bash +ical2rem --no-uuid --no-source --no-location --no-description --no-conference-url personal.ics +``` + +Override calendar name in `INFO` lines (single file only): +```bash +ical2rem --source "Work" work.ics > work.rem +``` + +Show diagnostic warnings (skipped events, unsupported rules, etc.): +```bash +ical2rem --verbose personal.ics > personal.rem +``` + +### All options + +| Option | Description | +|---|---| +| `FILE…` | One or more `.ics` files to convert | +| `-z`, `--timezone TZ` | Target timezone for output (default: local) | +| `--sort asc\|desc\|original` | Sort order by date (default: `desc`) | +| `--source NAME` | Override calendar name (single file only) | +| `-v`, `--verbose` | Print diagnostic messages on stderr | +| `--no-uuid` | Omit `INFO "UID: …"` lines | +| `--no-source` | Omit `INFO "Calendar: …"` lines | +| `--no-location` | Omit `INFO "Location: …"` lines | +| `--no-description` | Omit `INFO "Description: …"` lines | +| `--no-conference-url` | Omit `INFO "Url: …"` lines | +| `--version` | Print version and exit | +| `--help` | Print help and exit | + +## Output format examples + +### All-day single event + +``` +REM \ + INFO "UID: abc@google.com" \ + INFO "Calendar: PERSONAL" \ + 2026-06-15 MSG Birthday party +``` + +### Timed event with alarm + +``` +REM \ + INFO "UID: xyz@google.com" \ + INFO "Calendar: WORK" \ + 2026-05-27 AT 10:00 +30 DURATION 01:00 MSG %"Team standup%" (%1) +``` + +### Weekly recurrence with exception + +``` +PUSH-OMIT-CONTEXT +OMIT 6 Oct 2025 +REM \ + INFO "UID: …" \ + INFO "Calendar: WORK" \ + Mon 2025-09-01 *7 UNTIL 2025-12-31 SKIP AT 09:00 +15 MSG %"Standup%" (%1) +POP-OMIT-CONTEXT +``` + +### Recurring event with modified occurrence + +``` +PUSH-OMIT-CONTEXT +OMIT 3 Jun 2015 +REM \ + INFO "UID: …" \ + INFO "Calendar: PERSONAL" \ + Wed 1 FROM 2015-06-03 UNTIL 2015-06-09 SKIP MSG Monthly payment +POP-OMIT-CONTEXT +REM \ + INFO "UID: …" \ + INFO "Calendar: PERSONAL" \ + 2015-06-09 MSG Monthly payment +``` + +## Limitations + +### Recurrence rules (`RRULE`) + +- `FREQ=WEEKLY INTERVAL=N` (N > 1): supported (maps to `*7N`). +- `FREQ=MONTHLY INTERVAL=N` (N > 1): **not supported**, event skipped with warning. +- `FREQ=MONTHLY BYDAY=WD` without position (every Monday of the month): **not supported**. +- `BYSETPOS`: **not supported**. +- `FREQ=YEARLY` with `BYMONTH`/`BYDAY` variants: **not supported**, only simple yearly (same day every year). +- `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`) + +- `ACTION:EMAIL`: ignored silently. +- `TRIGGER;VALUE=DATE-TIME` (absolute datetime trigger): ignored silently. +- Positive triggers (after the event): ignored silently. +- `RELATED=END` triggers: **not implemented**. +- `REPEAT`/`DURATION` (repeating alarms) on all-day events: ignored. + +### Other + +- `ATTENDEE`, `CATEGORIES`, visibility/status fields: ignored. +- `VTIMEZONE` components: ignored; TZID names are resolved directly via IANA or the built-in Windows→IANA CLDR table. -Simple program to convert iCalendar files into remind format \ No newline at end of file