feat: make month week start configurable

Add a Monday-default --week-start option for every weekday and carry it through domain layout, full and compact rendering, event query windows, print mode, and the interactive TUI.

Preserve byte-compatible Monday output and cover rotated headers, spill dates, invalid input, sanitizer behavior, and live terminal restoration.
This commit is contained in:
2026-07-18 09:58:27 +01:00
parent b524e6e33d
commit 989332ef9a
17 changed files with 430 additions and 23 deletions

View File

@@ -48,8 +48,27 @@ void test_dates()
"Monday has index zero");
check(nocal::monday_first_weekday(nocal::Date{2026y / July / 12d}) == 6,
"Sunday has index six");
const nocal::Date wednesday{2026y / July / 1d};
for (unsigned start = 0; start < 7; ++start) {
const weekday week_start{start};
check(nocal::weekday_index(wednesday, week_start) ==
(Wednesday.c_encoding() + 7U - start) % 7U,
"weekday index is relative to each possible week start");
}
check(nocal::weekday_index(wednesday, Monday) ==
nocal::monday_first_weekday(wednesday),
"Monday weekday wrapper preserves configurable-index behavior");
check_throws([] { (void)nocal::parse_date("2023-02-29"); },
"invalid date is rejected");
check_throws(
[] {
(void)nocal::weekday_index(
nocal::Date{2023y / February / 29d}, Monday);
},
"weekday index rejects an invalid date");
check_throws(
[wednesday] { (void)nocal::weekday_index(wednesday, weekday{8}); },
"weekday index rejects an invalid week start");
}
void test_month_layout()
@@ -57,6 +76,8 @@ void test_month_layout()
using namespace std::chrono;
const auto layout = nocal::make_month_layout(2026y / July);
check(layout.cells.size() == 42, "month layout always has 42 cells");
check(layout.week_start == Monday,
"default month layout remains Monday-first");
check(layout.first_visible_date() == nocal::Date{2026y / June / 29d},
"July 2026 begins after Monday-leading spill cells");
check(layout.last_visible_date() == nocal::Date{2026y / August / 9d},
@@ -68,6 +89,50 @@ void test_month_layout()
const auto* july_17 = layout.find(nocal::Date{2026y / July / 17d});
check(july_17 != nullptr && july_17->row == 2 && july_17->column == 4,
"date lookup finds the correct Friday cell");
const auto sunday_layout = nocal::make_month_layout(2026y / July, Sunday);
check(sunday_layout.week_start == Sunday,
"month layout records its configured week start");
check(sunday_layout.first_visible_date() ==
nocal::Date{2026y / June / 28d} &&
sunday_layout.last_visible_date() ==
nocal::Date{2026y / August / 8d},
"Sunday-first July 2026 has the expected spill dates");
const auto* sunday_july_first =
sunday_layout.find(nocal::Date{2026y / July / 1d});
check(sunday_july_first != nullptr && sunday_july_first->row == 0 &&
sunday_july_first->column == 3,
"Sunday-first July 2026 places Wednesday in column three");
for (unsigned start = 0; start < 7; ++start) {
const weekday week_start{start};
const auto configured =
nocal::make_month_layout(2026y / July, week_start);
check(configured.week_start == week_start,
"each configured week start is retained by the layout");
check(weekday{nocal::to_sys_days(configured.first_visible_date())} ==
week_start,
"each layout starts on its configured weekday");
for (std::size_t index = 1; index < configured.cells.size(); ++index) {
check(nocal::to_sys_days(configured.cells[index].date) ==
nocal::to_sys_days(configured.cells[index - 1].date) +
days{1},
"month layout cells are 42 consecutive dates");
}
const auto* july_first =
configured.find(nocal::Date{2026y / July / 1d});
check(july_first != nullptr && july_first->row == 0 &&
july_first->column ==
nocal::weekday_index(nocal::Date{2026y / July / 1d},
week_start),
"July 2026 column follows the configured week start");
}
check_throws(
[] {
(void)nocal::make_month_layout(2026y / July, weekday{8});
},
"month layout rejects an invalid week start");
check_throws([&] { (void)layout.at(6, 0); }, "out-of-grid access is rejected");
}