Как ISO 8601 отформатировать дату со смещением часового пояса в JavaScript?

Цель: найдите local time и UTC time offset, затем создайте URL в следующем формате.

Пример URL: / Actions / Sleep? Duration = 2002-10-10T12: 00: 00−05: 00

Формат основан на рекомендации W3C: http://www.w3.org/TR/xmlschema11-2/#dateTime

В документации сказано:

Например, 2002-10-10T12: 00: 00−05: 00 (полдень 10 октября 2002 года по центральному летнему времени, а также по восточному поясному времени в США) равно 2002-10-10T17: 00: 00Z, на пять часов позже 2002-10-10T12: 00: 00Z.

Итак, исходя из моего понимания, мне нужно найти мое местное время с помощью new Date (), затем использовать функцию getTimezoneOffset (), чтобы вычислить разницу, а затем прикрепить ее к концу строки.

1.Получите местное время в формате

var local = new Date().format("yyyy-MM-ddThh:mm:ss"); //today (local time)

вывод

2013-07-02T09:00:00

2. получить смещение времени UTC по часам

var offset = local.getTimezoneOffset() / 60;

вывод

7

3.Создать URL (только временная часть)

var duration = local + "-" + offset + ":00";

вывод:

2013-07-02T09:00:00-7:00

Приведенный выше результат означает, что мое местное время - 2013/07/02, 9 утра, а разница от UTC составляет 7 часов (UTC на 7 часов опережает местное время).

Пока что вроде работает, но что, если getTimezoneOffset () вернет отрицательное значение, например -120?

Мне интересно, как должен выглядеть формат в таком случае, потому что я не могу понять из документа W3C. Заранее спасибо.


person Meow    schedule 02.07.2013    source источник


Ответы (10)


Приведенное ниже должно работать правильно и для всех браузеров (спасибо @MattJohnson за подсказку)

function toIsoString(date) {
  var tzo = -date.getTimezoneOffset(),
      dif = tzo >= 0 ? '+' : '-',
      pad = function(num) {
          var norm = Math.floor(Math.abs(num));
          return (norm < 10 ? '0' : '') + norm;
      };

  return date.getFullYear() +
      '-' + pad(date.getMonth() + 1) +
      '-' + pad(date.getDate()) +
      'T' + pad(date.getHours()) +
      ':' + pad(date.getMinutes()) +
      ':' + pad(date.getSeconds()) +
      dif + pad(tzo / 60) +
      ':' + pad(tzo % 60);
}

var dt = new Date();
console.log(toIsoString(dt));

person Steven Moseley    schedule 02.07.2013
comment
Вы хотите сказать, что я могу использовать знак плюса, если из getTimezoneOffset () возвращается отрицательное значение (-120), например, 2013-07-02T09: 00: 00 + 2: 00? - person Meow; 02.07.2013
comment
Знак указывает смещение местного времени от GMT. - person Steven Moseley; 02.07.2013
comment
@ masato-san Вы должны перевернуть знак, см. определение в developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ - person bfavaretto; 02.07.2013
comment
@StevenMoseley Я знаю, я согласен с твоим ответом. OP, похоже, смутил эту часть. - person bfavaretto; 02.07.2013
comment
Только что понял, что ваш исходный ответ, не относящийся к IE 8, неверен! Вы не настраиваете время, вы только добавляете смещение. Это отличает ваши два ответа. Только ваша версия проверки IE8 верна. Последний создаст 2013-11-26T14: 52: 26 + 01: 00, а первый создаст 2013-11-26T15: 52: 30 + 01: 00 - person oligofren; 26.11.2013
comment
Здесь функция pad возвращает правильную строку для каждого раздела даты, кроме миллисекунд. Например, для 5 мс в качестве входа будет возвращено 05, что должно быть 005. Вот ссылка с минимально измененной версией той же функции jsbin - person Safique Ahmed Faruque; 08.02.2017
comment
Обратите внимание: в этом ответе есть небольшая ошибка. Смещение часового пояса не будет рассчитано правильно, если зона имела смещение в минутах и ​​было отрицательным (например, -09: 30), например, в некоторых местах во Франции и Канаде. Mod возвращает отрицательное число, поэтому выполнение floor () перед abs () непреднамеренно сделало смещение БОЛЕЕ отрицательным. Чтобы исправить эту ошибку, используйте abs () и THEN floor (). Пытался отредактировать ответ, но, судя по всему, это изменение было предназначено для обращения к автору сообщения и не имеет смысла в качестве редактирования. - person tytk; 24.08.2017
comment
@tytk - Отличный улов! Это действительно тонко. Я добавил ваше исправление в ответ. Спасибо за комментарий! - person Steven Moseley; 30.08.2017
comment
Обычно считается плохой практикой модифицировать прототип глобального подобного рода. Почему бы просто не определить функцию, которая возвращает дату, и оставить на усмотрение пользователя, хотят ли они изменить глобальные переменные среды? - person callum; 31.03.2021
comment
Спасибо. Читая все ответы, я все еще удивляюсь, насколько javascript выглядит таким самостоятельным языком для некоторых вещей, связанных с датой. - person hlobit; 15.07.2021

getTimezoneOffset() возвращает знак, противоположный формату, требуемому спецификацией, на которую вы ссылаетесь.

Этот формат также известен как ISO8601 или, точнее, как RFC3339.

В этом формате UTC представлено с помощью Z, а все другие форматы представлены смещением от UTC. Значение такое же, как в JavaScript, но порядок вычитания инвертирован, поэтому результат имеет противоположный знак.

Кроме того, в собственном объекте Date с именем format нет метода, поэтому ваша функция в # 1 не будет работать, если вы не используете библиотеку для этого. См. эту документацию.

Если вы ищете библиотеку, которая может работать с этим форматом напрямую, я рекомендую попробовать moment.js. Фактически, это формат по умолчанию, поэтому вы можете просто сделать это:

var m = moment();    // get "now" as a moment
var s = m.format();  // the ISO format is the default so no parameters are needed

// sample output:   2013-07-01T17:55:13-07:00

Это хорошо протестированное кросс-браузерное решение, обладающее множеством других полезных функций.

person Matt Johnson-Pint    schedule 02.07.2013
comment
Использование toISOString () не сработает. Формат +01: 00 требует, чтобы часть времени была местным. toISOString () предоставит строку времени в формате UTC. - person Austin France; 13.07.2015
comment
@AustinFrance - Ты прав! Я удивлен, что сделал эту ошибку в то время, поскольку часто исправляю других по этому поводу. Извините, я не видел вашего комментария два года назад! Ответ отредактирован. - person Matt Johnson-Pint; 25.09.2015

Думаю, стоит учесть, что вы можете получить запрошенную информацию с помощью всего лишь одного вызова API к стандартной библиотеке ...

new Date().toLocaleString( 'sv', { timeZoneName: 'short' } );

// produces "2019-10-30 15:33:47 GMT−4"

Вам нужно будет выполнить замену текста, если вы хотите добавить разделитель «T», удалить «GMT-» или добавить «: 00» в конец.

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

Обратите внимание, что я использую Швецию в качестве локали, потому что это одна из стран, использующих формат ISO 8601. Я думаю, что большинство стран ISO используют этот формат «GMT-4» для смещения часового пояса, за исключением Канады, где используется аббревиатура часового пояса, например. "EDT" для восточного летнего времени.

Вы можете получить то же самое из новой стандартной функции i18n "Intl.DateTimeFormat ()", но вы должны указать ей, чтобы она включала время с помощью параметров, иначе она просто выдаст дату.

person Tom    schedule 30.10.2019
comment
Хм, для sv я на самом деле получаю CET, а для летнего времени - CEST ... Но спасибо за ввод, стандартного API мне достаточно (нужно добавлять к сообщениям журнала дату). Если бы я хотел серьезно относиться ко времени и часовым поясам, я бы пошел в библиотеку моментов ... - person mmey; 21.03.2020

Мой ответ - небольшое изменение для тех, кто просто хочет, чтобы сегодняшняя дата была в местном часовом поясе в формате ГГГГ-ММ-ДД.

Позвольте мне пояснить:

Моя цель: получить сегодняшнюю дату в часовом поясе пользователя, но в формате ISO8601 (ГГГГ-ММ-ДД)

Вот код:

new Date().toLocaleDateString("sv") // "2020-02-23" // 

Это работает, потому что в Швеции используется формат ISO 8601.

person Jonathan Steele    schedule 24.02.2020
comment
Неверный ответ, это ответ, но другой вопрос ... Этот ответ легко найти на SO. - person PsychoX; 14.07.2020
comment
Я не прочитал вопрос полностью, поэтому я не могу сказать, отвечает ли он на вопрос, но это именно то, что я искал (и он очень короткий). Спасибо! - person interestinglythere; 06.01.2021
comment
это работает спасибо - person 井上智文; 17.01.2021

Это моя функция для часового пояса клиентов, она легкая и простая

  function getCurrentDateTimeMySql() {        
      var tzoffset = (new Date()).getTimezoneOffset() * 60000; //offset in milliseconds
      var localISOTime = (new Date(Date.now() - tzoffset)).toISOString().slice(0, 19).replace('T', ' ');
      var mySqlDT = localISOTime;
      return mySqlDT;
  }
person Bbb    schedule 25.05.2018
comment
@tsh, ты уверен? Вы получаете текущее смещение времени, которое позже используется для моделирования местного времени. Возможно, в тот момент смещение было другим, но это не имеет значения, потому что вам просто важно сейчас. - person Andrew; 18.01.2019

Проверь это:

function dateToLocalISO(date) {
    const off    = date.getTimezoneOffset()
    const absoff = Math.abs(off)
    return (new Date(date.getTime() - off*60*1000).toISOString().substr(0,23) +
            (off > 0 ? '-' : '+') + 
            Math.floor(absoff / 60).toFixed(0).padStart(2,'0') + ':' + 
            (absoff % 60).toString().padStart(2,'0'))
}

// Test it:
d = new Date()

dateToLocalISO(d)
// ==> '2019-06-21T16:07:22.181-03:00'

// Is similar to:

moment = require('moment')
moment(d).format('YYYY-MM-DDTHH:mm:ss.SSSZ') 
// ==> '2019-06-21T16:07:22.181-03:00'
person Nahuel Greco    schedule 21.06.2019

Только мои двое посылают сюда

Я столкнулся с этой проблемой с датами, поэтому я сделал следующее:

const moment = require('moment-timezone')

const date = moment.tz('America/Bogota').format()

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


Чтобы установить moment-timezone

npm i moment-timezone
person Arnold Gandarillas    schedule 21.02.2019

Не требуется moment.js: вот полный ответ туда и обратно из типа input типа «datetime-local», который выводит строку ISOLocal в UTCseconds в GMT и обратно:

<input type="datetime-local" value="2020-02-16T19:30">

isoLocal="2020-02-16T19:30"
utcSeconds=new Date(isoLocal).getTime()/1000

//here you have 1581899400 for utcSeconds

let isoLocal=new Date(utcSeconds*1000-new Date().getTimezoneOffset()*60000).toISOString().substring(0,16)
2020-02-16T19:30
person Captain Fantastic    schedule 15.01.2020

Вот функции, которые я использовал для этого:

function localToGMTStingTime(localTime = null) {
    var date = localTime ? new Date(localTime) : new Date();
    return new Date(date.getTime() + (date.getTimezoneOffset() * 60000)).toISOString();
};

function GMTToLocalStingTime(GMTTime = null) {
    var date = GMTTime ? new Date(GMTTime) : new Date();;
    return new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toISOString();
};
person gildniy    schedule 22.07.2020

Вы можете добиться этого с помощью нескольких простых методов расширения. Следующий метод расширения даты возвращает только компонент часового пояса в формате ISO, затем вы можете определить другой компонент для части даты / времени и объединить их для получения полной строки смещения даты и времени.

Date.prototype.getISOTimezoneOffset = function () {
    const offset = this.getTimezoneOffset();
    return (offset < 0 ? "+" : "-") + Math.floor(Math.abs(offset / 60)).leftPad(2) + ":" + (Math.abs(offset % 60)).leftPad(2);
}

Date.prototype.toISOLocaleString = function () {
    return this.getFullYear() + "-" + (this.getMonth() + 1).leftPad(2) + "-" +
        this.getDate().leftPad(2) + "T" + this.getHours().leftPad(2) + ":" +
        this.getMinutes().leftPad(2) + ":" + this.getSeconds().leftPad(2) + "." +
        this.getMilliseconds().leftPad(3);
}

Number.prototype.leftPad = function (size) {
    var s = String(this);
    while (s.length < (size || 2)) {
        s = "0" + s;
    }
    return s;
}

Пример использования:

var date = new Date();
console.log(date.toISOLocaleString() + date.getISOTimezoneOffset());
// Prints "2020-08-05T16:15:46.525+10:00"

Я знаю, что сейчас 2020 год, и большинство людей, вероятно, уже используют Moment.js, но простое решение для копирования и вставки все еще иногда бывает удобно.

(Причина, по которой я разделяю методы даты / времени и смещения, заключается в том, что я использую старую библиотеку Datejs который уже предоставляет гибкий метод toString со спецификаторами настраиваемого формата, но просто не включает смещение часового пояса. Поэтому я добавил toISOLocaleString для всех, у кого нет указанной библиотеки.)

person Extragorey    schedule 05.08.2020