将Date对象转换为TimeWithZone
我需要将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日美国东部时间的开始。
我迟到了,但这仍然是一个很好的问题。 ActiveSupport的in_time_zone
是自OP以来引入的,但它完全符合您的要求,无需解析字符串(慢速)或设置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
如果在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 => #, @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
我强烈建议不要使用to_datetime
或to_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偏移两次validation,一个在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
这样的事情对你有用吗?
'2010-04-01'.to_time.in_time_zone('Eastern Time (US & Canada)').beginning_of_day
减去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 }
- Rails:在时区中获取#beginning_of_day
- 3.days.ago,2.hours.from_now等没有Rails?
- 测试ActiveSupport :: TimeWithZone对象是否相等
- NoMethodError:ActiveSupport的未定义方法`halt_callback_chains_on_return_false =’:模块
- 匿名模块可以用于什么目的?
- rails activesupport通知 – 错误的db运行时值
- 长期无安全方法链
- 在Rails 4.1中,validates_inclusion_of不再一样吗?
- Rails time_ago_in_words产生错误的输出