#59 added Time.rfc3339. Its behavior differs from the date gem's existing RFC 3339 parsers (DateTime.rfc3339 and Date.rfc3339, thin wrappers over Date._rfc3339 that accept and reject the same inputs).
Steps to reproduce
$ ruby -v
ruby 4.1.0dev (2026-07-22T21:52:01Z master 8645a948c0) +PRISM [x86_64-linux]
$ TZ=America/New_York ruby -rtime -e 'p Time.rfc3339("1999-12-31T19:00:00")'
$ TZ=America/New_York ruby -rdate -e 'p DateTime.rfc3339("1999-12-31T19:00:00")'
$ TZ=America/New_York ruby -rdate -e 'p Date.rfc3339("1999-12-31T19:00:00")'
$ ruby -rtime -e 'p Time.rfc3339("1999-12-31t19:00:00z")'
$ ruby -rdate -e 'p DateTime.rfc3339("1999-12-31t19:00:00z")'
$ ruby -rdate -e 'p Date.rfc3339("1999-12-31t19:00:00z")'
Expected behavior
The three methods accept and reject the same inputs: "1999-12-31T19:00:00" raises an exception because the date-time grammar in RFC 3339 Section 5.6 (https://datatracker.ietf.org/doc/html/rfc3339#section-5.6) requires time-offset, and "1999-12-31t19:00:00z" is accepted because the Section 5.6 NOTE permits the lower case t/z.
Actual behavior
Time.rfc3339 goes the other way on both inputs:
$ TZ=America/New_York ruby -rtime -e 'p Time.rfc3339("1999-12-31T19:00:00")'
1999-12-31 19:00:00 -0500
$ TZ=America/New_York ruby -rdate -e 'p DateTime.rfc3339("1999-12-31T19:00:00")'
-e:1:in 'DateTime.rfc3339': invalid date (Date::Error)
$ TZ=America/New_York ruby -rdate -e 'p Date.rfc3339("1999-12-31T19:00:00")'
-e:1:in 'Date.rfc3339': invalid date (Date::Error)
$ ruby -rtime -e 'p Time.rfc3339("1999-12-31t19:00:00z")'
/home/yahonda/.local/share/mise/installs/ruby/trunk/lib/ruby/4.1.0+4/time.rb:673:in 'Time#initialize': "+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: t19:00:00z (ArgumentError)
$ ruby -rdate -e 'p DateTime.rfc3339("1999-12-31t19:00:00z")'
#<DateTime: 1999-12-31T19:00:00+00:00 ((2451544j,68400s,0n),+0s,2299161j)>
$ ruby -rdate -e 'p Date.rfc3339("1999-12-31t19:00:00z")'
#<Date: 1999-12-31 ((2451544j,0s,0n),+0s,2299161j)>
Including the offset forms +04 / -0400, which RFC 3339 does not allow (time-numoffset has only the hh:mm form):
| Input |
Time.rfc3339 |
DateTime.rfc3339 / Date.rfc3339 |
"1999-12-31T19:00:00" (no offset) |
accepted, returns local time |
raise |
"1999-12-31T19:00:00+04", "1999-12-31T19:00:00-0400" |
accepted |
raise |
"1999-12-31t19:00:00z" (lower case) |
raises |
accept |
Additional context
The date gem's parser has behaved this way since 2011 (ruby/date@8361362) and documents its input as "a valid RFC 3339 format" (https://github.com/ruby/date/blob/3481cf7ce9149011f1a4eb2bfffc2a0b44c8bf07/ext/date/date_core.c#L4867-L4868).
One deployed use case relies on exactly that behavior: Active Support's Time.rfc3339 has been a thin wrapper around Date._rfc3339 since Rails 5.1 (rails/rails#28272, 2017): https://github.com/rails/rails/blob/fa8f0812160665bff083a089d2bb2fc1817ea03e/activesupport/lib/active_support/core_ext/time/calculations.rb#L69-L83. Its documented and tested contract — ArgumentError when the offset is missing — is the date gem's. On Ruby master the new method now redefines Active Support's (rails/rails#58172 proposes to silence the warning). If the behaviors matched, delegating to the Ruby implementation, as Active Support did with Range#overlap?, would become a possibility.
Since Time.rfc3339 is unreleased, this seems worth settling before Ruby 4.1 ships.
#59 added
Time.rfc3339. Its behavior differs from the date gem's existing RFC 3339 parsers (DateTime.rfc3339andDate.rfc3339, thin wrappers overDate._rfc3339that accept and reject the same inputs).Steps to reproduce
Expected behavior
The three methods accept and reject the same inputs:
"1999-12-31T19:00:00"raises an exception because thedate-timegrammar in RFC 3339 Section 5.6 (https://datatracker.ietf.org/doc/html/rfc3339#section-5.6) requirestime-offset, and"1999-12-31t19:00:00z"is accepted because the Section 5.6 NOTE permits the lower caset/z.Actual behavior
Time.rfc3339goes the other way on both inputs:Including the offset forms
+04/-0400, which RFC 3339 does not allow (time-numoffsethas only thehh:mmform):Time.rfc3339DateTime.rfc3339/Date.rfc3339"1999-12-31T19:00:00"(no offset)"1999-12-31T19:00:00+04","1999-12-31T19:00:00-0400""1999-12-31t19:00:00z"(lower case)Additional context
The date gem's parser has behaved this way since 2011 (ruby/date@8361362) and documents its input as "a valid RFC 3339 format" (https://github.com/ruby/date/blob/3481cf7ce9149011f1a4eb2bfffc2a0b44c8bf07/ext/date/date_core.c#L4867-L4868).
One deployed use case relies on exactly that behavior: Active Support's
Time.rfc3339has been a thin wrapper aroundDate._rfc3339since Rails 5.1 (rails/rails#28272, 2017): https://github.com/rails/rails/blob/fa8f0812160665bff083a089d2bb2fc1817ea03e/activesupport/lib/active_support/core_ext/time/calculations.rb#L69-L83. Its documented and tested contract —ArgumentErrorwhen the offset is missing — is the date gem's. On Ruby master the new method now redefines Active Support's (rails/rails#58172 proposes to silence the warning). If the behaviors matched, delegating to the Ruby implementation, as Active Support did withRange#overlap?, would become a possibility.Since
Time.rfc3339is unreleased, this seems worth settling before Ruby 4.1 ships.