Ruby TZInfo和ActiveSupport::TimeZone无法识别大多数时区缩写
在Ruby中,有了ActiveSupport和TZInfo,我试图解析可以包含时区标识符的任意时间字符串 处理作为偏移量给出的时区(例如,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是一个
'-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页面上的缩写不同。在很多情况下,没有标准的缩写,时区数据库和维基百科作者将使用不同的来源和方法