通过 Ruby/tzinfo 获取给定日期时区的 UTC 偏移量?
Get UTC offset for Timezone at given date via Ruby/tzinfo?
对于任何了解 tzinfo
API:
的人来说,这可能是微不足道的
给定一个来自 tzinfo
的 Timezone
对象,我如何获得给定时间点的 UTC 偏移量(以时区的本地时间或 UTC 给出)?
可以使用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
方法 returns 以秒为单位的偏移量,所以我除以 3600 得到以小时为单位的值。
请注意,我还使用 3600.0
强制结果为浮点数。如果我只使用 3600
,结果将被四舍五入,像 Asia/Kolkata
这样的时区(偏移量为 +05:30
)将给出不正确的结果(5
而不是 5.5
).
请注意,您必须了解夏令时的变化,因为您可能会有间隔或重叠。
在圣保罗时区,夏令时从 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
夏令时结束时,即 2018 年 2 月 18 日,午夜时分时钟将调回到 17 日晚上 11 点(偏移量从 -02:00
变为 -03:00
),因此当地时间在晚上 11 点到午夜存在两次(在两个偏移量中)。
在这种情况下,你必须指定你想要哪个(通过设置period_for_local
的第二个参数),表明你是否想要夏令时的偏移量:
# 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)
似乎在 Ruby 1.9.3 中涉及到一些 hackery(DateTime 到 Time),可能会丢失精度,但这是我根据 @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
对于任何了解 tzinfo
API:
给定一个来自 tzinfo
的 Timezone
对象,我如何获得给定时间点的 UTC 偏移量(以时区的本地时间或 UTC 给出)?
可以使用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
方法 returns 以秒为单位的偏移量,所以我除以 3600 得到以小时为单位的值。
请注意,我还使用 3600.0
强制结果为浮点数。如果我只使用 3600
,结果将被四舍五入,像 Asia/Kolkata
这样的时区(偏移量为 +05:30
)将给出不正确的结果(5
而不是 5.5
).
请注意,您必须了解夏令时的变化,因为您可能会有间隔或重叠。
在圣保罗时区,夏令时从 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
夏令时结束时,即 2018 年 2 月 18 日,午夜时分时钟将调回到 17 日晚上 11 点(偏移量从 -02:00
变为 -03:00
),因此当地时间在晚上 11 点到午夜存在两次(在两个偏移量中)。
在这种情况下,你必须指定你想要哪个(通过设置period_for_local
的第二个参数),表明你是否想要夏令时的偏移量:
# 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)
似乎在 Ruby 1.9.3 中涉及到一些 hackery(DateTime 到 Time),可能会丢失精度,但这是我根据 @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