Skip to content

fix: str2time rejects epoch -1 (Dec 31, 1969 23:59:59 UTC)#112

Merged
atoomic merged 1 commit into
cpan-authors:mainfrom
Koan-Bot:koan.atoomic/fix-epoch-neg1-check
Apr 26, 2026
Merged

fix: str2time rejects epoch -1 (Dec 31, 1969 23:59:59 UTC)#112
atoomic merged 1 commit into
cpan-authors:mainfrom
Koan-Bot:koan.atoomic/fix-epoch-neg1-check

Conversation

@Koan-Bot

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

Copy link
Copy Markdown

What

str2time returns undef for any date string representing epoch -1 (December 31, 1969 23:59:59 UTC).

Why

The epoch -1 detection logic compares join("", $ss, $mm, $hh, $day, $month, $year) against a hardcoded reference string to distinguish the legitimate -1 return from timegm/timelocal errors. Since the year normalization code (two-digit → four-digit) was added, $year is always 1969 at the comparison point — but the reference string still used 69. The join produces "59592331111969" (14 chars) while the check expected "595923311169" (12 chars), so the comparison always fails and str2time incorrectly returns undef.

How

  • UTC path: updated the hardcoded comparison from "595923311169" to "59592331111969"
  • Local-time path: localtime(-1) returns year=69 (years since 1900), so added += 1900 to match the normalized $year

Testing

  • 5 new tests in t/edge-cases.t covering ISO 8601, RFC 2822, 2-digit year, and space-separated formats
  • All 1225 tests pass across the full suite

🤖 Generated with Claude Code


Quality Report

Changes: 2 files changed, 24 insertions(+), 2 deletions(-)

Code scan: clean

Tests: skipped

Branch hygiene: clean

Generated by Kōan post-mission quality pipeline

The epoch -1 detection logic compares parsed date components against
a hardcoded string to distinguish the legitimate -1 return value from
timegm/timelocal errors. However, since two-digit years are now
normalized to four-digit years before the timegm call, the comparison
always fails — the join produces "59592331111969" (year=1969) but the
check expected "595923311169" (year=69).

Fix: update the UTC check to use the 4-digit year string, and adjust
the local-time check to add 1900 to the localtime(-1) year component.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@atoomic atoomic marked this pull request as ready for review April 26, 2026 12:38
@atoomic atoomic merged commit 2fd7166 into cpan-authors:main Apr 26, 2026
22 checks passed
@greptile-apps

greptile-apps Bot commented Apr 26, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes str2time incorrectly returning undef for epoch -1 (Dec 31, 1969 23:59:59 UTC) by updating the sentinel-string comparison to reflect the 4-digit year that $year now holds after normalization. Both the UTC (timegm) and local-time (timelocal) paths are addressed, and 5 new tests confirm the fix across common date formats.

Confidence Score: 5/5

Safe to merge — the logic fix is correct and well-tested; only minor style suggestions remain.

Both the UTC and local-time path fixes are logically sound. The hardcoded string '59592331111969' is verifiable by inspection (ss=59, mm=59, hh=23, day=31, month=11, year=1969). The local-time fix correctly applies += 1900 to match the normalized $year. All remaining comments are P2.

No files require special attention.

Important Files Changed

Filename Overview
lib/Date/Parse.pm Correctly updates both UTC and local-time epoch-(-1) comparison strings to account for the 4-digit year normalization; one minor style concern (unconditional localtime(-1) call).
t/edge-cases.t Adds 5 well-chosen tests covering ISO 8601, RFC 2822, 2-digit year, and space-separated UTC formats; local-time (else) branch for epoch -1 is not directly tested.

Reviews (1): Last reviewed commit: "fix: str2time rejects epoch -1 (Dec 31, ..." | Re-trigger Greptile

Comment thread lib/Date/Parse.pm
Comment on lines +316 to +317
my @_neg1 = localtime(-1);
$_neg1[5] += 1900;

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 localtime(-1) now called unconditionally

localtime(-1) is called on every execution of the local-time branch, whereas the original code only called it lazily (short-circuit evaluation, only when $result == -1). This is a negligible overhead in practice, but architecturally the call would be cleaner kept inline as before — the only change needed is adding + 1900 to the join.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment thread t/edge-cases.t
Comment on lines +257 to 277
# --- Epoch -1 (Dec 31, 1969 23:59:59 UTC) parsing ---
# timegm() returns -1 for this date, which collides with Perl's error
# convention. str2time must distinguish the legitimate -1 from errors.
{
my $t = str2time("31 Dec 1969 23:59:59 GMT");
is($t, -1, "epoch -1: 4-digit year with GMT");

my $t2 = str2time("1969-12-31T23:59:59Z");
is($t2, -1, "epoch -1: ISO 8601 with Z");

my $t3 = str2time("Wed, 31 Dec 1969 23:59:59 +0000");
is($t3, -1, "epoch -1: RFC 2822 with +0000");

my $t4 = str2time("31 Dec 69 23:59:59 GMT");
is($t4, -1, "epoch -1: 2-digit year with GMT");

my $t5 = str2time("1969-12-31 23:59:59 +0000");
is($t5, -1, "epoch -1: ISO with space separator");
}

done_testing;

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 Local-time path not covered by new tests

All five new tests specify an explicit UTC offset (GMT, Z, +0000), so they exercise only the if (defined $zone) branch. The local-time else branch fix ($_neg1[5] += 1900) is not exercised by any of them. Consider adding a test without a timezone specifier to cover that code path.

@Koan-Bot Koan-Bot deleted the koan.atoomic/fix-epoch-neg1-check 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