3

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

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

查看Wikipedia 上的时区缩写列表,有 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% 可靠或权威的——尤其是在少数情况下,相同的缩写指的是具有不同偏移量的多个时区——但我仍然需要做出“最佳猜测”而不是没有.)

4

2 回答 2

3

TZInfo 和 ActiveSupport 不支持通过缩写查找时区。那些可用的缩写实际上是Time Zone DatabaseTZInfo::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 返回的缩写与您链接到的时区缩写列表Wikipedia 页面上的缩写不同。在很多情况下,没有标准的缩写,时区数据库和维基百科的作者将使用不同的来源和方法。

于 2020-06-12T20:20:58.507 回答
0

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

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

  • TZInfo::Data 库(tzinfo-data gem)。TZInfo::Data 包含一组同样从 IANA 时区数据库生成的 Ruby 模块。

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

tzinfo-data 在这里,也没有PMDT

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

于 2020-06-09T21:06:27.870 回答