Add Time.rfc3339 parsing method

The `Time.xmlschema` and consequently its alias `iso8601` accepts
timestamps without a offset in contravention of the RFC 3339
standard. This method enforces that constraint and raises an
`ArgumentError` if it doesn't.
This commit is contained in:
Andrew White 2017-03-03 20:12:47 +00:00
parent f61062c70f
commit 08e05d4a49
3 changed files with 62 additions and 0 deletions

@ -1,3 +1,11 @@
* Add `Time.rfc3339` parsing method
The `Time.xmlschema` and consequently its alias `iso8601` accepts timestamps
without a offset in contravention of the RFC 3339 standard. This method
enforces that constraint and raises an `ArgumentError` if it doesn't.
*Andrew White*
* Add `ActiveSupport::TimeZone.rfc3339` parsing method
Previously there was no way to get a RFC 3339 timestamp into a specific

@ -53,6 +53,29 @@ def at_with_coercion(*args)
end
alias_method :at_without_coercion, :at
alias_method :at, :at_with_coercion
# Creates a +Time+ instance from an RFC 3339 string.
#
# Time.rfc3339('1999-12-31T14:00:00-10:00') # => 2000-01-01 00:00:00 -1000
#
# If the time or offset components are missing then an +ArgumentError+ will be raised.
#
# Time.rfc3339('1999-12-31') # => ArgumentError: invalid date
def rfc3339(str)
parts = Date._rfc3339(str)
raise ArgumentError, "invalid date" if parts.empty?
Time.new(
parts.fetch(:year),
parts.fetch(:mon),
parts.fetch(:mday),
parts.fetch(:hour),
parts.fetch(:min),
parts.fetch(:sec) + parts.fetch(:sec_fraction, 0),
parts.fetch(:offset)
)
end
end
# Returns the number of seconds since 00:00:00.

@ -910,6 +910,37 @@ def test_all_quarter
def test_all_year
assert_equal Time.local(2011, 1, 1, 0, 0, 0)..Time.local(2011, 12, 31, 23, 59, 59, Rational(999999999, 1000)), Time.local(2011, 6, 7, 10, 10, 10).all_year
end
def test_rfc3339_parse
time = Time.rfc3339("1999-12-31T19:00:00.125-05:00")
assert_equal 1999, time.year
assert_equal 12, time.month
assert_equal 31, time.day
assert_equal 19, time.hour
assert_equal 0, time.min
assert_equal 0, time.sec
assert_equal 125000, time.usec
assert_equal(-18000, time.utc_offset)
exception = assert_raises(ArgumentError) do
Time.rfc3339("1999-12-31")
end
assert_equal "invalid date", exception.message
exception = assert_raises(ArgumentError) do
Time.rfc3339("1999-12-31T19:00:00")
end
assert_equal "invalid date", exception.message
exception = assert_raises(ArgumentError) do
Time.rfc3339("foobar")
end
assert_equal "invalid date", exception.message
end
end
class TimeExtMarshalingTest < ActiveSupport::TestCase