Are the Date, Time, and DateTime classes necessary?

前端 未结 5 1625
生来不讨喜
生来不讨喜 2020-12-08 00:51

What is the purpose of having Date and Time classes when there is a DateTime class that can handle both?

5条回答
  •  悲&欢浪女
    2020-12-08 01:06

    To summarize what the common ruby time classes are:

    Time

    This is the basic workhorse core ruby time class.

    • Has date and time attributes (year, month, day, hour, min, sec, subsec)
    • Based on floating-point second intervals from unix epoch (1970-01-01)
    • Can handle negative times before unix epoch
    • Can handle time arithmetic in units of seconds
    • Natively works in either UTC or "local" (system time zone)

    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:

    • Can handle any time zone
    • Respects DST
    • Can convert times between zones

    I generally always reach for this when ActiveSupport is available as it takes care of all the time zone pitfalls.

    Date

    • Has date attributes only (year, month, day)
    • Based on integer whole-day intervals from an arbitrary "day zero" (-4712-01-01)
    • Can handle date arithmetic in units of whole days
    • Can convert between dates in the ancient Julian calendar to modern Gregorian

    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

    • Has date and time attributes (year, month, day, hour, min, sec)
    • Based on fractions of whole-day intervals from an arbitrary "day zero" (-4712-01-01)
    • Can handle date arithmetic in units of whole days or fractions

    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.

提交回复
热议问题