【问题标题】:Get UTC offset for Timezone at given date via Ruby/tzinfo?通过 Ruby/tzinfo 在给定日期获取时区的 UTC 偏移量?
【发布时间】:2018-01-17 17:04:34
【问题描述】:

这对于任何了解tzinfo API 的人来说可能都是微不足道的:

给定来自tzinfoTimezone 对象,我如何获取给定时间点的UTC 偏移量(在时区的本地时间或UTC 中给出)?

【问题讨论】:

  • 与 UTC 的偏移不是特定时区时间的属性,而不是日期或时区的属性吗?
  • 偏移量应该是DateTimeTimezone 的属性。 2017 年 8 月 9 日星期三 19:21,America/New_York 与 UTC 的偏移量为 -4:00。
  • 它是DateTime 的属性of(或绑定到)Timezone,而不是(某物和)Timezone 的属性。它也不是“给定日期”的属性。
  • 是的,我知道 DateTime 已经在其中存储了一个时区。

标签: ruby datetime timezone timezone-offset tzinfo


【解决方案1】:

您可以使用period_for_local method。对于这些示例,我使用我居住的时区 (America/Sao_Paulo),其中偏移量在冬季(3 月至 10 月)为 -03:00,在夏季(夏令时)为 -02:00

# Sao Paulo timezone
zone = TZInfo::Timezone.new('America/Sao_Paulo')

# date in January (Brazilia Summer Time - DST)
d = DateTime.new(2017, 1, 1, 10, 0)

period = zone.period_for_local(d)
puts period.offset.utc_total_offset / 3600.0

# date in July (Brazilia Standard Time - not in DST)
d = DateTime.new(2017, 7, 1, 10, 0)

period = zone.period_for_local(d)
puts period.offset.utc_total_offset / 3600.0

输出是:

-2.0
-3.0

utc_total_offset 方法以秒为单位返回偏移量,所以我除以 3600 得到了以小时为单位的值。

请注意,我还使用了3600.0 来强制结果为浮点数。如果我只使用3600,结果将被四舍五入,像Asia/Kolkata(偏移量为+05:30)这样的时区会给出不正确的结果(5 而不是5.5)。


请注意,您必须注意 DST 更改,因为您可以有间隙或重叠。

在圣保罗时区,夏令时从 2017 年 10 月 15 日开始:在午夜,时钟向前移动到凌晨 1 点(并且偏移量从 -03:00 更改为 -02:00),因此所有当地时间 00:00 到 01 之间: 00 无效。在这种情况下,如果你尝试获取偏移量,它会得到一个PeriodNotFound 错误:

# DST starts at October 15th, clocks shift from midnight to 1 AM
d = DateTime.new(2017, 10, 15, 0, 30)
period = zone.period_for_local(d) # error: TZInfo::PeriodNotFound

当 DST 于 2018 年 2 月 18 日结束时,午夜时钟移回 17 日晚上 11 点(并且偏移量从 -02:00 更改为 -03:00),因此本地时间在晚上 11 点和午夜之间存在两次(在偏移量)。

在这种情况下,你必须指定你想要的(通过设置period_for_local的第二个参数),表明你是否想要DST的偏移量:

# DST ends at February 18th, clocks shift from midnight to 11 PM of 17th
d = DateTime.new(2018, 2, 17, 23, 30)
period = zone.period_for_local(d, true) # get DST offset
puts period.offset.utc_total_offset / 3600.0 # -2.0

period = zone.period_for_local(d, false) # get non-DST offset
puts period.offset.utc_total_offset / 3600.0 # -3.0

如果不指定第二个参数,会得到TZInfo::AmbiguousTime 错误:

# error: TZInfo::AmbiguousTime (local time exists twice due do DST overlap)
period = zone.period_for_local(d)

【讨论】:

  • 很好的答案。另请注意,如果 UTC 时间已知,period_for_utc 会更好,因为它不会受到您提到的间隙和重叠的影响。
  • 只是一个小的后续问题:有没有办法利用 strftime("%:z") 格式?
  • @ChristopherOezbek 一种方法是将日期设置为偏移量并格式化:d.new_offset(period.offset.utc_total_offset / 3600 / 24.0).strftime('%:z')(对我来说,它打印-03:00)。不幸的是,我找不到直接格式化偏移值的方法
  • 感谢您的详细回复。您刚刚帮我解决了我的测试套件中的一个问题,该问题是由美国最近的 DST 切换引起的。
【解决方案2】:

似乎在 Ruby 1.9.3 中涉及一些黑客(日期时间到时间),可能会丢失精度,但这是我根据@Hugo 的回答得出的结果:

module TZInfo

class Timezone

    def utc_to_local_zone(dateTime)
        return dateTime.to_time.getlocal(self.period_for_utc(dateTime).utc_total_offset)
    end

    def offset_to_s(dateTime, format = "%z")
        return utc_to_local_zone(dateTime).strftime(format)
    end 
end

end

【讨论】:

    猜你喜欢
    • 2013-01-14
    • 1970-01-01
    • 2011-05-01
    • 1970-01-01
    • 2017-08-30
    • 1970-01-01
    • 2014-02-04
    • 2010-11-19
    • 2014-01-09
    相关资源
    最近更新 更多