Ruby/Rails 中的夏令时开始和结束日期

Posted

技术标签:

【中文标题】Ruby/Rails 中的夏令时开始和结束日期【英文标题】:Daylight Saving Time start and end dates in Ruby/Rails 【发布时间】:2015-11-23 13:24:49 【问题描述】:

我正在开发一个 Rails 应用程序,我需要在给定特定偏移量或时区的情况下找到夏令时的开始和结束日期。

我基本上将从用户浏览器接收到的时区偏移量("+3""-5")保存在我的数据库中,并且我想在它因夏令时而更改时对其进行修改。

我知道Time 实例变量具有dst?isdst 方法,如果存储在其中的日期是否处于夏令时,它们会返回真或假。

 > Time.new.isdst
 => true 

但是使用它来查找夏令时的开始和结束日期会占用太多资源,而且我还必须为我拥有的每个时区偏移量这样做。

我想知道一个更好的方法。

【问题讨论】:

所以回顾你正在做的事情会想到一个问题。您只存储它们的偏移量。但是是您的时间还是UTC的偏移量?如果偏移量是 UTC,则 DST 不会起作用,除非它们恰好位于遵守 DST 的区域中。还是偏移量与您的不同,再次,您会知道他们是否在观察 DST 吗?如果您的当地时间因为 DST 而发生变化,而他们的时间也发生变化,则偏移量保持不变。 我发现您执行此操作的方式可能存在一些问题。仅仅因为一个人的偏移量使他们处于美国西部时区,并不能告诉您他们是否遵守 DST。亚利桑那州的大部分地区不遵守 DST。请注意,我说的是大多数,而不是全部。这就是您可能希望将实际时区存储为字符串的原因。 让我给你更多的细节。我正在研究一个太阳能计算器,它计算地球上给定位置每天的太阳仰角。该应用程序只获取一次地理位置坐标、日期和时区偏移量。在服务器上,我运行一个 cron 作业,每天午夜重新计算每个时区的海拔高度(我可以拥有来自纽约、伦敦、莫斯科等地的用户)。 显然我需要为每个时区执行此操作,所以基本上我的 cron 作业实际上每 15 分钟运行一次,找到时间变为 00:00 AM 的时区并重新计算所有内容。现在我使用从用户浏览器收到的偏移量存储我的时区(目前我将获得欧洲和美国大部分地区的夏季偏移量)。但是到了秋天,这会发生变化(并且变化的日期因地区而异)。这就是我需要找到 DST 期间的原因。 回到您的问题,是的,我目前只存储从.getTimezoneOffset() 获得的偏移量。我知道美国的某些州和世界上大多数国家都没有 DST。但是我认为我可以使用我已经拥有的地理位置坐标来解决这个问题。我可以使用它们获取用户的国家/地区并进行适当的查询。 【参考方案1】:

好的,根据你所说的和@dhouty's answer:

您希望能够输入偏移量并获得一组日期,以了解是否存在 DST 偏移量。我建议最终使用由两个 DateTime 对象组成的范围,因为它在 Rails 中很容易用于多种用途...

require 'tzinfo'

def make_dst_range(offset)

  if dst_end = ActiveSupport::TimeZone[offset].tzinfo.current_period.local_end
     dst_start = ActiveSupport::TimeZone[offset].tzinfo.current_period.local_start
     dst_range = dst_start..dst_end
  else
     dst_range = nil
  end

end

现在,由于 ActiveSupport 附带的糖,您有了一种方法,它可以做的不仅仅是抵消。您可以执行以下操作:

make_dst_range(-8)
#=> Sun, 08 Mar 2015 03:00:00 +0000..Sun, 01 Nov 2015 02:00:00 +0000

make_dst_range('America/Detroit')
#=> Sun, 08 Mar 2015 03:00:00 +0000..Sun, 01 Nov 2015 02:00:00 +0000

make_dst_range('America/Phoenix')
#=> nil     #returns nil because Phoenix does not observe DST

my_range = make_dst_range(-8)
#=> Sun, 08 Mar 2015 03:00:00 +0000..Sun, 01 Nov 2015 02:00:00 +0000

今天恰好是 8 月 29 日所以:

my_range.cover?(Date.today)
  #=> true
my_range.cover?(Date.today + 70)
  #=> false
my_range.first
  #=> Sun, 08 Mar 2015 03:00:00 +0000
  #note that this is a DateTime object. If you want to print it use:
my_range.first.to_s
  #=> "2015-03-08T03:00:00+00:00"
my_range.last.to_s
  #=> "2015-11-01T02:00:00+00:00"

ActiveSupport 为您提供各种展示的好东西:

my_range.first.to_formatted_s(:short)
  #=> "08 Mar 03:00"
my_range.first.to_formatted_s(:long)
  #=> "March 08, 2015 03:00"
my_range.first.strftime('%B %d %Y')
   #=> "March 08 2015"

正如您所看到的,仅使用偏移量是完全可行的,但正如我所说,偏移量并不能告诉您一切,因此您可能想要获取他们的实际时区并将其存储为字符串,因为该方法会很乐意接受该字符串并仍然为您提供日期范围。即使您只是获得您所在区域与他们所在区域之间的时间偏移,您也可以轻松地将其校正为 UTC 偏移:

my_offset = -8
their_offset = -3
utc_offset = my_offset + their_offset

【讨论】:

似乎是个不错的建议,我会投赞成票,但是我仍然会保持这个问题。【参考方案2】:

您可能正在寻找的是TZInfo::TimezonePeriod。具体来说local_start/utc_startlocal_end/utc_end这两个方法。

给定一个时区偏移,你可以得到一个TZInfo::TimezonePeriod 对象

ActiveSupport::TimeZone[-8].tzinfo.current_period

或者如果你有一个时区名称,你也可以得到一个TZInfo::TimezonePeriod 对象

ActiveSupport::TimeZone['America/Los_Angeles'].tzinfo.current_period

【讨论】:

啊,你打败了我,还有一个更简单的例子。

以上是关于Ruby/Rails 中的夏令时开始和结束日期的主要内容,如果未能解决你的问题,请参考以下文章

如何在 SQL Server 中创建夏令时开始和结束函数

获取任何时区的 DST 开始和结束日期

R - 从一年中获取夏令时开始和结束

澳大利亚/墨尔本时区的 Java 8 中的日期时间夏令时问题

夏令时持续时间

Java API 获得一年的夏令时边界