moment.js - UTC fornece data errada

93

Por que moment.js UTC sempre mostra a data errada? Por exemplo, no console do desenvolvedor do Chrome:

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

Ambos retornarão "2013-07-17" porque está retornando em 17 em vez de 18 , que foi aprovado.

Mas se eu usar momentjs sem o utc:

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

Recebo "2013-07-18", que é o que também espero ao usar o UTC moment.js.

Isso significa que não podemos obter a data correta ao usar o moment.js UTC?

brg
fonte
4
Não acho que você precise toString()depois format()(já retorna uma string).
alex

Respostas:

158

Por padrão, o MomentJS analisa na hora local. Se apenas uma string de data (sem hora) for fornecida, o padrão de hora será meia-noite.

Em seu código, você cria uma data local e, em seguida, a converte para o fuso horário UTC (na verdade, faz a instância do momento mudar para o modo UTC ), portanto, quando é formatado, é deslocado (dependendo do seu horário local) para frente ou para trás.

Se o fuso horário local for UTC + N (N sendo um número positivo), e você analisar uma string apenas de data, obterá a data anterior.

Aqui estão alguns exemplos para ilustrar isso (minha diferença de horário local é UTC + 3 durante o horário de verão):

>>> 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)"

Se quiser que a string de data e hora seja interpretada como UTC, você deve ser explícito sobre isso:

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

ou, como Matt Johnson menciona em sua resposta, você pode ( e provavelmente deve ) analisá-la como uma data UTC em primeiro lugar usando moment.utc()e incluir a string de formato como um segundo argumento para evitar ambigüidade.

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

Para fazer o contrário e converter uma data UTC em uma data local, você pode usar o local()método da seguinte maneira:

>>> moment.utc('07-18-2013', 'MM-DD-YYYY').local().format("YYYY-MM-DD HH:mm")
"2013-07-18 03:00"
MasterAM
fonte
Muito Obrigado. Então, basicamente, devo sempre passar no tempo ao usar UTC ou passar em UTC como em sua segunda abordagem.
brg
Isso ou seguir o fuso horário local. Se você enviar horários do servidor, poderá expressá-los como carimbo de data / hora Unix (X) ou como strings em um fuso horário específico. Por que usar UTC em vez do fuso horário local do usuário, de qualquer maneira (exceto para o propósito de enviar dados normalizados para o servidor)?
MasterAM
1
Esteja ciente de que new Date('07-18-2013 UTC')não funcionará no IE8, se você se importar.
Dzmitry Lazerka
2
Eu tenho lutado com isso por muito tempo. Eles realmente deveriam ter isso explicado bem em seu site, pois presumo que este seja o caso de uso mais comum de moment.js. Muito obrigado! Você realmente salvou minha pele!
WebWanderer
este código funciona para mim: [code] moment (strDate, 'DD / MM / AAAA h: mm A'). utc (strDate) .format ("AAAA-MM-DD HH: mm") [/ code]
Omar Isaid
36

Ambos Datee momentirão analisar a string de entrada no fuso horário local do navegador por padrão. No entanto, Dateàs vezes é inconsistente a esse respeito. Se a string estiver especificamente YYYY-MM-DDusando hífens , ou se estiver YYYY-MM-DD HH:mm:ss, será interpretada como hora local . Ao contrário Date, momentsempre será consistente sobre como analisa.

A maneira correta de analisar um momento de entrada como UTC no formato que você forneceu seria esta:

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

Consulte esta documentação .

Se você quiser formatá-lo de maneira diferente para a saída, faça o seguinte:

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

Você não precisa chamar toStringexplicitamente.

Observe que é muito importante fornecer o formato de entrada. Sem ele, uma data como 01-04-2013pode ser processada como 4 de janeiro ou 1º de abril, dependendo das configurações de cultura do navegador.

Matt Johnson-Pint
fonte
Só para aprender, no console: moment.utc ('2013-07-18 0:00 +0100', 'AAAA-MM-DD HH: mm') dá-me "2013-07-18 0:00 +0100 " Mas o que é exibido no jsfiddle quando executado é diferente, ou seja: Qui, 25 de julho de 2013 01:00:00 GMT + 0100 Observe o 01:00:00 . obrigado.
brg
A saída de um raw momentno console não é muito útil. Você provavelmente está olhando para uma de suas propriedades internas. Você deve formatá-lo antes de verificar os resultados. Por exemplo moment.utc().format()ou moment().format().
Matt Johnson-Pint
Tanto a data quanto o momento analisarão a string de entrada no fuso horário local do navegador por padrão. Estou em EDT agora. new Date('2010-12-12')dá-me Date {Sat Dec 11 2010 19:00:00 GMT-0500 (Eastern Daylight Time)}em FF 38.0.5. Apenas para contextualizar o que "na hora local" significa exatamente - neste caso, parece significar, " Dateassumirá que uma string sem zona de tempo está em UTC e analisará a hora local." d.getUTCDate()= 12e d.getDate()=11
ruffin de
1
Sim, existem algumas exceções. ES5 (a maioria dos navegadores atuais) interpretará datas com hífens como UTC, mas quase todo o resto é interpretado como hora local. ES6 está mudando este comportamento para interpretar a mesma string como hora local. Eu atualizei a resposta.
Matt Johnson-Pint de
Ha, sim, acabei de encontrar isso no MDN dizendo exatamente isso ( '2012-12-12'é UTC b / c está em um formato ISO, mas 'December 12, 2012'e até mesmo '2012/12/12'são analisados ​​com um fuso horário local em ES5), mas você chegou antes de mim. Tão bom que ES6 torna todos locais [disse ele sarcasticamente]. As datas são uma dor, (c) Advento das datas
ruffin de