From cded44207c916a9f713ad1f85544d56143590bdf Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Tue, 7 Jul 2026 10:14:20 +0200 Subject: [PATCH] core-time: add LocalDate.isoWeekNumber() (ISO-8601 week-of-year) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Centralises the calendar-week-number computation — ISO-8601 week-of-week-based-year (Monday-start, week 1 holds the first Thursday) — that Calendula previously inlined identically in its Week and Month grid headers. Names the scheme choice (there are several incompatible "week number" definitions) and covers the year-boundary rollover cases with a unit test. Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 2 + .../de/jeanlucmakiola/floret/time/IsoWeek.kt | 16 ++++++++ .../jeanlucmakiola/floret/time/IsoWeekTest.kt | 37 +++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 core-time/src/main/kotlin/de/jeanlucmakiola/floret/time/IsoWeek.kt create mode 100644 core-time/src/test/kotlin/de/jeanlucmakiola/floret/time/IsoWeekTest.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index aa11462..f784a29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ are no binary releases; "version" tracks the shared `version` in the root build - `Instant.formatDate()` / `formatTime()` / `formatDateTime()` — locale- and zone-aware display formatting over `kotlin.time.Instant`. - `TimeBridge` — `Long` epoch-millis ↔ `Instant`, for content-provider I/O. + - `LocalDate.isoWeekNumber()` — ISO-8601 week-of-week-based-year, the + calendar-week number for week/month grids (one scheme, shared). - **`core-crash`** (Android library) — privacy-respecting, on-device crash capture and reporting. Writes an allowlist-only report (app/Android/device version, locale, time, stack trace — nothing else) to private storage and diff --git a/core-time/src/main/kotlin/de/jeanlucmakiola/floret/time/IsoWeek.kt b/core-time/src/main/kotlin/de/jeanlucmakiola/floret/time/IsoWeek.kt new file mode 100644 index 0000000..d42ee27 --- /dev/null +++ b/core-time/src/main/kotlin/de/jeanlucmakiola/floret/time/IsoWeek.kt @@ -0,0 +1,16 @@ +package de.jeanlucmakiola.floret.time + +import java.time.LocalDate +import java.time.temporal.IsoFields + +/** + * ISO-8601 week-of-week-based-year (1..53) for [this] date: weeks start on + * Monday and week 1 is the one containing the year's first Thursday. This is the + * calendar-week number shown in week/month grids. + * + * Centralised here so every surface agrees on one scheme — "week number" has + * several incompatible definitions (ISO vs. locale-first-day vs. US), and the + * ISO year a date belongs to can differ from its calendar year around New Year + * (e.g. 2018-12-31 is week 1 of 2019; 2021-01-01 is week 53 of 2020). + */ +fun LocalDate.isoWeekNumber(): Int = get(IsoFields.WEEK_OF_WEEK_BASED_YEAR) diff --git a/core-time/src/test/kotlin/de/jeanlucmakiola/floret/time/IsoWeekTest.kt b/core-time/src/test/kotlin/de/jeanlucmakiola/floret/time/IsoWeekTest.kt new file mode 100644 index 0000000..646a5d9 --- /dev/null +++ b/core-time/src/test/kotlin/de/jeanlucmakiola/floret/time/IsoWeekTest.kt @@ -0,0 +1,37 @@ +package de.jeanlucmakiola.floret.time + +import com.google.common.truth.Truth.assertThat +import org.junit.jupiter.api.Test +import java.time.LocalDate + +class IsoWeekTest { + + @Test + fun `first Thursday's week is week 1`() { + // 2026-01-01 is a Thursday → ISO week 1. + assertThat(LocalDate.of(2026, 1, 1).isoWeekNumber()).isEqualTo(1) + } + + @Test + fun `mid-year week`() { + assertThat(LocalDate.of(2026, 6, 15).isoWeekNumber()).isEqualTo(25) + } + + @Test + fun `late-December days can roll into week 1 of the next ISO year`() { + // 2018-12-31 (Mon) belongs to ISO week 1 of 2019. + assertThat(LocalDate.of(2018, 12, 31).isoWeekNumber()).isEqualTo(1) + } + + @Test + fun `early-January days can belong to the last week of the previous ISO year`() { + // 2021-01-01 (Fri) belongs to ISO week 53 of 2020. + assertThat(LocalDate.of(2021, 1, 1).isoWeekNumber()).isEqualTo(53) + } + + @Test + fun `a 53-week year reports week 53`() { + // 2020-12-31 (Thu) is ISO week 53. + assertThat(LocalDate.of(2020, 12, 31).isoWeekNumber()).isEqualTo(53) + } +}