Moment: Suportar durações inválidas

Criado em 28 jul. 2014  ·  44Comentários  ·  Fonte: moment/moment

Atualmente, não há como determinar se uma análise de duração foi bem-sucedida, mesmo inspecionando os campos.

New Feature

Comentários muito úteis

Há alguma atualização sobre isso?

Com moment 2.23.0 a função isValid() também retorna true ao tentar criar uma duração a partir de uma string ISO8601 inválida.

Exemplo:

const mom = moment.duration('asdf')
console.log(mom.isValid()) // This returns true, expected would be false 

É meio chato, imo. e anula completamente o propósito da função isValid() em objetos Duration (existe mesmo um caso, quando isValid() retorna false desde que o momento interpreta até mesmo um inválido entrada?)

Todos 44 comentários

Eu concordo. Precisamos de um método isValid para começar.

Acho que durações inválidas devem lançar exceções e o tratamento atual de durações inválidas é um bug, não um aprimoramento. Considere estes casos:

`var wrong = moment.duration(3,'mintues');`

Qual será o resultado? Os documentos não definem o que acontece com uma entrada incorreta.

Isso tem um efeito em cascata, já que duration é usado em vez de add() e subtract() , então estes também têm comportamento indefinido:

var hmm = moment().subtract(3,'mintues').toDate();
var uhoh = moment().add(3,'mintues').toDate();

Com o teste manual, posso dizer o que acontece: moment "é bem-sucedido" usando uma duração zero.

Se moment() tivesse simplesmente lançado uma exceção na entrada incorreta, esse problema teria sido identificado imediatamente. Uma vez que silenciosamente foi "bem-sucedido", um bug Garbage-In, Garbage-Out da classe persistiu.

Como o comportamento da duração digitada é atualmente indefinido, começar a lançar exceções em strings incorretas seria compatível com versões anteriores.

Se estiver sendo usada uma string que não é confiável e que pode ter um erro de digitação, try/catch pode ser usado explicitamente ao testar se a string pode ser usada para descrever uma duração válida.

+1 @markstos.

Que tal apoiar uma opção strict (como o próprio momento para datas) que lança? Retornar uma duração de zero quando a análise falha é muito perigoso.

Uma opção strict seria melhor do que nenhuma alteração, mas não acho que analisar datas inválidas como duração zero seja um bom comportamento padrão em primeiro lugar.

Concordou. Uma possível mudança só pousaria com [email protected]?

Por que não usar o mesmo padrão do resto do momento? Tenha um campo _isValid e um método isValid () e defina uma duração como inválida se a análise falhar.

Para referência, aqui estão os outros locais documentados onde isValid () é usado:

https://github.com/moment/momentjs.com/search?utf8=%E2%9C%93&q=isValid

O vínculo com a documentação e não com o código foi intencional, embora as menções ao código também sejam relevantes.

+1
A capacidade de descobrir se a análise foi bem-sucedida ou não é muito necessária.
Se a compatibilidade com versões anteriores for uma preocupação, então poderia ser possível introduzir um método Duration.parse(input: string, strict?: boolean = true): Duration que lançaria se a entrada estivesse incorreta e o argumento strict fosse especificado.
O comportamento atual quando estamos obtendo uma duração com todos os zeros para dados arbitrários é muito estranho.

+1
Ter um método isValid () em uma duração (para ver se a análise foi bem-sucedida) seria útil

Olá a todos, O Moment 2.18.0 agora tem um método .isValid para durações. No entanto, é bastante tolerante. @markstos @theazureshadow e outros, experimente e diga-nos se tiver sugestões.

@marwahaha ,

Como teste inicial, copiei / colei um dos exemplos acima. Em vez de retornar verdadeiro ou falso conforme o esperado, ele lança uma exceção (com o momento 2.18.0):

 moment.duration(3,'mintues').isValid();

(A duração inválida criada ainda "é bem-sucedida" sem lançar uma exceção, mas agora verificar se é válida lança uma exceção!)

Embora pareça que ainda não há documentos para o novo método, talvez esse não seja o uso pretendido.

@markstos Acabei de executar esse código no console do Momentjs.com - que tem 2.18.0 - sem problemas. Podemos obter mais detalhes sobre isso?

Talvez esse código deva ser inválido porque os minutos estão escritos incorretamente.

Em 21 de março de 2017, 14h35, "Maggie Pint" [email protected] escreveu:

@markstos https://github.com/markstos Acabei de executar esse código no
console do Momentjs.com - que tem 2.18.0 - sem problemas. Podemos obter
mais detalhes sobre isso?

-
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/moment/moment/issues/1805#issuecomment-288176838 ou mudo
o segmento
https://github.com/notifications/unsubscribe-auth/ACbGmWIf24KlRs8oiS2wTn206iJNMod7ks5roBhfgaJpZM4CRuVM
.

@maggiepint @marwahaha Sim, o erro ortográfico de "minutos" É por isso que a duração deveria ser inválida.

@markstos Também não consigo reproduzir essa exceção no console do Momentjs.com. (No entanto, agora é 2.18.1.) Se você ainda puder reproduzir a exceção ao chamar isValid, podemos obter mais detalhes?

screen shot 2017-03-22 at 10 38 06

(Obviamente, Mark queria que isValid retornasse false nesta situação, mas isso é um problema separado da exceção. Não é 100% óbvio para mim como devemos lidar com a validade da análise com entradas de objetos.)

Também não consigo reproduzir a exceção com 2.18.1. Talvez tenha sido um alarme falso. Posso reproduzir resultado de @butterflyhug de ter este tipo de duração inválido retornar 'true' para 'isValid ()'.

A análise não deveria falhar neste caso, em vez de retornar "0 minutos" como uma saída incorretamente válida como resultado?

Deveria é uma questão diferente, mas o que IIRC faz é interpretar que
valor em milissegundos porque a unidade não foi encontrada na tabela de hash da unidade.

Deve ser inválido? IMO provavelmente.

Em 22 de março de 2017, 8:42 AM, "Mark Stosberg" [email protected] escreveu:

Também não consigo reproduzir a exceção com 2.18.1. Talvez fosse um falso
alarme. Posso reproduzir @butterflyhug https://github.com/butterflyhug 's
resultado de ter esse tipo de duração inválida retornando "verdadeiro" para
"é válido()".

A análise não deve falhar neste caso, em vez de retornar "0 minutos" como um
saída incorretamente válida como resultado?

-
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/moment/moment/issues/1805#issuecomment-288440827 ou mudo
o segmento
https://github.com/notifications/unsubscribe-auth/AFxi0nZAz8gwAJM8fVx5rYPyIWjeEfHMks5roUF4gaJpZM4CRuVM
.

Parece haver um bug aqui:

https://github.com/moment/moment/blob/497f918515ae6ab900f008f19523b1e4ae5e2450/src/lib/duration/create.js#L34

Supõe-se que a string é válida e aparece no mapa de "duração". Parece que a correção é adicionar uma verificação aqui para confirmar se a string é um valor válido a ser definido. Caso contrário, defina _isValid:false

Este comportamento é consistente com o método isValid padrão:

moment({'mintues': 3}).isValid()
> true

A fonte disso é normalizeObjectUnits https://github.com/moment/moment/blob/497f918515ae6ab900f008f19523b1e4ae5e2450/src/lib/units/aliases.js#L14 -L29 que apenas adiciona atributos válidos (e elimina o ofensivo uns).

@maggiepint @ichernev esse comportamento deve mudar? Faremos um lançamento de última hora em breve ...

Converter '3 minutos' em '0 minutos' e declarar o resultado "válido" é um bug. Corrigir pode ser uma alteração importante, mas ainda é uma correção de bug.

Concordo que esse comportamento de validação não é o ideal, mas discordo que isso seja necessariamente um bug .

Considere o caso em que você analisa algo como moment({'minutes': 3, '$cacheKey': 92619502}).isValid() . Espero fortemente que tenhamos usuários que apreciem ter essa análise como um momento válido, e não vejo uma distinção de princípio entre este exemplo e o bug que você escreveu.

A API que estamos discutindo é o "analisador de objeto" para o construtor moment , documentado aqui:

http://momentjs.com/docs/#/parsing/object/

A documentação não menciona atualmente o tratamento de passar argumentos desconhecidos ou adicionais.

No momento, o comportamento de Moment aqui pode ser descrito como "Garbage In, Garbage Out". A entrada de lixo é aceita silenciosamente e o resultado é "Saída de lixo" - datas com erros ortográficos sendo convertidas para outras datas e consideradas válidas!

Essas atualizações permitiriam ao desenvolvedor descobrir o mais rápido possível que cometeu um erro:

  • O documento de que uma exceção será lançada em argumentos desconhecidos é passado para o construtor do objeto.

    • Lança uma exceção se argumentos desconhecidos são passados ​​para o construtor do objeto

Enquanto o Moment continuar a aceitar "Garbage In" como válido, continuará a ser um péssimo serviço para os desenvolvedores e os usuários serão deixados com o Moment relatando erroneamente que as datas quebradas são "válidas".

Essa melhoria parece valer a pena uma mudança "decisiva".

Eu fui mordido por isto: moment().subtract('1 day') ... esta não é uma forma válida de expressar uma duração. Eu não esperaria que fosse equivalente a .subtract(0) embora. IMO, a coisa menos surpreendente a fazer neste caso é jogar.

@johnvh eu concordo completamente.

Você sabe que não é uma "forma válida de expressar uma duração", mas mesmo assim escreveu esse código? O que você espera? Presumo que você tenha percebido que não era uma forma válida de expressar uma duração a partir dos excelentes momentos de documentação que os autores fornecem.

Em 25/04/2017, às 08:29, Mark Stosberg [email protected] escreveu:

@johnvh eu concordo completamente.

-
Você está recebendo isto porque está inscrito neste tópico.
Responda a este e-mail diretamente, visualize-o no GitHub ou ignore a conversa.

Os desenvolvedores @johnvh tivesse recebido imediatamente uma falha de validação quando seu código foi executado, ele poderia ter verificado a excelente documentação para ver qual poderia ser o problema. Infelizmente, o Moment tratou a entrada inválida como válida e gerou nenhum alarme.

@markstos exatamente. Eu concordo 100%.

Esse era um bug que precisava ser rastreado. Nós não escrevemos dessa forma sabendo que era inválido, é claro. Parece certo, já que os métodos de manipulação de momento são muito liberais em suas assinaturas suportadas. Também usamos agenda no mesmo projeto, que usa intervalo humano para converter linguagem natural em durações. Portanto, teremos algo assim, apenas algumas linhas de distância:

moment().subtract('1 day');
agenda.processEvery('1 day');

O último funciona como pretendido, o primeiro não. Se o primeiro gerasse uma exceção, teria nos alertado de nosso erro.

Além disso, o fato de que Moment / has / an isValid () método implica que ele valida os valores como válidos ou inválidos. Nos casos em que os valores inválidos são classificados como válidos, o método isValid() parece não funcionar conforme documentado.

Olá a todos, adoraríamos avaliar quaisquer RPs nesta área!

Encontrei esse bug ou algo semelhante. Se eu executar moment.duration('3', 'minutes').asMinutes() usando o Moment 2.18.1, obtenho o resultado 0. Acho que isso deve retornar 3 ou lançar uma exceção. No mínimo, moment.duration('3', 'minutes').isValid() deve retornar false .

A maneira como o Moment se comporta agora pode causar muitos problemas, causando bugs que podem ser difíceis de detectar e diagnosticar, como várias pessoas mencionaram.

Isso foi resolvido?

@TomJSmith Um teste rápido mostra que o bug ainda existe. Este código:

 moment().subtract('1 day');

"é bem-sucedido", subtraindo zero dias em vez de um dia conforme o esperado. Você pode chamar .isValid() na data resultante e obter uma resposta true volta indicando que o resultado é válido quando não é.

O comportamento atual pode levar a consequências ruins em alguns casos de uso e deve ser claramente documentado com um aviso:

Caso de uso
Desejo excluir recursos com base em uma duração de retenção:

const duration = moment.duration('invalid');
const currentDate = moment();
const removeBeforeDate  = moment().subtract(duration);

console.log(`currentDate      : ${currentDate}`);
console.log(`removeBeforeDate : ${removeBeforeDate}`);

Resultado:

currentDate      : Fri Apr 13 2018 13:15:04 GMT+0200
removeBeforeDate : Fri Apr 13 2018 13:15:04 GMT+0200

Consequência:
Todos os recursos até a data atual são excluídos ...

Até agora, a única maneira que encontrei de contornar isso é testar o valor após a análise:

const duration = moment.duration('invalid');
if (duration.toISOString() === 'P0D') {
    // throw an Error
}

Sim, esse bug é ruim e está aberto desde 2014. Isso é algo que os projetos "date-fns" acertam. Na verdade, eles informam se a entrada inválida é inválida. https://date-fns.org/ Infelizmente, date-fns não lida com conversões de fuso horário, mas talvez haja uma maneira de lidar com isso com uma biblioteca externa.

Para que o projeto Moment conserte isso, eles precisam parar de aceitar a entrada de lixo e tratá-la como uma data válida. Embora pareça um comportamento melhor, é tecnicamente uma mudança de "comportamento reverso", então parece que há resistência para fazer a mudança. Mas, como o projeto não entrou em ação nos últimos quase quatro anos, não esperaria uma solução tão cedo.

Há alguma atualização sobre isso?

Com moment 2.23.0 a função isValid() também retorna true ao tentar criar uma duração a partir de uma string ISO8601 inválida.

Exemplo:

const mom = moment.duration('asdf')
console.log(mom.isValid()) // This returns true, expected would be false 

É meio chato, imo. e anula completamente o propósito da função isValid() em objetos Duration (existe mesmo um caso, quando isValid() retorna false desde que o momento interpreta até mesmo um inválido entrada?)

@robbiecloset A atualização é que desde meu último post em abril, o projeto "date-fns" melhorou no suporte de fuso horário. Agora existem projetos "date-fns-timezone" e "date-fns-tz" para ajudar com isso.

Eu consideraria usá-lo se for viável a troca.

Eu uso moment.duration(value).toISOString() === value , esse valor é lido de uma configuração.

Eu uso moment.duration (value) .toISOString () === value, esse valor é lido de uma configuração.

@ bors-ltd Acho que parte do problema é que o momento pode ser capaz de decodificar dois valores que produzem o mesmo valor codificado.

Por exemplo: moment.duration('PT0M0S').toISOString() === moment.duration('PT0S').toISOString() .

Se você olhar os testes, parece que os valores inválidos estão realmente fora da especificação:

https://github.com/moment/moment/blob/2.24.0/src/test/moment/duration.js#L419 -L420

Os padrões que não correspondem ao padrão de duração ISO estão sendo testados como sendo "0" segundos.

Olá, também tivemos o mesmo problema com o método isValid() e acabamos fazendo isso manualmente.
const isValidDuration = duration => { return !!duration.match( /^(-?)P(?=\d|T\d)(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)([DW]))?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+)?)S)?)?$/ ); };
Espero que possa ajudar alguém.

Consulte https://momentjs.com/docs/#/ -project-status /

Obrigado por toda a discussão aqui.

Uma solução hacky é substituir o método isValid por um método que garanta que a soma das partes dos dados seja> 0 (ou pelo menos provavelmente inválido)

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

vbullinger picture vbullinger  ·  3Comentários

benhathaway picture benhathaway  ·  3Comentários

alvarotrigo picture alvarotrigo  ·  3Comentários

Shoroh picture Shoroh  ·  3Comentários

tanepiper picture tanepiper  ·  3Comentários