如何获取时区别名的规范时区名称?

How to get the canonical timezone name of a timezone alias?

我想创建一个函数,将时区别名转换为所述时区的规范名称。

Status of timezones from Wikipedia

例如,如果我有 Africa/Accra,我希望能够查找 Africa/Abidjan 是时区的规范名称。

function getCanonicalName(name) {
  // TODO
}
getCanonicalName('Africa/Accra'); // => 'Africa/Abidjan'

我自己调查过的事情

我曾尝试使用 moment-timezoneluxon 来解析时区名称,但似乎没有办法将规范时区作为这些库的一部分进行逆向工程。

根据他们的源代码中的moment-timezone docs, you can get the Canonical Name of a timezone from the "packed" data representation, but looking at the implementation of pack,似乎只是通过source.name

luxon 有一个 normalizeZone 帮助程序,但它似乎只是 return 一个来自各种输入类型的 Zone 实例。但是,它不会规范化区域名称。 luxon Zones 上有一个 isUniversal 标志,但是这个标志似乎与 DST 有关,而不是时区状态。

鉴于时区数据库每年仅更新一次,我认为我的解决方案维护成本低。我在这里遵循一种非常粗略的方法。实际上,您可以首先将维基百科页面上显示的信息获取到 JSON,然后通过 运行 在浏览器控制台中获取包含所有时区及其规范时区的地图:

const keys = [];
const timezones = [];

$("table:first thead tr th").each(function () {
    keys.push($(this).text().replace(/\n/g, ''));
});

$("table:first tbody tr").each(function () {
    const obj = {};
    let i = 0;

    $(this).children("td").each(function () {
        obj[keys[i]] = $(this).text().replace(/\n/g, '');
        i++;
    });

    timezones.push(obj);
});

const canonicalTimezones = Object.assign({}, ...timezones.map(x => ({ [x['TZ database name']]: (x['Type'] === 'Canonical' ? x['TZ database name'] : x['Notes'].substring(8)) })));

JSON.stringify(canonicalTimezones);

您可以将输出保存在某个文件中。然后你只需要在你的代码中解析它并轻松获得相应的规范时区。

import canonicalTimezones from './canonicalTimezones.json';

function getCanonicalName(name) {
  return canonicalTimezones[name];
}

这可能无法将特定区域识别为别名,因为它已在 September 2021 中更新:

但总的来说:

import moment from 'moment';
import momenttz from 'moment-timezone';

// From moment-timezone, but not exported
function normalizeName (name) {
  return (name || '').toLowerCase().replace(/\//g, '_');
}

function getCanonicalName(name) {
  const normalizedName = normalizeName(name);

  // A canonical zone might exist
  if (moment.tz._zones[normalizedName]) {
    return moment.tz._names[normalizedName];
  }

  // If not, there'll be a link to a canonical name
  const canonicalZoneNormalized = moment.tz._links[normalizeName(name)];

  // And return the friendly name
  return moment.tz._names[canonicalZoneNormalized];
}

console.log(getCanonicalName('Pacific/Wallis')); // => Pacific/Tarawa
console.log(getCanonicalName('Pacific/Tarawa')); // => Pacific/Tarawa