The de-facto standard for representing dates and times is ISO8601. The standard describes a variety of date/time formats, but as developers on the modern web the format that we routinely encounter looks like this:
YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00)
If you read slightly farther in the spec you'll see that there is also an enhanced form that includes fractions of a second:
YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00)
Note the decimal near the end of the second format (ss.s
). The decimal allows
sub-second precision within date/time, which is potentially useful. Ruby's
standard Date
and Time
libraries support parsing the higher resolution
format via methods like Date.iso6801. Though they support parsing they don't
output fractions of a second when the #iso8601
method is called on an
instance of Time
:
Time.now.iso8601 #=> "2013-07-22T20:57:21-05:00"
Changes in Rails 4
Those of you with a test suite that verifies timestamps may have noticed a change when upgrading to Rails 4. Given a request spec similar to this:
it 'lists an existing post' do
post = Post.create(title: 'Strawberries')
get "http://example.com/api/posts/#{post.id}"
decoded = JSON.parse(last_response.body)
expect(decoded).to eq(
'id' => post.id,
'title' => 'Strawberries',
'created_at' => post.created_at.iso8601,
'updated_at' => post.updated_at.iso8601
)
end
The spec will fail when comparing both timestamps, even though they have been
formatted as iso8601
. The fake diff below attempts to illustrate this:
-"created_at"=>"2013-07-22T21:48:19Z",
+"created_at"=>"2013-07-22T21:48:19.355Z",
The problem stems from an inconsistent overriding of as_json
that only
applies to instances of TimeWithZone
but does not effect the as_json
monkey
patching of Time, Date, and DateTime.
Temporary Solution
The sub-second resolution change, as small as it is, was enough to break
timestamp parsing within some iOS apps. To prevent backward incompatibilities
I've laid my own monkey patch over TimeWithZone
:
# config/initializers/time_with_zone.rb
module ActiveSupport
class TimeWithZone
def as_json(options = nil)
iso8601
end
end
end
Going forward I'm looking to expose configuration that allows the
resolution to be configured. The default behavior in Rails 4 will remain 3
digits of resolution, but setting the resolution to 0
will remove it
entirely.