Skip to content

A calendar, with the dates computed rather than asked for - #79

Merged
ralyodio merged 1 commit into
mainfrom
feat/calendar
Sep 8, 2026
Merged

A calendar, with the dates computed rather than asked for#79
ralyodio merged 1 commit into
mainfrom
feat/calendar

Conversation

@ralyodio

@ralyodio ralyodio commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

The second slice of #64. A month as a grid, with per-day styling: marks for days with something on, a selected day, an optional header and weekday row, and a week that starts on Monday or Sunday.

ui.calendar({
  year: 2026,
  month: 9,
  selected: 8,
  marks: [{ day: 15, color: theme.warning }],
});

No host calendar is consulted, anywhere

Every language this library is ported to has a different date type — different epochs, different month numbering, different opinions about time zones — and a widget whose output depends on any of that cannot be held to a fixture. So the arithmetic is here, in terms every port already has: integers. Sakamoto's method for the weekday, the full Gregorian leap rule, and a test that greps the TypeScript source to make sure nobody quietly reaches for Date later.

Three things that are easy to get wrong

The leap rule is the whole rule — every four years, except centuries, except every fourth century. Truncating it at "every four years" is right for 1901-2099 and wrong for 1900 and 2100, which is the kind of bug that sits quietly for decades.

The leap count divides towards negative infinity. Go, C++ and Zig all truncate towards zero by default, which is off by one for years before 1; each port uses a floor division rather than the operator.

A month is four, five or six week rows depending on where its first day falls. calendarHeight returns the month's own, and a test walks all twelve months of a year asserting that what was reserved is what got drawn — reserving five for everything leaves some months a row short and others a blank row long.

Scope

Six ports and five fixtures, all additive. The Ruby, PHP and Perl bridges gained a calendar node and COBOL a CALENDAR/CALDAY pair, which both COBOL bridges render identically. The widget gallery is now 31. This is one slice of #64; the bounds-aware canvas and Shadow are still open there, which is why this closes nothing.

Verified

  • 322 TypeScript tests under both bun test and node --test, typecheck clean
  • 81 widget scenes matching the reference in Rust, Go, Python, Zig and C++ (was 76)
  • 11/11 ctest, ports/bindings/tests/check.py, all eleven galleries
  • both COBOL bridges render the new verbs identically (diff clean)
  • next build for the site

🤖 Generated with Claude Code

https://claude.ai/code/session_017Df2FNu5DhinMV2soRz3cy

The second slice of #64. A month as a grid, with per-day styling: marks for
days with something on, a selected day, an optional header and weekday row,
and a week that starts on Monday or Sunday.

The interesting decision is that no host calendar is consulted anywhere. Every
language this library is ported to has a different date type -- different
epochs, different month numbering, different opinions about time zones -- and
a widget whose output depends on any of that cannot be held to a fixture. So
the arithmetic is here, in terms every port already has: integers. Sakamoto's
method for the weekday, the full Gregorian leap rule, and a test that greps
the TypeScript source to make sure nobody quietly reaches for `Date` later.

Three things that are easy to get wrong and are pinned by tests:

The leap rule is the whole rule -- every four years, except centuries, except
every fourth century. Truncating it at "every four years" is right for
1901-2099 and wrong for 1900 and 2100, which is the kind of bug that sits
quietly for decades.

The leap count divides towards negative infinity. Go, C++ and Zig all truncate
towards zero by default, which is off by one for years before 1; each port
uses a floor division rather than the operator.

A month is four, five or six week rows depending on where its first day falls.
`calendarHeight` returns the month's own, and a test walks all twelve months
of a year asserting that what was reserved is what got drawn -- reserving five
for everything leaves some months a row short and others a blank row long.

Six ports and five fixtures, all additive. The Ruby, PHP and Perl bridges
gained a calendar node and COBOL a CALENDAR/CALDAY pair, which both COBOL
bridges render identically. The widget gallery is now 31.

Verified: 322 TS tests under bun and node; 81 widget scenes matching the
reference in Rust, Go, Python, Zig and C++; 11/11 ctest; the bindings check;
all eleven galleries; both COBOL bridges diff clean; the site builds.

Part of #64

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Df2FNu5DhinMV2soRz3cy
@ralyodio
ralyodio merged commit 3882fb4 into main Sep 8, 2026
18 checks passed
@ralyodio
ralyodio deleted the feat/calendar branch September 8, 2026 23:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant