moment.js - UTC дает неправильную дату

95

Почему момент.js UTC всегда показывает неправильную дату. Например, из консоли разработчика Chrome:

moment(('07-18-2013')).utc().format("YYYY-MM-DD").toString()
// or
moment.utc(new Date('07-18-2013')).format("YYYY-MM-DD").toString()

Оба они вернут "2013-07-17", почему он возвращает 17-е вместо 18-го , которое было пропущено.

Но если я использую momentjs без utc:

moment(new Date('07-18-2013')).format("YYYY-MM-DD").toString()

Я возвращаюсь "2013-07-18", чего я и ожидал при использовании moment.js UTC.

Означает ли это, что мы не можем получить правильную дату при использовании moment.js UTC?

brg
источник
4
Я не думаю, что вам нужно toString()после format()(он уже возвращает строку).
Alex

Ответы:

161

По умолчанию MomentJS анализирует по местному времени. Если указана только строка даты (без времени), время по умолчанию устанавливается на полночь.

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

Если местный часовой пояс - UTC + N (N - положительное число), и вы разбираете строку, содержащую только дату, вы получите предыдущую дату.

Вот несколько примеров, чтобы проиллюстрировать это (мое местное смещение времени - UTC + 3 во время летнего времени):

>>> moment('07-18-2013', 'MM-DD-YYYY').utc().format("YYYY-MM-DD HH:mm")
"2013-07-17 21:00"
>>> moment('07-18-2013 12:00', 'MM-DD-YYYY HH:mm').utc().format("YYYY-MM-DD HH:mm")
"2013-07-18 09:00"
>>> Date()
"Thu Jul 25 2013 14:28:45 GMT+0300 (Jerusalem Daylight Time)"

Если вы хотите, чтобы строка даты и времени интерпретировалась как UTC, вы должны четко указать это:

>>> moment(new Date('07-18-2013 UTC')).utc().format("YYYY-MM-DD HH:mm")
"2013-07-18 00:00"

или, как упоминает Мэтт Джонсон в своем ответе, вы можете ( и, вероятно, должны ) проанализировать его как дату в формате UTC в первую очередь, используя moment.utc()и включив строку формата в качестве второго аргумента, чтобы предотвратить двусмысленность.

>>> moment.utc('07-18-2013', 'MM-DD-YYYY').format("YYYY-MM-DD HH:mm")
"2013-07-18 00:00"

Чтобы пойти другим путем и преобразовать дату в формате UTC в местную дату, вы можете использовать этот local()метод следующим образом:

>>> moment.utc('07-18-2013', 'MM-DD-YYYY').local().format("YYYY-MM-DD HH:mm")
"2013-07-18 03:00"
MasterAM
источник
Большое спасибо. В общем, я всегда должен проходить вовремя при использовании UTC или переходить в UTC, как в вашем втором подходе.
brg
Либо так, либо придерживайтесь местного часового пояса. Если вы отправляете время с сервера, вы можете выразить его как временную метку Unix (X) или как строки в определенном часовом поясе. В любом случае, зачем использовать UTC вместо местного часового пояса пользователя (кроме как для отправки нормализованных данных на сервер)?
MasterAM
1
Имейте new Date('07-18-2013 UTC')в виду, что не будет работать в IE8, если вам интересно.
Дмитрий Лазерка
2
Я так долго боролся с этим. Им действительно стоит хорошо объяснить это на своем сайте, поскольку я предполагаю, что это наиболее распространенный вариант использования moment.js. Спасибо огромное! Вы действительно спасли мою кожу!
WebWanderer
у меня работает этот код: [code] moment (strDate, 'DD / MM / YYYY h: mm A'). utc (strDate) .format ("YYYY-MM-DD HH: mm") [/ code]
Omar Isaid
36

Оба Dateи momentпо умолчанию будут анализировать входную строку в местном часовом поясе браузера. Однако Dateиногда это противоречит этому вопросу. Если в строке YYYY-MM-DDиспользуются дефисы или если это так YYYY-MM-DD HH:mm:ss, она будет интерпретировать ее как местное время . В отличие от Date, momentвсегда будет согласован в том, как он анализирует.

Правильный способ синтаксического анализа входного момента как UTC в указанном вами формате будет следующим:

moment.utc('07-18-2013', 'MM-DD-YYYY')

Обратитесь к этой документации .

Если вы хотите отформатировать его по-другому для вывода, сделайте следующее:

moment.utc('07-18-2013', 'MM-DD-YYYY').format('YYYY-MM-DD')

Вызывать toStringявно не нужно .

Обратите внимание, что очень важно указать формат ввода. Без него такая дата 01-04-2013может обрабатываться как 4 января или 1 апреля, в зависимости от настроек языка и региональных параметров браузера.

Мэтт Джонсон-Пинт
источник
Просто ради обучения в консоли: moment.utc ('2013-07-18 0:00 +0100', 'YYYY-MM-DD HH: mm') дает мне "2013-07-18 0:00 +0100 " Но при запуске jsfiddle отображается другое, а именно: Thu Jul 25 2013 01:00:00 GMT + 0100 Обратите внимание на 01:00:00 . Спасибо.
brg
Вывод RAW momentна консоль не очень полезен. Вы, наверное, смотрите на одно из его внутренних свойств. Вы должны отформатировать его перед проверкой результатов. Например moment.utc().format()или moment().format().
Мэтт Джонсон-Пинт
По умолчанию дата и момент будут анализировать входную строку в местном часовом поясе браузера. Я сейчас на EDT. new Date('2010-12-12')дает мне Date {Sat Dec 11 2010 19:00:00 GMT-0500 (Eastern Daylight Time)}в FF 38.0.5. Просто чтобы контекстуализировать, что именно означает «по местному времени» - в данном случае это, по-видимому, означает « Dateбудет предполагать, что строка без часового пояса находится в формате UTC и будет анализироваться по местному времени». d.getUTCDate()= 12and d.getDate()=11
ruffin
1
Да, есть исключения. ES5 (большинство современных браузеров) интерпретирует даты с дефисами как UTC, но почти все остальное интерпретируется как местное время. ES6 меняет это поведение, чтобы интерпретировать ту же строку, что и местное время. Я обновил ответ.
Мэтт Джонсон-Пинт,
Ха, да, только что столкнулся с этим в MDN, говоря именно это ( '2012-12-12'это UTC b / c, это в формате ISO, но 'December 12, 2012'и даже '2012/12/12'анализируются с местным часовым поясом в ES5), но вы меня опередили. Настолько классно, что ES6 делает их всех локальными [саркастически сказал он]. Сроки - это боль, (c) Advent of Dates
ruffin