Project

General

Profile

Actions

Bug #22375

open

`Time.new(String)` can disagree with itself about the date

Bug #22375: `Time.new(String)` can disagree with itself about the date

Added by rwstauner (Randy Stauner) about 12 hours ago.

Status:
Open
Assignee:
-
Target version:
-
[ruby-core:126831]

Description

Time.new("2026-02-30T00:00:00Z") returns a Time object that is internally inconsistent.
It shows as Feb 30 or Mar 2 depending on what methods are called:

t = Time.new("2026-02-30T00:00:00Z") # => 2026-02-30 00:00:00 UTC

t.mon     # => 2
t.day     # => 30 (February 30 is invalid)

t.to_i                     # => 1772409600
Time.utc(2026, 3, 2).to_i  # => 1772409600 # (Mar 2 gives same #to_i result)
t == Time.utc(2026, 3, 2)  # => true

t + 0  # => 2026-03-02 00:00:00 UTC # plus zero normalizes
t      # => 2026-02-30 00:00:00 UTC # still invalid

t.to_a # => [0, 0, 0, 2, 3, 2026, 1, 61, false, "UTC"] # to_a shows Mar 2
t      # => 2026-03-02 00:00:00 UTC # the to_a call appears to have normalized the object

The numeric constructor normalizes Time.new(2026, 2, 30) to March 2.
A string with an offset other than Z (+02:00) also normalizes:

require "time"
Time.new("2026-02-30T00:00:00+02:00") # => 2026-03-02 00:00:00 +0200

This has been the behavior since at least Ruby 3.3.0.

However, since commit 77d0d22b
(2026-06-22), Time.iso8601, Time.rfc3339, and Time.xmlschema all parse by calling
Time.new(string) instead of building from Time.utc(y, m, d, ...). So they now inherit
this behavior:

require "time"
Time.iso8601("2026-02-30T00:00:00.000000000Z").iso8601(9) # => "2026-02-30T00:00:00.000000000Z"

Currently the test suite has a test that raises ArgumentError for an invalid month
but we don't appear to have tests for invalid days.

I think we should add a test for this case and fix Time.new(string) to normalize consistently.

No data to display

Actions

Also available in: PDF Atom