Search code examples
rubytimeutc

Understanding Time#utc?


As I understand, UTC means a time is given in timezone +00:00. But Ruby thinks different in Time#utc?. I've observed this in Ruby 2.5.1:

a = Time.new(2018,6,13, 9,0,0, '+00:00')
# => 2018-06-13 09:00:00 +0000 

b = Time.utc(2018,6,13, 9,0,0)
# => 2018-06-13 09:00:00 UTC 

a == b 
# => true

a.utc? 
# => false (WHY???)

b.utc?
# => true

IMHO, a.utc? should return true. Is there any explanation?

Addition: From the Ruby docs for Time#utc?

Returns true if time represents a time in UTC (GMT).

What exactly means "representing a time in UTC/GMT"? An offset of 0 is not enough, obviously.


Solution

  • Implementation-wise, Ruby's (i.e. MRI) internal time structure has a gmt field which specifies the time's type:

    PACKED_STRUCT_UNALIGNED(struct time_object {
        wideval_t timew; /* time_t value * TIME_SCALE.  possibly Rational. */
        struct vtm vtm;
        uint8_t gmt:3; /* 0:localtime 1:utc 2:fixoff 3:init */
        uint8_t tm_got:1;
    });
    

    The utc? method merely checks whether gmt is 1.

    Therefore, a time instance in local time or a time instance with explicit offset will never be utc?, even if your system's timezone offset is UTC+0:

    Time.local(2018)      #=> 2018-01-01 00:00:00 +0000
    Time.local(2018).utc? #=> false
    
    Time.new(2018)        #=> 2018-01-01 00:00:00 +0000
    Time.new(2018).utc?   #=> false
    

    as opposed to a time instance created via utc: (note that the offset is shown as UTC)

    Time.utc(2018)        #=> 2018-01-01 00:00:00 UTC
    Time.utc(2018).utc?   #=> true
    

    You could check the utc_offset instead:

    t = Time.new(2018) #=> 2018-01-01 00:00:00 +0000
    t.utc_offset       #=> 0
    t.utc_offset.zero? #=> true