【问题标题】:converting Date object to TimeWithZone将 Date 对象转换为 TimeWithZone
【发布时间】:2011-01-31 21:10:38
【问题描述】:

我需要将 Date 对象转换为 TimeWithZone 对象,该对象表示给定时区中那一天的开始。

以下方法可行,但似乎过于复杂,因为它需要我将日期转换为字符串:

?> date = Date.parse("2010-02-17")
=> Wed, 17 Feb 2010
>> ActiveSupport::TimeZone['Eastern Time (US & Canada)'].parse(date.to_s)
=> Wed, 17 Feb 2010 00:00:00 EST -05:00
>> ActiveSupport::TimeZone['UTC'].parse(date.to_s)
=> Wed, 17 Feb 2010 00:00:00 UTC 00:00

我错过了更好的方法吗?

编辑: 人们建议以下变化:

?> date.to_datetime.in_time_zone('Eastern Time (US & Canada)').beginning_of_day
=> Tue, 16 Feb 2010 00:00:00 EST -05:00

如您所见,这不是等效的转换,因为它是在美国东部标准时间 2 月 16 日开始时离开我的,而不是美国东部标准时间 2 月 17 日开始。

【问题讨论】:

  • 看起来您的解决方案可能是正确的方法。
  • 我已经编辑了我的回复来解决这个问题。
  • 给自己解决方案! =) 时区的时间总是很混乱。

标签: ruby-on-rails ruby activesupport


【解决方案1】:

我迟到了,但这仍然是一个很好的问题。 ActiveSupport 的 in_time_zone 是自 O.P. 以来引入的,但它完全符合您的要求,无需解析字符串(慢)或设置 Time.zone(有风险):

>> 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

当然,如果您想以 UTC 表示一天的开始,您可以这样做:

>> date.in_time_zone('Eastern Time (US & Canada)').utc
=> 2010-02-17 05:00:00 UTC

【讨论】:

    【解决方案2】:

    如果你在 Rails 中设置了Time.zone,那么你可以调用Date#at_beginning_of_day(参见http://api.rubyonrails.org/classes/Date.html#method-i-at_beginning_of_day)。对比一下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
    

    【讨论】:

    • Time.zone 线程安全的,它将区域设置为Thread.current[:time_zone]source
    • 感谢您的更正,我删除了该评论,但留下了这条评论,以免人们认为您疯了 ;)
    【解决方案3】:

    我强烈建议不要使用任何使用to_datetimeto_time 将日期转换为时间的解决方案,因为这些方法不知道该区域,并且正如一些答案所暗示的那样,将in_time_zone 附加到结果上不会追溯修正错误。此外,不要尝试使用 UTC 偏移量构建自己的夏令时数学。你一定会弄错,而且你做的工作是不必要的。

    使用内置此逻辑的 TimeZone 本身。

    给定一个区域和一个日期,您可以获得一天开始的 TimeWithZone,如下所示:

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

    如果您想要一天中的特定时间而不是开始,您可以将小时、分钟和秒作为第 4、第 5 和第 6 个参数传递给 #local

    如果 zone 实际上是您系统的本地时区 (Time.zone),那么 ActiveSupport 会让您将上述内容缩短为:

    time = date.to_time_in_current_zone
    

    以上所有内容都正确处理夏令时。让我们通过两次查看 UTC 偏移量来验证这一点,一次在 DST 之外,一次在 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
    

    【讨论】:

      【解决方案4】:

      这样的东西对你有用吗?

      '2010-04-01'.to_time.in_time_zone('Eastern Time (US & Canada)').beginning_of_day
      

      【讨论】:

        【解决方案5】:

        减去 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 }
        

        使用 ActiveSupport::TimeZone[tz] 不会考虑夏令时。

        Time.zone.class.all.map(&:name).map { |tz| o = d.to_datetime.in_time_zone(tz).utc_offset - ActiveSupport::TimeZone[tz].utc_offset }
        

        【讨论】:

          猜你喜欢
          • 2013-12-30
          • 1970-01-01
          • 2011-05-28
          • 1970-01-01
          • 1970-01-01
          • 2011-11-21
          • 1970-01-01
          • 2015-02-11
          相关资源
          最近更新 更多