
O que vem à mente se eu fornecer o seguinte requisito de validação de data do formulário?
O usuário deve selecionar uma data, que é um campo obrigatório.
Bastante fácil. Posso usar uma type="date"
entrada e garantir que ela tenha um required
atributo.
A data selecionada deve ser dos últimos dez anos.
Respire fundo… vou te contar o que me vem à cabeça… encontros são difíceis!
Programando datas e fusos horários… argh!
Era uma noite de semana, 21h30, horário local (vida dos novos pais), e quando mudei de assunto para minha próxima tarefa de recurso, percebi algo… o código de validação de data que funcionava no início do dia foi quebrado repentinamente .
O que aconteceu? Eu não tinha mudado nada. Eu estava sonhando? Foi privação de sono? Eu poderia jurar que o código estava funcionando perfeitamente bem apenas algumas horas atrás!
Eu estava tão confuso.
Acontece que minha lógica estava errada por um dia, permitindo-me escolher a data de amanhã quando não deveria. Mas só descobri porque mudei minha programação mais tarde do que o normal. Durante o horário normal de trabalho diurno, o código funcionou muito bem! Mas depois das 17h, horário local, não!
Resumindo, datas e fusos horários são difíceis. Um dos recursos mais abrangentes que encontrei foi a transcrição de uma palestra de Zach Holman, que começa dizendo: “Programação de horário, datas, fusos horários, eventos recorrentes, segundos bissextos… tudo é bastante terrível”. O melhor conselho sobre como lidar com fusos horários vem de Tom Scott, que disse: “ Você nunca deve lidar com fusos horários se puder evitar ”.
Acho que você entendeu.
Validação de intervalo de datas

Neste artigo, quero examinar as lições aprendidas relacionadas a essa restrição de validação. Passando por mim, eu teria caído na toca do coelho sobre como escrever uma lógica de validação de data personalizada, eventualmente ficando confuso sobre fusos horários e, em seguida, ultrapassando minha estimativa de meio dia com apenas uma solução parcialmente funcional para oferecer.
Então, além de mim, isso é para você.
Os insights e artigos mais recentes do Cloud Four, direto na sua caixa de entrada
Lição 1: Use recursos de validação integrados
Para validar se um valor de data está nos últimos dez anos, comecei no caminho certo adicionando atributos type="date"
, required
e max
. Estava faltando o min
atributo e não planejava usar APIs de validação do navegador.
Present me agora sabe que tudo que preciso para garantir que a data selecionada esteja dentro de um intervalo especificado está integrado à plataforma web:
- O
max
valor do atributo deve ser a data de hoje - O
min
valor do atributo deveria ser há dez anos a partir de hoje - Use o método da API de validação de restrição
checkValidity
para validar se a data atende aosmin
/max
requisitos
Por exemplo, supondo que hoje seja 5 de setembro de 2023 , input
seria mais ou menos assim:
<!-- Use HTML min/max validation constraint attributes -->
<input
id="purchase-date"
name="purchaseDate"
type="date"
min="2013-09-05"
max="2023-09-05"
required
>
Linguagem de código: HTML, XML ( xml )
Um bônus: ao adicionar valores min
/ max
, o calendário integrado do navegador não permitirá que os usuários selecionem uma data antes ou depois das restrições min
/ max
. Por exemplo, no Chrome, todas as datas posteriores à data de hoje têm cores mais claras e são restritas:

max
valor do atributo estiver definido para a data de hoje.Para validar se a seleção de “data de compra” está dentro do intervalo especificado, posso usar a API de validação de restrição:
// Use the Constraint Validation API to validate the input
const purchaseDate = document.querySelector('#purchase-date');
purchaseDate.checkValidity(); // true or false
Linguagem de código: JavaScript ( javascript )
Confira Validação de formulário progressivamente aprimorada, Parte 2: Camadas em JavaScript para obter mais informações sobre como usar a API de validação de restrição.
A mente de além de mim está explodida. 🤯
Lição para mim: não complique as coisas mais do que o necessário; use os recursos de validação integrados do navegador.
Lição 2: Tenha cuidado ao alternar acidentalmente entre a hora local e o UTC
Como não queremos que o usuário selecione um valor futuro para a data de compra, o max
valor do atributo deve ser a data de hoje e formatado adequadamente (YYYY-MM-DD
).
Minha tentativa inicial de gerar o max
valor foi a seguinte:
- Use
new Date()
para obter a data de hoje - Use
Date.prototype.toISOString
para obter uma string de data no formato baseado em ISO 8601- por exemplo,
'2023-03-21T04:15:47.000Z'
- por exemplo,
- Divida a string de data para obter o
YYYY-MM-DD
formato adequado- por exemplo,
'2023-03-21'
- por exemplo,
// Assuming today is September 5, 2023…
const today = new Date();
const todayFormatted = today.toISOString().split('T')[0];
console.log(todayFormatted); // "2023-09-05"
Linguagem de código: JavaScript ( javascript )
O formato parece ótimo. Pronto para ir, certo? Não exatamente.
Há uma pegadinha: a string de data retornada por Date.prototype.toISOString
é sempre UTC (Tempo Universal Coordenado) .
Você obterá resultados inesperados usando uma data UTC em um fuso horário diferente do UTC. Meu conto acima é um excelente exemplo; Eu acidentalmente descobri um bug em meu código onde eu poderia selecionar a data de amanhã se fosse depois das 17h, meu horário local. Ops.
Vamos dar uma olhada mais de perto para entender o porquê
Continuando com a suposição de que hoje é 5 de setembro de 2023, 17h30, meu horário local , new Date()
retorna minha string de data do fuso horário local , conforme esperado:
const today = new Date();
console.log(today);
// Tue Sep 05 2023 17:30:17 GMT-0700 (Pacific Daylight Time)
Linguagem de código: JavaScript ( javascript )
Se eu chamar o Date.prototype.toISOString
método on today
, a data de amanhã será retornada…não era isso que eu esperava:
const todayAsISOString = today.toISOString();
console.log(todayAsISOString);
// "2023-09-06T00:30:17.479Z"
Linguagem de código: JavaScript ( javascript )
Lembre-se, Date.prototype.toISOString
retorna uma string de data UTC. Meu fuso horário local está sete horas atrás do UTC. Portanto, se você pegar meu horário local de 17h30 e adicionar sete horas a ele, você terá o dia seguinte , 12h30, em UTC.
Então, e agora?
Poderíamos ser complicados e compensar a string de data UTC pela diferença entre o fuso horário local do usuário e o UTC usando Date.prototype.getTimezoneOffset
. Eu segui esse caminho inicialmente. Funciona, mas podemos fazer algo mais simples.
Em vez de alternar entre o fuso horário local do usuário e o UTC com truques de deslocamento de fuso horário, vamos permanecer no fuso horário do usuário:
- Use
Date.prototype.getFullYear
para obter o ano local de 4 dígitos - Use
Date.prototype.getMonth
para obter o valor do mês local- Retorna um número inteiro entre 0 e 11
- Use
Date.prototype.getDate
para obter o dia do mês- Retorna um número inteiro entre 1 e 31
- Use
String.prototype.padStart
para garantir que o mês e o dia sejam sempre valores de dois dígitos - Combine-os usando uma string de modelo literal para criar o
YYYY-MM-DD
formato esperado
/**
* Generates a date string from a Date object in the format: YYYY-MM-DD
* @param {Date} date The date object to format
* @returns {string} A date string formatted as follow: YYYY-MM-DD
*/
export const getISOFormattedDate = (date) => {
// Get 4-digit year.
const year = date.getFullYear();
// Use padding to ensure 2 digits.
// Note: January is 0, February is 1, and so on.
const month = (date.getMonth() + 1).toString().padStart(2, '0');
// Use padding to ensure 2 digits.
const day = date.getDate().toString().padStart(2, '0');
// Return the date formatted as YYYY-MM-DD.
return `${year}-${month}-${day}`;
};
Linguagem de código: JavaScript ( javascript )
Supondo que você esteja usando uma estrutura como Astro (ou Svelte, Vue ou algo semelhante), o max
atributo para a entrada “data de compra” poderia então ser atualizado da seguinte forma:
<input
id="purchase-date"
name="purchaseDate"
type="date"
min="2013-09-05"
max={getISOFormattedDate(new Date())}
required
>
Linguagem de código: HTML, XML ( xml )
Se estiver usando JavaScript vanilla, você pode fazer algo como o seguinte:
const purchaseDate = document.querySelector('#purchase-date');
purchaseDate.setAttribute('max', getISOFormattedDate(new Date()));
Linguagem de código: JavaScript ( javascript )
Lição para mim: preste atenção aos Date
valores de retorno do método API (eles são UTC?) e teste depois da meia-noite UTC para garantir que a lógica da data não esteja errada em um dia.
Você não precisa ficar acordado até tarde como eu para testar. Descubra quando a meia-noite UTC é relativa ao seu fuso horário e, em seguida, avance/atrase o relógio do computador conforme necessário para testar.
Lição 3: Como calcular anos atrás
Vamos terminar de adicionar o último bit de lógica para o campo “data de compra”, que gera o min
valor do atributo. Precisamos gerar um valor de data dez anos antes de hoje para o min
atributo. Não existe uma maneira integrada de obter uma quantidade específica de anos atrás a partir de hoje usando JavaScript.
Minha pesquisa online continuou me mostrando algo semelhante ao seguinte:
const tenYearsAgoToday = new Date();
tenYearsAgoToday.setFullYear(tenYearsAgoToday.getFullYear() - 10);
console.log(tenYearsAgoToday);
// Thu Sep 05 2013 14:17:38 GMT-0700 (Pacific Daylight Time)
Linguagem de código: JavaScript ( javascript )
Muito arrumado! Eu não tinha visto o padrão setFullYear
/ getFullYear
antes.
Date.prototype.getFullYear
e Date.prototype.setFullYear
são métodos específicos da hora local . Como só precisamos nos preocupar com o fuso horário local do usuário, não há necessidade de recorrer aos métodos da variante UTC.
Acabei envolvendo essa lógica também em uma função utilitária (também disponível na fonte demo):
/**
* Returns a Date object representing the number of years ago from today.
* @param {number} years - The number of years ago from today.
* @returns {Date} - A Date object.
*/
const yearsAgoFromToday = (years) => {
const date = new Date();
date.setFullYear(date.getFullYear() - years);
return date;
};
Linguagem de código: JavaScript ( javascript )
Usando esta função utilitária (combinada com a getISOFormattedDate
função acima para formatação), a entrada “data de compra” pode ser atualizada da seguinte forma:
<input
id="purchase-date"
name="purchaseDate"
type="date"
min={getISOFormattedDate(yearsAgoFromToday(10))}
max={getISOFormattedDate(new Date())}
required
>
Linguagem de código: HTML, XML ( xml )
Semelhante a antes, se estiver usando JavaScript vanilla, você pode fazer o seguinte:
const purchaseDate = document.querySelector('#purchaseDate');
purchaseDate.setAttribute(
'min',
getISOFormattedDate(yearsAgoFromToday(10))
);
Linguagem de código: JavaScript ( javascript )
Lição para mim: calcular a data como um valor passado não precisa ser complexo. Use os métodos de horário local e mantenha a simplicidade.
Empacotando
Ao validar um campo de entrada de data que possui um requisito de intervalo de datas:
- Use recursos de validação integrados ao navegador (
min
/max
e a API de validação de restrição) - Considere os fusos horários ao escrever a lógica de data. Você precisa prestar contas deles?
- Preste atenção aos valores de retorno dos
Date
métodos; alguns são UTC e outros não - Teste em relação à meia-noite UTC para garantir que a lógica da data não esteja errada em um dia
Embora a programação para datas e fusos horários ainda seja difícil e confusa, adicionar validação de formulário para um intervalo de datas não precisa ser assustador.