feat: add ISO 8601 week date parsing (YYYY-Www-D)#121
Conversation
strptime now recognizes ISO week dates in both extended (2025-W17-1)
and basic (2025W171) forms, with optional time+timezone suffix.
Previously, week dates were silently misparsed by the boost format
regex — "2025-W17-1" matched with $month{"w17"} = undef, falling
back to the current month and producing a wrong date. The new pattern
is matched before ISO compact and boost patterns to prevent this.
Week-to-date conversion uses the Jan-4-is-always-in-week-1 algorithm.
Validates week 1-53, day 1-7; rejects invalid inputs.
Co-Authored-By: Claude <noreply@anthropic.com>
Greptile SummaryThis PR adds ISO 8601 week-date parsing (
Confidence Score: 4/5Not safe to merge as-is: a 52-week year passed with W53 silently produces a wrong date instead of returning undef. One clear P1 defect: the week-53 guard accepts W53 for all years even though most years only have 52 ISO weeks, reintroducing the silent-wrong-date pattern the PR explicitly targeted. The rest of the implementation (algorithm, regex, timezone handling, existing tests) is correct and well-structured. lib/Date/Parse.pm lines 99-101 (week validation), and a corresponding negative test for W53 on a 52-week year in t/iso-week-date.t
|
| Filename | Overview |
|---|---|
| lib/Date/Parse.pm | Adds ISO 8601 week-date parsing branch; week-53 validation only checks ≤53 and does not verify the year actually contains 53 weeks, silently producing a wrong date for most years |
| t/iso-week-date.t | 35 new tests covering extended/basic format, cross-year week 1, week 53 (only for 2020 which does have 53 weeks), timezone, fractional seconds, strptime components, and invalid inputs; no test for an invalid W53 on a 52-week year |
Reviews (1): Last reviewed commit: "feat: add ISO 8601 week date parsing (YY..." | Re-trigger Greptile
| # Validate: week 1-53, day 1-7 | ||
| if ($wk_n >= 1 && $wk_n <= 53 && $wk_d >= 1 && $wk_d <= 7) { | ||
| # Convert ISO week date to calendar date. |
There was a problem hiding this comment.
Week 53 validation is incomplete
The guard $wk_n <= 53 accepts week 53 for every year, but most years only have 52 ISO weeks. When a year does not contain week 53 (e.g. 2025), the algorithm silently computes a date that falls in week 1 of the next year — the same silent-wrong-answer problem the PR explicitly set out to eliminate.
A year has 53 ISO weeks if and only if its Jan 4 day-of-week is Thursday (ISO 4), or equivalently if Dec 28 falls in week 53. You can compute the actual number of weeks for $wk_y and gate the return:
my $jan4_t = timegm(0, 0, 12, 4, 0, $wk_y);
my $jan4_dow = (gmtime($jan4_t))[6] || 7;
my $weeks_in_year = ($jan4_dow == 4 || ($jan4_dow == 3 && _is_leap($wk_y))) ? 53 : 52;
if ($wk_n >= 1 && $wk_n <= $weeks_in_year && $wk_d >= 1 && $wk_d <= 7) {Without this fix str2time("2025-W53-1") (2025 has only 52 weeks) returns a timestamp for January 5, 2026 instead of undef.
| # Must be matched before ISO compact and boost patterns to prevent misparse. | ||
| # Without this, "2025-W17-1" would be caught by the boost pattern with | ||
| # $month{"w17"} = undef, silently producing a wrong date. | ||
| if ($dtstr =~ s/\s(\d{4})-?w(\d\d)-?(\d)(?:[-Tt ](\d\d?)(?:([-:]?)(\d\d?)(?:\5(\d\d?)(?:[.,](\d+))?)?)?)?(?=\D)/ /) { |
There was a problem hiding this comment.
Week number captured as two digits — single-digit weeks silently fail
The pattern w(\d\d) requires exactly two week digits. The extended format 2025-W01-1 works (leading zero supplied), but if a caller ever omits the leading zero (2025-W1-1) the week-date branch is skipped entirely and the input falls through to later patterns that may produce a wrong result. ISO 8601 mandates two-digit week numbers, so this is arguably correct, but a brief inline note (or a one-digit fallback with \d{1,2}) would prevent silent misparse for tolerant callers.
What
Add support for parsing ISO 8601 week dates (
YYYY-Www-DandYYYYWwwD) instrptimeandstr2time.Why
ISO week dates were silently misparsed. The boost format regex matched
2025-W17-1with$month{"w17"} = undef, falling back to the current month — producing a wrong date without any error. For example,str2time("2025-W17-1")returned April 1 instead of April 21.Silent wrong answers are worse than parse failures.
How
2025-W17-1) and basic (2025W171) forms2025-W17-1T10:30:00Z)2025-W01-1= Dec 30, 2024)Date::Languagemodules viagen_parser()Testing
t/iso-week-date.tcovering: extended/basic format, cross-year week 1, week 53, time+timezone, fractional seconds, strptime components, validation of invalid inputs, multiple years for algorithm correctness🤖 Generated with Claude Code
Quality Report
Changes: 2 files changed, 150 insertions(+), 1 deletion(-)
Code scan: clean
Tests: skipped
Branch hygiene: clean
Generated by Kōan post-mission quality pipeline