aboutsummaryrefslogtreecommitdiff
path: root/modules/calendar-sync-ics.el
Commit message (Collapse)AuthorAgeFilesLines
* fix(calendar-sync): render multi-day events as org rangesCraig Jennings17 hours1-12/+51
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | format-timestamp built its date from the start and took only the hour and minute from the end, so the end date was thrown away. A conference running Jul 20 09:00 to Jul 23 17:00 produced <2026-07-20 Mon 09:00-17:00>, byte for byte the same timestamp as a same-day meeting, and the agenda showed it on the first day only. All-day spans collapsed the same way, to a bare <2026-07-20 Mon>. An event whose last day is later than its start now renders as an org range, <start>--<end>, which org parses as an active-range and shows on every day it covers. DTEND is the non-inclusive end of the event (RFC 5545 3.6.1), so an all-day event's last day is DTEND-1. Getting that backwards would have been worse than the bug: a one-day all-day event carries DTEND = start+1, so a naive range would turn every single all-day event into a two-day one. The decrement only applies when both ends are date-only. A date-only start with a timed end is malformed, and treating it as all-day would put the last day before the start and emit a backwards range. That case falls through to the same-day form, as before. I split out format-stamp and format-hhmm so both halves of a range and the compact same-day form build from one place. Same-day events are unchanged, pinned by two tests: the compact HH:MM-HH:MM form, and a single all-day event staying a single stamp. One unrelated bug stays open. A timed event with no DTEND renders as <date> and drops its time, so a 09:00 meeting reads as all-day. I left it alone.
* fix(calendar-sync): keep the final occurrence of an UNTIL-bounded seriesCraig Jennings18 hours1-0/+12
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | RFC 5545 3.3.10 bounds a recurrence inclusively: when UNTIL lines up with the recurrence, that date is the last instance. The expansion loops guarded on calendar-sync--before-date-p, a strict comparison, so the instance landing exactly on UNTIL was dropped. Every bounded series silently lost its last meeting from the agenda. A series whose UNTIL equals its start date lost the only instance it had and vanished. I added calendar-sync--date-on-or-before-p beside --before-date-p and swapped the three UNTIL sites: the simple-recurrence loop feeding daily, monthly and yearly, plus both weekly checks. --before-date-p is unchanged, since a date is still not before itself. The two UNTIL property tests asserted the wrong invariant. They required every occurrence to fall strictly before UNTIL, the exclusive reading the RFC contradicts, so they pinned the defect they should have caught. The property is now on-or-before. An upper bound alone can't catch a dropped occurrence, so I added one asserting the series reaches its UNTIL date. Reverting the fix failed three daily tests and one property test, and no weekly ones. The weekly loop was fixed but unguarded, so it has its own test now. The comparison stays date-granular, matching --date-to-time. An UNTIL carrying a time of day earlier than the event's start will include that final instance where the RFC would exclude it. Making it datetime-precise means reworking --date-to-time and the timezone conversions feeding it, a much larger change than this bug warrants.
* refactor: split calendar-sync.el into layered modulesCraig Jennings2026-06-291-0/+582
Break the 1724-line calendar-sync.el into a thin public face plus four layered libraries, moving every function verbatim so behavior and public names are unchanged: - calendar-sync-ics.el — base parsing: RFC 5545 text cleaning, VEVENT property extraction, attendee/organizer/URL parsing, timezone and timestamp conversion, date arithmetic, single-event parsing. Depends on neither of the other new modules. - calendar-sync-recurrence.el — RRULE/EXDATE/RECURRENCE-ID expansion. - calendar-sync-org.el — Org rendering and atomic file output. - calendar-sync-source.el — sync state and persistence, async .ics fetch, the batch conversion worker, and the Google Calendar API path. calendar-sync.el keeps configuration, the parse orchestrator, sync dispatch, the user commands, the timer, and the C-; g keymap, and requires the four layers. Each layer forward-declares the config defvars it reads, so no layer requires the top module back. The batch worker loads the whole graph, so source forward-declares the two functions it calls there. Every public name is preserved, so all 574 existing calendar-sync tests pass unchanged through the require chain. The four new modules carry the load-graph and package headers and join the header-contract allowlist. Claude-Session: https://claude.ai/code/session_014fyKMTTqLrZpL3rDF3dYc3