Ruby TZInfo和ActiveSupport::TimeZone无法识别大多数时区缩写



在Ruby中,在ActiveSupport和TZInfo存在的情况下,我正在尝试解析可以包含时区标识符的任意时间字符串。

当作为偏移量(例如,'-08:00'(时处理时区是没有问题的。两种风格的长名称似乎都有效(例如,'America/Vancouver''Pacific Time (US & Canada)'(,但常见的缩写及其夏令时替代品大多失败——只有少数例外。

查看维基百科上的时区缩写列表,有190个不同的缩写。当我用TZInfo::Timezone.get(abbrev)测试它们中的每一个时(其中abbrev是一个包含给定缩写字符串的变量,例如'MST'(,只有 9 个被识别:CET、EET、EST、GMT、HST、MET、MST、UTC 和 WET。

例如:

> TZInfo::Timezone.get('MST')
=> #<TZInfo::DataTimezone: MST>
> TZInfo::Timezone.get('PST')
TZInfo::InvalidTimezoneIdentifier

那么:有没有一种方法可以将大部分或全部常见的 3-5 个字符的时区缩写转换为 TZInfo 或 ActiveSupport 时区?

或者,我是否必须编写自己的转换帮助程序,以便在世界各地的时区政策发生变化时使转换表保持最新状态?

(我确实认识到缩写不是100%可靠或权威的 - 特别是因为在某些情况下,相同的缩写指的是具有不同偏移量的多个时区 - 但我仍然需要做出"最佳猜测"而不是没有。

TZInfo 和 ActiveSupport 不支持按缩写查找时区。TZInfo::Timezone.get提供的那些缩写实际上是时区数据库中旧时区的标识符。

不过,您可以使用 TZInfo 构建自己的从缩写到时区标识符的映射。例如:

# Consider abbreviations used in the current year.
from = Time.utc(Time.now.utc.year)
to = Time.utc(from.year + 1)
# Build an array of [abbreviation, identifier] pairs.
abbrev_identifiers = TZInfo::Timezone.all_data_zones.flat_map do |tz|
abbrevs = tz.offsets_up_to(to, from).map {|o| o.abbreviation.to_s }.uniq
abbrevs.map {|a| [a, tz.identifier] }
end
# Create a Hash using abbreviation as the key and an array of identifiers as the value.
lookup = abbrev_identifiers.each.with_object(Hash.new {|h,k| h[k] = [] }) {|(a, i), h| h[a] << i }

现在,您可以使用lookup获取时区标识符(适用于TZInfo::Timezone.get(。例如,使用 MST:

p lookup['MST']
#=> ["America/Boise", "America/Cambridge_Bay", "America/Chihuahua", "America/Creston", "America/Dawson", "America/Dawson_Creek", "America/Denver", "America/Edmonton", "America/Fort_Nelson", "America/Hermosillo", "America/Inuvik", "America/Mazatlan", "America/Ojinaga", "America/Phoenix", "America/Whitehorse", "America/Yellowknife", "MST", "MST7MDT"]

自 1970 年以来,这些时区中的每一个都会在某个时候使用不同的规则(至少在过去几年发布的时区数据库中(。如果您只对处理较小窗口中的时间感兴趣,则可以将查找筛选为在该窗口中具有不同规则的时区:

current_year = lookup.map.with_object(Hash.new) do |(a, z), h|
h[a] = z.uniq {|i| TZInfo::Timezone.get(i).transitions_up_to(to, from) }
end
p current_year['MST']
#=> ["America/Boise", "America/Chihuahua", "America/Creston", "America/Dawson"]

根据您的应用程序,您可能需要预先计算和存储查找。循环访问时区会在进程的生存期内将每个时区加载到内存中。

请注意,时区数据库中使用的缩写和由 TZInfo 返回的缩写与您链接到的时区缩写列表维基百科页面上的缩写不同。在很多情况下,没有标准的缩写,时区数据库和维基百科作者将使用不同的来源和方法。

tzinfo 描述了它使用的数据来自哪里:

  • 包含时区定义文件的 zoneinfo 目录。这些文件是使用 zic 实用程序从 IANA 时区数据库生成的。大多数类Unix系统都包含一个zoneinfo目录。

  • TZInfo::D ata library(tzinfo-data gem(。TZInfo::D ata 包含一组 Ruby 模块,这些模块也是从 IANA 时区数据库生成的。

时区定义文件通常位于/usr/share/zoneinfo中。在我的机器上,此目录中没有任何内容,例如,PMDT.

tzinfo数据在这里,也没有PMDT

如果找到一组合适的时区信息定义文件,其中包含要查询的区域,理论上tzinfo应该能够使用它们。

最新更新