converting Date object to TimeWithZone

后端 未结 5 707
清歌不尽
清歌不尽 2020-12-31 18:24

I need to convert a Date object into a TimeWithZone object representing the beginning of that day in a given time zone.

The following approach works, but seems too c

相关标签:
5条回答
  • 2020-12-31 19:01

    If you have Time.zone set in Rails then you can call Date#at_beginning_of_day (see http://api.rubyonrails.org/classes/Date.html#method-i-at_beginning_of_day). Contrast this with Date#to_datetime:

    Time.zone
     => #<ActiveSupport::TimeZone:0x10cf10858 @tzinfo=#<TZInfo::TimezoneProxy: Etc/UTC>, @utc_offset=nil, @current_period=nil, @name="UTC"> 
    
    date = Date.today
     => Thu, 31 May 2012 
    
    date.to_datetime
     => Thu, 31 May 2012 00:00:00 +0000 
    
    date.at_beginning_of_day
     => Thu, 31 May 2012 00:00:00 UTC +00:00 
    
    Time.zone = 'America/Chicago'
     => "America/Chicago" 
    
    date.to_datetime
     => Thu, 31 May 2012 00:00:00 +0000 
    
    date.at_beginning_of_day
     => Thu, 31 May 2012 00:00:00 CDT -05:00
    
    0 讨论(0)
  • 2020-12-31 19:02

    Subtract utc_offset:

    d = Date.today
    Time.zone.class.all.map(&:name).map { |tz| dt = d.to_datetime.in_time_zone(tz); dt -= dt.utc_offset }
    

    Using ActiveSupport::TimeZone[tz] doesn't take daylight savings time into account.

    Time.zone.class.all.map(&:name).map { |tz| o = d.to_datetime.in_time_zone(tz).utc_offset - ActiveSupport::TimeZone[tz].utc_offset }
    
    0 讨论(0)
  • 2020-12-31 19:04

    I strongly recommend against any solution that converts the date to a time using to_datetime or to_time because those methods are unaware of the zone, and tacking in_time_zone onto the result, as some answers suggest, won't retroactively fix the mistake. Also, don't try to build your own daylight saving time math using UTC offsets. You're bound to get it wrong, and you're doing work unnecessarily.

    Use the TimeZone itself which has this logic built in.

    Given a zone and a date, you can get a TimeWithZone for the beginning of the day like this:

    time = zone.local(date.year, date.month, date.day)
    

    If you want a specific time of day other than the beginning, you can pass the hour, minute, and second as the 4th, 5th, and 6th arguments to #local.

    If zone is actually your system's local time zone (Time.zone), then ActiveSupport will let you shorten the above to this:

    time = date.to_time_in_current_zone
    

    All of the above handle daylight saving time correctly. Let's verify that by looking at the UTC offsets for two times, one that's outside DST and one that's within DST:

    irb(main):009:0> zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
    => (GMT-05:00) Eastern Time (US & Canada)
    irb(main):010:0> t1 = zone.local(2013, 1, 1)
    => Tue, 01 Jan 2013 00:00:00 EST -05:00
    irb(main):011:0> t2 = zone.local(2013, 5, 1)
    => Wed, 01 May 2013 00:00:00 EDT -04:00
    irb(main):012:0> t1.utc_offset
    => -18000
    irb(main):013:0> t2.utc_offset
    => -14400
    
    0 讨论(0)
  • 2020-12-31 19:14

    Would something like this work for you?

    '2010-04-01'.to_time.in_time_zone('Eastern Time (US & Canada)').beginning_of_day
    
    0 讨论(0)
  • 2020-12-31 19:23

    I'm late to the party, but this is still a great question. ActiveSupport's in_time_zone was introduced since the O.P., but it does exactly what you want without parsing a string (slow) or setting Time.zone (risky):

    >> date = Date.parse("2010-02-17")
    => Wed, 17 Feb 2010
    >> date.in_time_zone('Eastern Time (US & Canada)')
    => Wed, 17 Feb 2010 00:00:00 EST -05:00
    

    Of course if you want the beginning of day expressed at utc, you can do this:

    >> date.in_time_zone('Eastern Time (US & Canada)').utc
    => 2010-02-17 05:00:00 UTC
    
    0 讨论(0)
提交回复
热议问题