Date._strptime normally returns nil when the input does not match the format. With a format containing %z or %Z, a single byte >= 0x80 anywhere after the point the zone directive starts scanning makes it raise ArgumentError: invalid byte sequence in US-ASCII instead.
DateTime.strptime/DateTime._strptime use "%FT%T%z" as their default format, so this is reachable without the caller choosing a %z format at all.
require'date's="2011-10-05T22:26:12é"# a valid timestamp plus one non-ASCII byteDateTime._strptime(s)# default format "%FT%T%z"Date._strptime(s,"%Y-%m-%dT%H:%M:%S%z")Time.strptime(s,"%Y-%m-%dT%H:%M:%S%z")# same path via dateDate._strptime("2011-10-05T22:26:12@@@","%Y-%m-%dT%H:%M:%S%z")# control, ASCII garbageActual:
ArgumentError: invalid byte sequence in US-ASCII
ArgumentError: invalid byte sequence in US-ASCII
ArgumentError: invalid byte sequence in US-ASCII
nil # control behaves as documented
Expected: nil in all four cases, as for any other non-matching input.
Realistic inputs are not exotic: a trailing NBSP ("2011-10-05T22:26:12+00:00 ") or a typographic quote pasted into a form field is enough.
Cause: ext/date/date_strptime.c:600 re-tags the remainder of the caller's string as US-ASCII before matching the zone pattern:
m=f_match(pat, rb_usascii_str_new(&str[si], slen-si));
The rest of the file works on the caller's encoding, and date_core.c deliberately accepts any ASCII-compatible encoding (date_core.c:4530) and copies the caller's encoding back onto the zone and leftover values (:4557, :4561), so the US-ASCII tag here looks unintended. Since the zone pattern is ASCII-only, matching against the original encoding, or against a binary string, would give the same matches without the exception.
Note that Date::Error < ArgumentError, but this is a plain ArgumentError, so rescue Date::Error does not catch it while rescue ArgumentError does.
Tested: ruby 3.4.5 / date 3.5.1, and ruby 4.1.0dev (master) / date 3.5.1. Same result on both.
Filing this publicly as a correctness bug: it produces an unexpected exception class, not a memory-safety or resource-exhaustion problem.
Date._strptimenormally returnsnilwhen the input does not match the format. With a format containing%zor%Z, a single byte >= 0x80 anywhere after the point the zone directive starts scanning makes it raiseArgumentError: invalid byte sequence in US-ASCIIinstead.DateTime.strptime/DateTime._strptimeuse"%FT%T%z"as their default format, so this is reachable without the caller choosing a%zformat at all.Actual:
Expected:
nilin all four cases, as for any other non-matching input.Realistic inputs are not exotic: a trailing NBSP (
"2011-10-05T22:26:12+00:00 ") or a typographic quote pasted into a form field is enough.Cause:
ext/date/date_strptime.c:600re-tags the remainder of the caller's string as US-ASCII before matching the zone pattern:The rest of the file works on the caller's encoding, and
date_core.cdeliberately accepts any ASCII-compatible encoding (date_core.c:4530) and copies the caller's encoding back onto thezoneand leftover values (:4557,:4561), so the US-ASCII tag here looks unintended. Since the zone pattern is ASCII-only, matching against the original encoding, or against a binary string, would give the same matches without the exception.Note that
Date::Error < ArgumentError, but this is a plainArgumentError, sorescue Date::Errordoes not catch it whilerescue ArgumentErrordoes.Tested: ruby 3.4.5 / date 3.5.1, and ruby 4.1.0dev (master) / date 3.5.1. Same result on both.
Filing this publicly as a correctness bug: it produces an unexpected exception class, not a memory-safety or resource-exhaustion problem.