tl; dr;
Вы неправильно отформатировали дату.Добавьте букву «Z» в конец строки даты, и она будет рассматриваться как UTC.
var x = new Date('2019-01-01T00:00:00Z') // Jan 1, 2019 12 AM UTC
Этими проблемами форматирования легче управлять с помощью библиотеки, например momentjs (utc
и format
функции), как описано в других ответах.Если вы хотите использовать vanilla javascript, вам необходимо вычесть смещение часового пояса перед вызовом toISOString
(см. Предупреждения в более длинном ответе ниже).
Подробности
Date
вJavascript имеет дело с часовыми поясами в несколько противоречащем интуитивном ключе.Внутри дата хранится в виде количества миллисекунд с начала Unix (1 января 1970 г.).Это число, которое вы получаете, когда звоните getTime()
, и это число, которое используется для математики и сравнений.
Однако - когда вы используете стандартные функции форматирования строк (toString
, toTimeString
, toDateString
и т. д.) javascript автоматически применяет смещение часового пояса для локальных компьютеров часового пояса перед форматированием.В браузере это означает, что он будет применять смещение для компьютера конечного пользователя, а не для сервера.Функции toISOString
и toUTCString
не будут применять смещение - они печатают фактическое значение UTC, сохраненное в Date
.Вероятно, это все равно будет выглядеть «неправильно», потому что оно не будет соответствовать значению, которое вы видите в консоли или при вызове toString
.
Вот где вещи действительно становятся интересными.Вы можете создать Date
в javascript, указав количество миллисекунд с начала Unix, используя new Date(milliseconds)
, или используя синтаксический анализатор с new Date(dateString)
.С помощью метода миллисекунд не нужно беспокоиться о часовом поясе - он определяется как UTC.Вопрос в том, как с помощью метода синтаксического анализа javascript определяет, какой часовой пояс вы намеревались?До ES5 (выпущенный в 2009 году) ответ был другим в зависимости от браузера !Пост ES5, ответ зависит от , как вы форматируете строку !Если вы используете упрощенную версию ISO 8601 (только с датой, без времени), javascript считает эту дату UTC.В противном случае, если вы указываете время в формате ISO 8601 или используете формат, «читаемый человеком», он считает дату местным часовым поясом. Проверьте MDN для получения дополнительной информации.
Некоторые примеры.Я указал для каждого, рассматривает ли javascript его как UTC или локальную дату.В UTC значение будет 1 января 1970 года в полночь.В местном это зависит от часового пояса.Для OP в указанное время (UTC-8) значение UTC будет 1 января 1970 года в 8 часов утра.
new Date(0) // UTC (milliseconds is always UTC)
new Date("1/1/1970"); // Local - (human readable string)
new Date("1970-1-1"); // Local (invalid ISO 8601 - missing leading zeros on the month and day)
new Date("1970-01-01"); // UTC (valid simplified ISO 8601)
new Date("1970-01-01T00:00"); // Local (valid ISO 8601 with time and no timezone)
new Date("1970-01-01T00:00Z"); // UTC (valid ISO 8601 with UTC specified)
Вы не можете изменить это поведение - но вы можете быть педантичнымо форматах, которые вы используете для разбора дат.В вашем случае проблема заключалась в том, что вы предоставили строку ISO-8601 с компонентом времени, но без часового пояса.Добавление буквы «Z» к концу вашей строки или удаление времени будет работать для вас.
Или всегда используйте библиотеку, например моменты, чтобы избежать этих сложностей.
Vanilla JSОбходной путь
Как уже говорилось, настоящей проблемой здесь является знание того, будет ли дата рассматриваться как локальная или по Гринвичу.Вы не можете "кастовать" с локального на UTC, потому что все Date
уже UTC - это просто форматирование.Однако, если вы уверены, что дата была проанализирована как локальная, и она действительно должна быть в формате UTC, вы можете обойти ее, вручную отрегулировав смещение часового пояса.Это называется «смена эпох» (спасибо @MattJohnson за термин!), И это опасно.Вы фактически создаете совершенно новую дату , которая относится к другому моменту времени!Если вы используете его в других частях вашего кода, вы можете получить неправильные значения!
Вот пример метода смены эпох (для ясности переименован в castAsUtc
).Сначала получите смещение часового пояса от объекта, затем вычтите его и создайте новую дату с новым значением.Если вы объедините это с toISOString
, вы получите отформатированную вами дату.
function epochShiftToUtc(date) {
var timezoneOffsetMinutes = date.getTimezoneOffset();
var timezoneOffsetMill = timezoneOffsetMinutes * 1000 * 60;
var buffer = new Date(date.getTime() - timezoneOffsetMill);
return buffer;
}
epochShiftToUtc(date).toUTCString();