What is the purpose of having Date
and Time
classes when there is a DateTime
class that can handle both?
To summarize what the common ruby time classes are:
Time
This is the basic workhorse core ruby time class.
There are really 3 kinds of Time object when it comes to dealing with time zones, let's look at a summer time to show DST:
utc = Time.utc(2012,6,1) # => 2012-12-21 00:00:00 UTC utc.zone # => "UTC" utc.dst? # => false utc.utc? # => true utc.utc_offset # => 0 local = Time.local(2012,6,1) # => 2012-06-01 00:00:00 -0700 local.zone # => "PDT" local.dst? # => true local.utc? # => false local.utc_offset # => -25200 nonlocal = Time.new(2012,6,1,0,0,0, "-07:00") # => 2012-06-01 00:00:00 -0700 nonlocal.zone # => nil nonlocal.dst? # => false nonlocal.utc? # => false nonlocal.utc_offset # => -25200
The last 2 look similar, but beware: you should not do arithmetic with a non-local Time. This is simply a time with a UTC offset and no zone, so it doesn't know the rules of DST. Adding time over the DST boundary will not change the offset and the resulting time-of-day will be wrong.
ActiveSupport::TimeWithZone
This one is worth mentioning here since it's what you use in Rails. Same as Time, plus:
I generally always reach for this when ActiveSupport is available as it takes care of all the time zone pitfalls.
Date
Date is more useful than Time whenever you deal in whole days: no time zones to worry about! (I'm surprised this doesn't deal with the modern Persian calendar since it knows about the obsolete Julian calendar from centuries ago.)
DateTime
Personally, I never have reason to use this: it's slow, it handles time without considering time zones, and it has an inconsistent interface. I find it leads to confusion whenever you assume you have a Time-like object, but it actually behaves like a Date instead:
Time.new(2012, 12, 31, 0, 0, 0) + 1 == Time.new(2012, 12, 31, 0, 0, 1) DateTime.new(2012, 12, 31, 0, 0, 0) + 1 == DateTime.new(2013, 1, 1, 0, 0, 0)
Further, it has a meaningless "zone" attribute (note how non-local Time objects warn you that zone == nil
), and you can't know anything else about it before turning it into a Time first:
dt = DateTime.new(2012,12,6, 1, 0, 0, "-07:00") dt.zone # => "-07:00" dt.utc? # => NoMethodError: undefined method `utc?' dt.dst? # => NoMethodError: undefined method `dst?' dt.utc_offset # => NoMethodError: undefined method `utc_offset'
Dealing with microseconds to check for rounding is also a little strange. You would think that because it doesn't have a usec
attribute that it only deals in whole numbers, but you'd be wrong:
DateTime.now.usec # => NoMethodError: undefined method `usec' DateTime.now.to_time.usec => 629399
In short, unless you're dealing with astronomical events in the ancient past and need to convert the Julian date (with time of day) to a modern calendar, please don't use DateTime. If anyone has an actual use case for this class, I'd love to read your comments.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With