Skip to content

feat: add ISO 8601 week date parsing (YYYY-Www-D)#121

Merged
atoomic merged 1 commit into
cpan-authors:mainfrom
Koan-Bot:koan.atoomic/add-iso-week-date-parsing
Apr 26, 2026
Merged

feat: add ISO 8601 week date parsing (YYYY-Www-D)#121
atoomic merged 1 commit into
cpan-authors:mainfrom
Koan-Bot:koan.atoomic/add-iso-week-date-parsing

Conversation

@Koan-Bot
Copy link
Copy Markdown

@Koan-Bot Koan-Bot commented Apr 21, 2026

What

Add support for parsing ISO 8601 week dates (YYYY-Www-D and YYYYWwwD) in strptime and str2time.

Why

ISO week dates were silently misparsed. The boost format regex matched 2025-W17-1 with $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

  • New regex pattern matched before ISO compact and boost patterns to intercept week dates
  • Week-to-date conversion uses the ISO standard algorithm (Jan 4 is always in week 1)
  • Supports both extended (2025-W17-1) and basic (2025W171) forms
  • Supports optional time+timezone suffix (2025-W17-1T10:30:00Z)
  • Validates week 1-53 and day 1-7; rejects invalid inputs
  • Cross-year boundaries handled correctly (e.g., 2025-W01-1 = Dec 30, 2024)
  • Works with Date::Language modules via gen_parser()

Testing

  • 35 new tests in t/iso-week-date.t covering: extended/basic format, cross-year week 1, week 53, time+timezone, fractional seconds, strptime components, validation of invalid inputs, multiple years for algorithm correctness
  • All existing tests pass unchanged

🤖 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

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>
@atoomic atoomic marked this pull request as ready for review April 26, 2026 12:28
@atoomic atoomic merged commit b68c9bd into cpan-authors:main Apr 26, 2026
22 checks passed
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 26, 2026

Greptile Summary

This PR adds ISO 8601 week-date parsing (YYYY-Www-D / YYYYWwwD) to Date::Parse, correctly placing the new branch before the boost pattern that previously silently misparsed week-date strings. The algorithm (anchoring on Jan 4 always being in week 1) is sound and cross-year boundaries are handled correctly.

  • P1 – Silent wrong date for W53 on 52-week years: The validation accepts week 53 for every year ($wk_n <= 53), but most years only have 52 ISO weeks. str2time(\"2025-W53-1\") returns a timestamp in January 2026 instead of undef, reproducing the same silent-wrong-answer class of bug the PR was written to fix.
  • The test suite covers 2020-W53 (a year that genuinely has 53 weeks) but has no negative test for 2025-W53 (a 52-week year), so the gap is undetected.

Confidence Score: 4/5

Not 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

Important Files Changed

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

Comment thread lib/Date/Parse.pm
Comment on lines +99 to +101
# 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.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 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.

Comment thread lib/Date/Parse.pm
# 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)/ /) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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.

@Koan-Bot Koan-Bot deleted the koan.atoomic/add-iso-week-date-parsing branch April 27, 2026 09:14
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.

2 participants