Difference between DateTime and Time in Ruby

What's the difference between DateTime and Time classes in Ruby and what factors would cause me to choose one or the other?


Newer versions of Ruby (2.0+) do not really have significant differences between the two classes. Some libraries will use one or the other for historical reasons, but new code does not necessarily need to be concerned. Picking one for consistency is probably best, so try and mesh with what your libraries expect. For example, ActiveRecord prefers DateTime.

In versions prior to Ruby 1.9 and on many systems Time is represented as a 32-bit signed value describing the number of seconds since January 1, 1970 UTC, a thin wrapper around a POSIX-standard time_t value, and is bounded:

Time.at(0x7FFFFFFF)
# => Mon Jan 18 22:14:07 -0500 2038
Time.at(-0x7FFFFFFF)
# => Fri Dec 13 15:45:53 -0500 1901

Newer versions of Ruby are able to handle larger values without producing errors.

DateTime is a calendar-based approach where the year, month, day, hour, minute and second are stored individually. This is a Ruby on Rails construct that serves as a wrapper around SQL-standard DATETIME fields. These contain arbitrary dates and can represent nearly any point in time as the range of expression is typically very large.

DateTime.new
# => Mon, 01 Jan -4712 00:00:00 +0000

So it's reassuring that DateTime can handle blog posts from Aristotle.

When choosing one, the differences are somewhat subjective now. Historically DateTime has provided better options for manipulating it in a calendar fashion, but many of these methods have been ported over to Time as well, at least within the Rails environment.


[Edit July 2018]

All of the below still holds true in Ruby 2.5.1. From the reference documentation:

DateTime does not consider any leap seconds, does not track any summer time rules.

What hasn't been noted in this thread before is one of the few advantages of DateTime: it is aware of calendar reforms whereas Time is not:

[…] Ruby's Time class implements a proleptic Gregorian calendar and has no concept of calendar reform […].

The reference documentation concludes with the recommendation to use Time when exclusively dealing with near-past, current or future dates/times and only use DateTime when, for example, Shakespeare's birthday needs to be accurately converted: (emphasis added)

So when should you use DateTime in Ruby and when should you use Time? Almost certainly you'll want to use Time since your app is probably dealing with current dates and times. However, if you need to deal with dates and times in a historical context you'll want to use DateTime […]. If you also have to deal with timezones then best of luck - just bear in mind that you'll probably be dealing with local solar times, since it wasn't until the 19th century that the introduction of the railways necessitated the need for Standard Time and eventually timezones.

[/Edit July 2018]

As of ruby 2.0, most of the information in the other answers is out of date.

In particular, Time is now practically unbound. It can be more or less than even 63 bits away from Epoch:

irb(main):001:0> RUBY_VERSION
=> "2.0.0"
irb(main):002:0> Time.at(2**62-1).utc # within Integer range
=> 146138514283-06-19 07:44:38 UTC
irb(main):003:0> Time.at(2**128).utc # outside of Integer range
=> 10783118943836478994022445751222-08-06 08:03:51 UTC
irb(main):004:0> Time.at(-2**128).utc # outside of Integer range
=> -10783118943836478994022445747283-05-28 15:55:44 UTC

The only consequence of using larger values should be performance, which is better when Integers are used (vs. Bignums (values outside of Integer range) or Rationals (when nanoseconds are tracked)):

Since Ruby 1.9.2, Time implementation uses a signed 63 bit integer, Bignum or Rational. The integer is a number of nanoseconds since the Epoch which can represent 1823-11-12 to 2116-02-20. When Bignum or Rational is used (before 1823, after 2116, under nanosecond), Time works slower as when integer is used. (http://www.ruby-doc.org/core-2.1.0/Time.html)

In other words, as far as I understand, DateTime no longer covers a wider range of potential values than Time.

In addition, two previously unmentioned restrictions of DateTime should probably be noted:

DateTime does not consider any leapseconds, does not track any summer time rules. (http://www.ruby-doc.org/stdlib-2.1.0/libdoc/date/rdoc/Date.html#class-Date-label-DateTime)

First, DateTime has no concept of leap seconds:

irb(main):001:0> RUBY_VERSION
=> "2.0.0"
irb(main):002:0> require "date"
=> true
irb(main):003:0> t = Time.new(2012,6,30,23,59,60,0)
=> 2012-06-30 23:59:60 +0000
irb(main):004:0> dt = t.to_datetime; dt.to_s
=> "2012-06-30T23:59:59+00:00"
irb(main):005:0> t == dt.to_time
=> false
irb(main):006:0> t.to_i
=> 1341100824
irb(main):007:0> dt.to_time.to_i
=> 1341100823

For the above example to work with Time, the OS needs to support leap seconds and timezone information needs to be set correctly, e.g. through TZ=right/UTC irb (on many Unix systems).

Second, DateTime has very limited understanding of time zones and in particular has no concept of daylight savings. It pretty much handles time zones as simple UTC + X offsets:

irb(main):001:0> RUBY_VERSION
=> "2.0.0"
irb(main):002:0> require "date"
=> true
irb(main):003:0> t = Time.local(2012,7,1)
=> 2012-07-01 00:00:00 +0200
irb(main):004:0> t.zone
=> "CEST"
irb(main):005:0> t.dst?
=> true
irb(main):006:0> dt = t.to_datetime; dt.to_s
=> "2012-07-01T00:00:00+02:00"
irb(main):007:0> dt.zone
=> "+02:00"
irb(main):008:0> dt.dst?
NoMethodError: undefined method `dst?' for #<DateTime:0x007f34ea6c3cb8>

This may cause trouble when times are entered as DST and then converted into a non-DST time zone without keeping track of the correct offsets outside of DateTime itself (many operating systems may actually already take care of this for you).

Overall, I'd say that nowadays Time is the better choice for most applications.

Also note an important difference on addition: when you add a number to a Time object, it is counted in seconds, but when you add a number to a DateTime, it is counted in days.