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

103

Цель: найти 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. Заранее спасибо.

мяу
источник

Ответы:

178

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

Date.prototype.toIsoString = function() {
    var tzo = -this.getTimezoneOffset(),
        dif = tzo >= 0 ? '+' : '-',
        pad = function(num) {
            var norm = Math.floor(Math.abs(num));
            return (norm < 10 ? '0' : '') + norm;
        };
    return this.getFullYear() +
        '-' + pad(this.getMonth() + 1) +
        '-' + pad(this.getDate()) +
        'T' + pad(this.getHours()) +
        ':' + pad(this.getMinutes()) +
        ':' + pad(this.getSeconds()) +
        dif + pad(tzo / 60) +
        ':' + pad(tzo % 60);
}

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

Стивен Мозли
источник
2
Знак указывает смещение местного времени от GMT
Стивен Мозли
1
@ masato-san Вы должны перевернуть знак, см. определение на сайте developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
bfavaretto 02
2
Здесь функция pad возвращает правильную строку для каждого раздела даты, кроме миллисекунд. Например, для 5 мс в качестве входных данных будет возвращено 05, что должно быть 005. Вот ссылка с минимально измененной версией той же функции jsbin
Сафик Ахмед Фарук
9
Обратите внимание: в этом ответе есть небольшая ошибка. Смещение часового пояса не будет рассчитано правильно, если зона имела смещение в минутах и ​​было отрицательным (например, -09: 30), например, в некоторых местах во Франции и Канаде. Mod возвращает отрицательное число, поэтому выполнение floor () перед abs () непреднамеренно сделало смещение БОЛЕЕ отрицательным. Чтобы исправить эту ошибку, используйте abs () и THEN floor (). Пытался отредактировать ответ, но, судя по всему, «Это изменение предназначено для обращения к автору сообщения и не имеет смысла как изменение».
tytk
5
@tytk - Отличный улов! Это действительно тонко. Я добавил ваше исправление в ответ. Спасибо за комментарий!
Стивен Мозли,
67

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

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

Мэтт Джонсон-Пинт
источник
Использование toISOString () не сработает. Формат +01: 00 требует, чтобы часть времени была местным. toISOString () предоставит строку времени в формате UTC.
Остин Франс,
1
@AustinFrance - Ты прав! Я удивлен, что совершил эту ошибку в то время, поскольку часто исправляю других по этому поводу. Извините, я не видел вашего комментария два года назад! Ответ отредактирован.
Мэтт Джонсон-Пинт,
9

Думаю, стоит учесть, что вы можете получить запрошенную информацию с помощью всего одного вызова 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 ()», но вы должны указать ей, чтобы она включала время с помощью параметров, иначе она просто выдаст дату.

Том
источник
1
Хм, для sv я на самом деле получаю "CET", а для даты летнего времени "CEST" ... Но спасибо за ввод, стандартного API мне достаточно (нужно добавлять в сообщения журнала дату). Если бы я хотел серьезно
относиться ко
4

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

  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;
  }
BBB
источник
@tsh, ты уверен? Вы получаете текущее смещение времени, которое позже используется для имитации местного времени. Может быть, в тот момент смещение было другим, но это не имеет значения, потому что вам просто важно сейчас.
Эндрю
2

Проверь это:

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 ? '-' : '+') + 
            (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'
Науэль Греко
источник
1

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

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

const moment = require('moment-timezone')

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

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


Установить moment-timezone

npm i moment-timezone
Арнольд Гандариллас
источник
1

Не требуется moment.js: вот полный ответ туда и обратно из типа ввода "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
Капитан Фантастик
источник
0

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

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

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

Вот код:

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

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

Джонатан Стил
источник
Неверный ответ, это действительно ответ, но другой вопрос ... Этот ответ можно легко найти на SO.
PsychoX,
0

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

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();
};
гильдный
источник
0

Вы можете добиться этого с помощью нескольких простых методов расширения. Следующий метод расширения даты возвращает только компонент часового пояса в формате 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для всех, у кого нет сказала библиотека.)

Extragorey
источник