Show birthdays from the address book as a calendar

The dates were already on the contact cards and nothing ever showed them,
so the one thing a birthday is for -- noticing it in time -- was the one
thing the app could not do with it.

Derived, not stored. The dates stay on the cards: a second copy of the
same fact drifts the first time somebody corrects one, and keeping a
calendar of its own is exactly what ihasmail does not do. Entries are
generated when a view asks for a range and vanish when the contact does.
They go through instancesIn like everything else, so no view has to know
they are different.

Off until switched on. It is derived data, and a calendar that fills
itself with dates nobody put there is a surprise rather than a feature. It
can also be hidden from the calendar's own sidebar without being turned
off, which is the same distinction the shared calendars already draw.

They cannot be edited or deleted, and that falls out of the design rather
than being special-cased: the virtual calendar reports no write rights, so
every control that already asks before offering Edit or Delete declines on
its own. updateEvent and destroyEvent refuse a synthesised id as well, so
the store is safe whatever calls it -- including anything added later.

Two things about the dates themselves. A card that records only a day and
month is the common case rather than the exceptional one, and gets a
birthday with no age rather than no birthday. And 29 February falls on the
28th in a year that has no 29th: somebody born in February has a birthday
in February, and moving it into March is the arithmetic winning over the
fact. Both are conventions; these are the ones that keep the fact intact.

The mock now carries birthdays on most of its contacts, including one with
no year and one on 29 February, so both cases are visible without a real
address book.
This commit is contained in:
2026-09-01 23:28:21 -07:00
parent d300107be0
commit c9ab203b76
8 changed files with 400 additions and 4 deletions
+18
View File
@@ -452,6 +452,24 @@ nothing for anybody else.
- **iCal import** through `CalendarEvent/parse` (a file of any number of
events), from the calendar's own menu, into that calendar. The events are
filed rather than scheduled: no invitations go out to anyone named in them.
- **Birthdays**, as a calendar of its own derived from the birthdays already on
your contacts. Off until switched on in Settings Calendar & contacts, and
hideable from the calendar's own sidebar without turning it off.
**Nothing is written anywhere.** The dates live on the cards; a second copy
of the same fact would drift the first time somebody corrected one, and
keeping a calendar of its own is exactly what ihasmail does not do. An entry
disappears when the contact does, or when the birthday is cleared.
They cannot be edited or deleted, and that falls out of the design rather
than being special-cased: the virtual calendar reports no write rights, so
every control that asks before offering Edit or Delete already declines. The
store refuses a synthesised id as well, whatever calls it.
A card that records only a day and month — the common case — gets a birthday
with no age rather than no birthday. And 29 February falls on the 28th in a
year that has no 29th: somebody born in February has a birthday in February,
and moving it into March is the arithmetic winning over the fact.
## Events