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

Ruby TZInfo和ActiveSupport::TimeZone无法识别大多数时区缩写,ruby,activesupport,tzinfo,Ruby,Activesupport,Tzinfo,在Ruby中,有了ActiveSupport和TZInfo,我试图解析可以包含时区标识符的任意时间字符串 处理作为偏移量给出的时区(例如,'-08:00')没有问题。这两种长名称的风格似乎都有效(例如,'America/Vancouver','Pacific Time(美国和加拿大)),但常见的缩写和它们的夏令时交替,除了少数例外,大多数都失败了 看看维基百科上的缩写,有190个不同的缩写。当我用TZInfo::Timezone.get(abbrev)测试它们中的每一个时(其中abbrev是一个

在Ruby中,有了ActiveSupport和TZInfo,我试图解析可以包含时区标识符的任意时间字符串

处理作为偏移量给出的时区(例如,
'-08:00'
)没有问题。这两种长名称的风格似乎都有效(例如,
'America/Vancouver'
'Pacific Time(美国和加拿大)
),但常见的缩写和它们的夏令时交替,除了少数例外,大多数都失败了

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

例如:

# 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 }
TZInfo::Timezone.get('MST')) => # >TZInfo::Timezone.get('PST') TZInfo::InvalidTimezoneIdentifier 因此:是否有一种方法可以将大多数或全部常见的3-5字符时区缩写转换为TZInfo或ActiveSupport时区?

或者我必须编写自己的转换助手,随着世界各地时区策略的变化,努力使转换表保持最新

(我确实认识到缩写并非100%可靠或权威,特别是在少数情况下,同一缩写指的是具有不同偏移量的多个时区,但我仍然需要做出“最佳猜测”,而不是没有。)

tzinfo它使用的数据来自:

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

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

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

tzinfo数据是,也没有
PMDT


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

tzinfo和ActiveSupport不支持按其缩写查找时区。中可用的那些缩写实际上是中遗留时区的标识符

不过,您可以使用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 }
自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返回的缩写与您链接到的Wikipedia页面上的缩写不同。在很多情况下,没有标准的缩写,时区数据库和维基百科作者将使用不同的来源和方法