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

7
我正在开发一个Rails应用程序,需要根据特定的偏移量或时区找到夏令时的开始和结束日期。
基本上,我将从用户浏览器接收的时区偏移保存在我的数据库中("+3""-5"),并且当由于夏令时而更改时,我希望对其进行修改。
我知道Time实例变量具有dst?isdst方法,如果存储在其中的日期处于夏令时,则返回true,否则返回false。
 > Time.new.isdst
 => true 

但是使用这种方法来查找夏令时的开始和结束日期将需要太多的资源,并且我还必须为每个时区偏移量执行此操作。

我想知道更好的解决方法。


回顾一下你正在做的事情,会有一个问题浮现。你只是在存储它们的偏移量。但是偏移是否来自于你的时间还是协调世界时(UTC)?如果偏移是UTC,则夏令时不会起作用,除非它们碰巧处于遵守夏令时的地区。或者偏移是相对于你的时间差,而且你是否知道他们是否观察夏令时?如果你的本地时间因为夏令时而发生变化而他们的时间也是如此,那么偏移量保持不变。 - Beartech
我看到你处理这个的方式可能存在一些问题。仅仅因为一个人的偏移量使他们处于美国西部时区,并不能告诉你他们是否遵守夏令时。亚利桑那州大部分地区不遵守夏令时。请注意,我说的是大多数而不是全部。这就是为什么你可能想要将实际时区存储为字符串的原因。 - Beartech
让我给你更多的细节。我正在开发一个太阳能计算器,可以计算出地球上某个位置每天的太阳高度角。该应用程序仅需一次输入地理位置坐标、日期和时区偏移量。在服务器上,我运行一个 cron 作业,每天午夜为每个时区重新计算高度(我可以有来自纽约、伦敦、莫斯科等地的用户)。 - user3790827
显然,我需要为每个时区执行此操作,因此基本上我的cron作业实际上每15分钟运行一次,找到时间变为00:00 AM的时区并重新计算所有内容。现在,我使用从用户浏览器接收到的偏移量存储我的时区(目前,我将获得大多数欧洲和美洲的夏季偏移量)。但是在秋季,这将会改变(而且它改变的日期在各个地区是不同的)。这就是我需要找到DST期间的原因。 - user3790827
回到你的问题,是的,我目前只存储从.getTimezoneOffset()获取的偏移量。我知道在美国的某些州和世界上大多数国家都没有夏令时。但是我认为我可以使用我已经拥有的地理位置坐标来解决这个问题。我可以使用它们获取用户的国家/州并进行适当的查询。 - user3790827
好的,所以你正在获取UTC偏移量,并计划从地理位置获取时区名称。我的回答对你不起作用吗?因为你可以使用它来获取他们的时区名称并获取他们是否遵循夏令时的开始和结束日期。我可能会将其整合到模型中的某些方法中,以便为您的对象提供这些方法。 - Beartech
2个回答

10

好的,根据你所说的和@dhouty的答案

您希望能够输入偏移量并获取一组日期以了解是否存在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"

你可以看到,仅仅使用offset就完全可行,但是我说过,offset并不能告诉你所有信息,所以你可能想要获取他们的实际时区并将其存储为字符串,因为该方法仍然会接受该字符串并给出日期范围。即使您只是获取您的时区和他们之间的时间偏移量,您也可以轻松地计算正确的UTC偏移量:

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

看起来是个不错的建议,我会点赞的,但是我仍然会保持问题的开放状态。 - user3790827

6
你可能正在寻找的是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

啊,你比我更快地完成了,并且使用了一个更简单的例子。 - Beartech

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接