Получить смещение utc от часового пояса в Javascript

Мне нужна функция Javascript, которая с учетом часового пояса возвращает текущее смещение utc.

Например, theFuncIneed('США/Восток') -> 240

Есть идеи?

Спасибо


person user3124032    schedule 20.12.2013    source источник
comment
Создайте объект с именами часовых поясов в качестве свойств и смещением в качестве значения. Обратите внимание, что не существует стандарта для названий часовых поясов, они время от времени меняются и есть дубликаты (например, EST).   -  person RobG    schedule 21.12.2013
comment
Проверьте moment.js, у него есть метод zone().   -  person Barmar    schedule 21.12.2013
comment
@RobG – US/Eastern – это действительный идентификатор часового пояса IANA. Вы можете найти его внизу списка здесь.   -  person Matt Johnson-Pint    schedule 21.12.2013
comment
@MattJohnson - я не понимаю, о чем вы. IANA предоставляет смещения часовых поясов для определенных местоположений. Данные IANA не являются стандартными или даже авторитетными, и нет никаких претензий на то, что они таковые (заголовок файла данных гласит: «Эти данные ни в коем случае не являются авторитетными; если вы думаете, что знаете лучше, отредактируйте файл»).   -  person RobG    schedule 21.12.2013
comment
@RobG - Согласен. База данных часовых поясов IANA не является стандартом в том смысле, в каком стандартом является ISO 8601. Но по соглашению стандартом де-факто он повсеместно используется во многих операционных системах, языках программирования и библиотеках. Таким образом, несмотря на отсутствие гарантий, весьма вероятно, что US/Eastern будет распознаваться всеми реализациями.   -  person Matt Johnson-Pint    schedule 21.12.2013
comment
@ MattJohnson - я думаю, что это далеко не так. Я хотел предупредить, что не существует стандарта для обозначений часовых поясов. База данных IANA включает ряд исключений для часового пояса, наблюдаемого в месте, по сравнению с его географическим положением, а также для начала и окончания перехода на летнее время, когда это противоречит официальным датам.   -  person RobG    schedule 22.12.2013


Ответы (5)


Вообще это невозможно.

  • US/Eastern – это идентификатор часового пояса. (На самом деле это псевдоним America/New_York, который является настоящим идентификатором.)

  • 240 – это смещение часового пояса. Чаще пишется как -04:00 (переверните знак, разделите на 60).

  • Восточный часовой пояс США состоит из восточного стандартного времени со смещением -05:00 и восточного летнего времени со смещением -04:00.

Таким образом, совсем неточно говорить US/Eastern = 240. Пожалуйста, прочитайте вики тега часового пояса, особенно раздел под названием "Время Зона != Смещение".

Теперь вы запросили текущее смещение, что возможно. Если вы предоставите ссылку на дату + время, вы можете решить эту проблему.

  • Для местного часового пояса компьютера, на котором выполняется код javascript, это встроено в .getTimeZoneOffset() из любого экземпляра объекта Date.

  • Но если вы хотите использовать его для конкретного часового пояса, вам нужно будет использовать одну из библиотек, которые я перечислил. здесь.

person Matt Johnson-Pint    schedule 20.12.2013
comment
ECMA-262 определяет смещение часового пояса как UTC - localTime, выраженное в минутах. . Поэтому я думаю, что на форуме javascript разумно писать +240 там, где в противном случае можно было бы использовать -04:00. - person RobG; 21.12.2013
comment
.getTimeZoneOffset() должно быть .getTimezoneOffset() без заглавной буквы Z - person Graham Walters; 21.08.2015

Следующая функция может использоваться для возврата смещения UTC с учетом часового пояса:

const getTimezoneOffset = (timeZone, date = new Date()) => {
  const tz = date.toLocaleString("en", {timeZone, timeStyle: "long"}).split(" ").slice(-1)[0];
  const dateString = date.toString();
  const offset = Date.parse(`${dateString} UTC`) - Date.parse(`${dateString} ${tz}`);
  
  // return UTC offset in millis
  return offset;
}

Его можно использовать как:

const offset = getTimezoneOffset("Europe/London");
console.log(offset);
// expected output => 3600000
person ranjan_purbey    schedule 31.07.2020
comment
Это немного неправильно. Смещение UTC должно быть положительным для часовых поясов, которые отстают от него, и отрицательным для часовых поясов, которые опережают его. Следовательно, вычитание должно быть обратным: Date.parse(`${dateString} ${tz}`) - Date.parse(`${dateString} UTC`);. В противном случае он не будет соответствовать встроенному Date.prototype.getTimezoneOffset. - person fgblomqvist; 30.09.2020
comment
Кроме того, ожидаемый результат для "Europe/London" должен быть 0 - person Avraham; 04.03.2021
comment
@Avraham смещение зависит от даты из-за летнего времени. Вот почему функция принимает дату в качестве второго аргумента. - person ranjan_purbey; 05.03.2021
comment
недопустимый стиль времени. Должно быть timeZoneName - person dude; 07.07.2021
comment
Результаты в NaN для меня. - person dude; 07.07.2021

Вы можете сделать это, используя moment.js

moment.tz('имя часового пояса').utcOffset()

Хотя это связано с использованием moment-timezone.js

person Alexandru R    schedule 12.08.2015
comment
вы также должны установить пакет момент-часовой пояс - person Brian McCall; 11.04.2018

Сегодня это стало возможным благодаря Международному API. :

Реализация Intl основана на icu4c. Если вы покопаетесь в исходном коде, вы обнаружите, что название часового пояса различается в зависимости от локали, например:

for (const locale of ["ja", "en", "fr"]) {
  const timeZoneName = Intl.DateTimeFormat(locale, {
    timeZoneName: "short",
    timeZone: "Asia/Tokyo",
  })
    .formatToParts()
    .find((i) => i.type === "timeZoneName").value;
  console.log(timeZoneName);
}

К счастью, существует локаль Interlingua (тег языка – ia), в которой используются те же шаблон (например, GMT+11:00) для названий часовых поясов.

Фрагмент ниже может удовлетворить ваши потребности:

const getOffset = (timeZone) => {
  const timeZoneName = Intl.DateTimeFormat("ia", {
    timeZoneName: "short",
    timeZone,
  })
    .formatToParts()
    .find((i) => i.type === "timeZoneName").value;
  const offset = timeZoneName.slice(3);
  if (!offset) return 0;

  const matchData = offset.match(/([+-])(\d+)(?::(\d+))?/);
  if (!matchData) throw `cannot parse timezone name: ${timeZoneName}`;

  const [, sign, hour, minute] = matchData;
  let result = parseInt(hour) * 60;
  if (sign === "+") result *= -1;
  if (minute) result += parseInt(minute);

  return result;
};

console.log(getOffset("US/Eastern")); // 240
console.log(getOffset("Atlantic/Reykjavik")); // 0
console.log(getOffset("Asia/Tokyo")); // -540

Этот способ может быть немного сложным, но он хорошо работает в моем производственном проекте. Я надеюсь, что это поможет и вам :)

Обновлять

Большое спасибо Борту за указание на опечатку. Я исправил фрагмент.

person Weihang Jian    schedule 08.10.2020
comment
В if (minute) result + parseInt(minute); есть опечатка. Это должно быть +=? - person Bort; 03.11.2020
comment
Также это не учитывает переход на летнее время. - person Bort; 03.11.2020
comment
Это не работает для значений в примере — возвращает 0 для США/Востока и Атлантики/Рейкьявика. - person Avraham; 04.03.2021
comment
@Avraham Это возвращает 0 для getOffset("Atlantic/Reykjavik"), потому что это текущее смещение этого часового пояса относительно UTC. Для getOffset("US/Eastern") возвращается 240, а не 0. - person kleinfreund; 11.05.2021
comment
@kleinfreund Странно, когда я запускаю второй фрагмент кода, я получаю 0 вместо console.log(getOffset("US/Eastern")); - person Avraham; 12.05.2021
comment
@Avraham Что вам возвращает Intl.DateTimeFormat("ia", { timeZoneName: "short", timeZone: "US/Eastern", }).formatToParts().find((i) => i.type === "timeZoneName").value? Возможно, что-то, что не начинается с "GMT"? И если да, запускаете ли вы это в браузере или, возможно, в среде Node.js? - person kleinfreund; 12.05.2021
comment
Для меня timeZoneName будет MESZ в Chrome, и это приводит к Cannot parse timezone name. Ввод: Europe/Zurich. В файрфоксе работает. - person dude; 07.07.2021

Ответ @ranjan_purbey приводит к NaN для меня, а ответ @Weihang Jian вызывает исключение в Chrome (но работает в Firefox).

Поэтому, основываясь на всех ответах, я придумал следующую функцию, которая в основном представляет собой комбинацию обоих ответов, успешно работающих вместе для меня:

const getTimeZoneOffset(timeZone) {
    const date = new Date().toLocaleString('en', {timeZone, timeZoneName: 'short'}).split(' ');
    const timeZoneName = date[date.length - 1];
    const offset = timeZoneName.slice(3);
    if (!offset) {
      return 0;
    }
    const matchData = offset.match(/([+-])(\d+)(?::(\d+))?/);
    if (!matchData) {
      throw new Error(`Cannot parse timezone name: ${timeZoneName}`);
    }
    const [, sign, hour, minute] = matchData;
    let result = parseInt(hour, 10) * 60;
    if (sign === '+') {
      result *= -1;
    }
    if (minute) {
      result += parseInt(minute, 10);
    }
    return result;
}
person dude    schedule 07.07.2021
comment
Я не получаю смещение часового пояса для Нью-Йорка, там просто указано EDT (по сравнению с UTC-4 или GMT-5). - person Mr. Polywhirl; 30.07.2021