将Date对象转换为TimeWithZone

时间:2010-03-25 18:42:23

标签: ruby-on-rails ruby activesupport

我需要将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日开始。

5 个答案:

答案 0 :(得分:6)

我迟到了,但这仍然是一个很好的问题。 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

答案 1 :(得分:4)

如果您在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

答案 2 :(得分:2)

我强烈建议不要使用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

答案 3 :(得分:0)

这样的事情对你有用吗?

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

答案 4 :(得分:0)

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