Trabalhar com datas em JavaScript é uma dor. Os métodos de datas nativo costumam ser prolixos e ocasionalmente inconsistentes - algo que também os torna sujeitos a erros. Mas boas notícias estão à mão. Existem várias bibliotecas para lidar com a manipulação de datas e removem essa dor. Essas bibliotecas são para datas em JavaScript, o que jQuery é para a API DOM nativa.
Aqui está um exemplo. Esta é a resposta aceita para uma pergunta do Stack Overflow perguntando como obter o último dia do mês:
var t = new Date();
alert( new Date(t.getFullYear(), t.getMonth() + 1, 0, 23, 59, 59) );
Claro que funciona, mas não é imediatamente óbvio o que os números depois de getMonth
representam. Agora compare isso com o consideravelmente mais legível:
const today = new Date();
console.log( lastDayOfMonth(today) );
Esse método lastDayOfMonth
é fornecido por date-fns, um conjunto abrangente de funções para manipular datas em JavaScript no navegador e Node.js.
Neste artigo, vou mostrar como começar a usar o date-fns. Depois de ler, você poderá inseri-lo em seus projetos e aproveitar os vários métodos auxiliares para manipular datas com facilidade. Isso tornará o código t.getMonth() + 1, 0, 23, 59, 59
uma coisa do passado.
Então, por que não usar apenas Moment.js?
Moment.js é uma biblioteca fantástica para trabalhar com datas em JavaScript - ela tem muitos recursos excelentes e oferece uma série de utilitários úteis. No entanto, não deixa de ter seus críticos.
Muitas pessoas citam o fato de que os objetos Moment são mutáveis (ou seja, operações semelhantes add
ou subtract
alteram o objeto Moment original) como sendo confuso para os desenvolvedores e uma fonte de bugs.
Ele também foi criticado por seu grande tamanho. O Moment não funciona bem com algoritmos modernos de "agitação de árvore" (tree shaking) e se você precisar de internacionalização ou suporte de fuso horário, poderá rapidamente se encontrar com um pacote JavaScript bastante grande.
Isso foi tão longe que as ferramentas de desenvolvimento do Chrome agora destacam o fato de que usar o Moment pode levar a um desempenho ruim.
Tudo isso levou os mantenedores do Moment a colocar o projeto em modo de manutenção e a desencorajar o Moment de ser usado em novos projetos no futuro.
Reconhecemos que muitos projetos existentes podem continuar a usar o Moment, mas gostaríamos de desencorajar o Moment de ser usado em novos projetos no futuro. Ao invés disso, gostaríamos de recomendar alternativas que são excelentes opções para uso em aplicações modernas hoje.
Isso torna o date-fns uma das melhores alternativas ao Moment.js que existe.
Instalação
Desde a versão dois da biblioteca, a única maneira de instalar date-fns é como um pacote npm.
npm install date-fns
Ou via Yarn:
yarn add date-fns
Você pode usar date-fns com o sistema de módulo CommonJS e também com módulos ES:
// CommonJS
const { lastDayOfMonth } = require('date-fns');
ou:
// ES Modules
import { lastDayOfMonth } from 'date-fns';
Infelizmente, não há atualmente nenhuma versão CDN de date-fns disponível. Sua remoção e possível reintegração estão sendo discutidas nesta conversa do GitHub . Mas, isso não quer dizer que você não possa usá-lo em um navegador, apenas que você precisa introduzi-lá em uma etapa de empacotamento em seu fluxo de trabalho.
Vamos ver como fazer isso agora.
Como empacotar date-fns para uso em um navegador
Vou assumir que você tem o Node e o npm instalados em sua máquina. Caso contrário, consulte nosso tutorial sobre como instalar o Node .
Em seguida, instale o Parcel. Este é um empacotador (semelhante ao webpack), que permitirá que você empacote seu JavaScript e o exiba em um navegador.
npm install -g parcel-bundler
A seguir, faça um novo projeto com um arquivo package.json
.
mkdir datefns
cd datefns
npm init -y
Instale a biblioteca date-fns:
npm install date-fns
Agora crie dois arquivos index.html
e index.js
.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>date-fns</title>
</head>
<body>
<script src="index.js"></script>
</body>
</html>
E:
import { lastDayOfMonth } from 'date-fns';
const today = new Date();
console.log(lastDayOfMonth(today));
Inicie o servidor de desenvolvimento embutido do parcel:
parcel index.html
E navegue até http: // localhost: 1234. Você não verá nada exibido na página, mas se você abrir o console do navegador, você deve ter registrado o último dia do mês atual.
Quando se trata de implantação, você pode executar:
parcel build index.js --experimental-scope-hoisting
Para que o Parcel crie um pacote na pasta dist
.
Uso básico de Date-fns
Agora que estamos prontos e funcionando, vamos ver o que o date-fns pode fazer.
Uma das tarefas mais comuns ao trabalhar com datas é a capacidade de formatá-las. Podemos fazer isso com a função "format" de data-fns .
Altere o HTML de nossa página do exemplo acima e mude para:
<body>
<h1>The date today is <span></span></h1>
<script src="index.js"></script>
</body>
Em index.js
queremos importar a função format
, podemos então passar a data de hoje e uma string de formato. Em seguida, queremos enviar o resultado para a página.
import { format } from 'date-fns';
const today = new Date();
const formattedDate = format(today, 'dd.MM.yyyy');
document.querySelector('h1 > span').textContent = formattedDate;
Claro, não estamos limitados a um formato dd.MM.yyyy
, vamos tentar algo diferente:
const formattedDate = format(today, 'PPPP');
Isso irá formatar a saída assim: Wednesday, September 16th, 2020
. Você pode encontrar uma lista completa de opções de formatação na documentação oficial.
Mudar a Localização
Se você tem um site em vários idiomas, o date-fns simplifica a internacionalização de horários e datas. Vamos cumprimentar nossos usuários alemães:
<h1>Heute ist <span></span></h1>
E no arquivo JavaScript, podemos importar a localidade alemã e passá-la para a função format
:
import { format } from 'date-fns';
import { de } from 'date-fns/locale';
const today = new Date();
const formattedDate = format(today, 'PPPP', { locale: de });
document.querySelector('h1 > span').textContent = formattedDate;
A saída será algo ao longo das linhas de: Heute ist Mittwoch, 16. September 2020
.
Pode parecer complicado exigir e passar localidades como opções, mas ao contrário do Moment.js que incha sua construção com todas as localidades por padrão, date-fns força os desenvolvedores a exigirem manualmente as localidades como e quando forem necessários.
Você pode ver uma lista de localidades disponíveis olhanda na pasta node_modules/date-fns/locale
em seu projeto.
Imutabilidade, Pureza e Simplicidade
Um dos pontos de venda do date-fns é que suas funções são puras e simples de explicar. Isso leva a um código fácil de entender, que é mais fácil de depurar quando as coisas dão errado.
Vou demonstrar isso usando Moment.js como um contra-exemplo. Como mencionado antes, as datas no Moment são mutáveis, o que pode levar a um comportamento inesperado.
const moment = require('moment');
const now = new Date();
const mNow = moment(now);
mNow.add('day', 3);
console.log(mNow.toDate());
mNow.add(3, 'day');
console.log(mNow.toDate());
// 2020-09-19T10:08:36.999Z
// 2020-09-22T10:08:36.999Z
Há algumas coisas a serem observadas aqui. A função add
do Moment não é exigente quanto à ordem em que aceita seus argumentos (embora o primeiro método agora emita um aviso de depreciação). Mas o mais confuso é que, se você chamar add
várias vezes consecutivas, não obterá o mesmo resultado porque os objetos Moment são mutáveis:
mNow.add(3, 'day'); // add 3 days
mNow.add(3, 'day'); // adds 3 **more** days
Agora compare isso com date-fns que mantém os argumentos em uma ordem e sempre retorna o mesmo resultado, retornando um novo objeto Date
para cada chamada.
import { addDays } from 'date-fns';
const today = new Date();
const threeDaysTime = addDays(3, today);
const sixDaysTime = addDays(threeDaysTime, 3);
console.log(today); // Wed Sep 16 2020 12:11:55 GMT+0200
console.log(threeDaysTime); // Sat Sep 19 2020 12:12:58 GMT+0200
console.log(sixDaysTime); // Invalid Date
Observe também como o nome do método é mais expressivo (addDays
ao invés de apenas add
), mantendo as coisas consistentes e tendo um método para fazer uma coisa e apenas uma coisa.
Comparando datas
Se você olhar a lista de postagens no canal JavaScript do SitePoint, poderá ver que algumas estão listadas como publicadas em uma determinada data, enquanto outras estão listadas como publicadas há X dias. Pode demorar um pouco se você tentar implementar isso em JavaScript puro, mas com date-fns isso é muito fácil - basta usar o método "formatDistance" .
Vamos comparar duas datas diferentes.
import { formatDistance } from 'date-fns';
const startDate = new Date(2020, 8, 16); // (Sep 16 2020)
const endDate = new Date(2020, 11, 25); // (Dec 25 2020)
const distanceInWords = formatDistance(startDate, endDate);
console.log(`It is ${distanceInWords} until Christmas`);
// It is 3 months until Christmas
Observe como, ao trabalhar com JavaScript, os meses são baseados em zero (por exemplo, mês 11 = dezembro), mas os dias contam a partir de um. Isso me confunde uma e outra vez.
Trabalho com coleções de datas
date-fns tem alguns métodos auxiliares muito úteis que você pode usar para manipular coleções de datas de todas as maneiras.
Solicitando uma coleção de datas
O exemplo a seguir usa compareAsc para classificar as datas em ordem crescente. Para fazer isso, ele retorna 1 se a primeira data for posterior à segunda, -1 se a primeira data for anterior à segunda ou 0 se as datas forem iguais.
import { compareAsc } from 'date-fns';
const date1 = new Date('2005-01-01');
const date2 = new Date('2010-01-01');
const date3 = new Date('2015-01-01');
const arr = [date3, date1, date2];
const sortedDates = arr.sort(compareAsc);
// [ 2005-01-01, 2010-01-01, 2015-01-01 ]
Como você pode ver, as datas agora estão em ordem crescente.
O método oposto é compareAsc
, veja a documentação em compareDesc .
import { compareDesc } from 'date-fns';
...
const sortedDates = arr.sort(compareDesc);
// [ 2015-01-01, 2010-01-01, 2005-01-01 ]
Gerando os dias entre duas datas
Para gerar os dias entre duas datas, você pode usar o método addDays que conhecemos anteriormente, bem como o auxiliar eachDayOfInterval que retorna um array de datas dentro do intervalo especificado.
import { addDays, eachDayOfInterval } from 'date-fns';
const today = new Date();
const aWeekFromNow = addDays(today, 7);
const thisWeek = eachDayOfInterval(
{ start: today, end: aWeekFromNow },
);
console.log(thisWeek);
/*
[
Wed Sep 16 2020 00:00:00 GMT+0200 (Central European Summer Time),
Thu Sep 17 2020 00:00:00 GMT+0200 (Central European Summer Time),
Fri Sep 18 2020 00:00:00 GMT+0200 (Central European Summer Time),
Sat Sep 19 2020 00:00:00 GMT+0200 (Central European Summer Time),
Sun Sep 20 2020 00:00:00 GMT+0200 (Central European Summer Time),
Mon Sep 21 2020 00:00:00 GMT+0200 (Central European Summer Time),
Tue Sep 22 2020 00:00:00 GMT+0200 (Central European Summer Time),
Wed Sep 23 2020 00:00:00 GMT+0200 (Central European Summer Time)
]
*/
Encontrando a Data Mais Próxima
Encontrar a data mais próxima de uma determinada data em um array de datas pode ser feito usando o método closestTo. Este snippet de código segue o exemplo anterior:
import { addDays, eachDayOfInterval, closestTo } from 'date-fns';
...
const christmas = new Date(2020, 11, 25);
const closestToChristmasDate = closestTo(christmas, thisWeek);
console.log(closestToChristmasDate);
// Wed Sep 23 2020 00:00:00 GMT+0200 (Central European Summer Time)
Existe também o método closestIndexTo se você deseja obter o índice do array.
Validando uma Data
O utilitário final que desejo examinar é o método isValid que, como o nome sugere, verifica se uma determinada data é válida.
No entanto, devido à maneira como o JavaScript lida com datas, existem algumas pegadinhas que você deve conhecer:
import { isValid } from 'date-fns';
const invalidDate = new Date('2020, 02, 30');
console.log(isValid(invalidDate));
// true, lol, wut?
Você seria perdoado por pensar que o snippet acima deve resultar false
, já que 30 de fevereiro de 2020 é obviamente uma data inválida. Para entender o que está acontecendo, entre new Date('2020, 02, 30')
no console do seu navegador. Você verá Sun Mar 01 2020
que voltará para você - JavaScript pegou o dia extra desde o final de fevereiro e o transformou em 1º de março (que é, obviamente, uma data válida).
Para contornar isso, podemos fazer o parse da data antes de verificar sua validade:
import { isValid, parse } from 'date-fns';
const validDate = parse('29.02.2020', 'dd.MM.yyyy', new Date());
const invalidDate = parse('30.02.2020', 'dd.MM.yyyy', new Date());
console.log(validDate);
// Sat Feb 29 2020 00:00:00 GMT+0100 (Central European Standard Time)
console.log(invalidDate);
// Invalid Date
console.log(isValid(validDate));
// true
console.log(isValid(invalidDate));
// false
Isso pode ser facilmente extraído em um pequeno método auxiliar, por exemplo, para validar a entrada do usuário em formulários.
Fusos Horários
Uma desvantagem de date-fns é que atualmente ele não tem nenhuma função auxiliar de fuso horário como o Moment.js, em vez disso, date-fns retorna o fuso horário local no qual o código está sendo executado.
Esta resposta do Stack Overflow fornece algumas informações básicas sobre como os objetos Date
nativos não armazenam dados de "fuso horário real". Nesse tópico, você notará que eles mencionam um método de definir fusos horários nativamente em JavaScript. Esta não é uma solução abrangente, mas funciona para muitos cenários que exigem apenas conversão de visualização (de UTC ou hora local para um fuso horário específico).
new Date().toLocaleString("en-US", {timeZone: "America/New_York"});
Os fusos horários são, na verdade, um problema complicado de resolver, por isso o MomentJS tem uma biblioteca separada para ele. Existem planos em andamento para adicionar suporte a fuso horário para date-fns, mas no momento em que este artigo foi escrito, ainda é um trabalho em andamento.
No entanto, existe um pacote disponível no npm (baseado em uma solicitação de pull não-aceita para date-fns ) que adiciona suporte de fuso horário para date-fns v2.0.0 usando a API Intl. Com 140 mil downloads semanais, parecia popular, mas no momento em que este artigo foi escrito, ele não era atualizado há vários meses.
Dito isso, veja como você pode usá-lo:
npm i date-fns-tz
E nosso código:
import { format, utcToZonedTime } from 'date-fns-tz';
const today = new Date(); // Wed Sep 16 2020 13:25:16
const timeZone = 'Australia/Brisbane'; // Vamos ver que horas são Lá Embaixo
const timeInBrisbane = utcToZonedTime(today, timeZone);
console.log(`
Time in Munich: ${format(today, 'yyyy-MM-dd HH:mm:ss')}
Time in Brisbane: ${format(timeInBrisbane, 'yyyy-MM-dd HH:mm:ss')}
`);
// Time in Munich: 2020-09-16 13:26:48
// Time in Brisbane: 2020-09-16 21:26:48
Conclusão
date-fns é uma ótima pequena biblioteca que coloca um monte de métodos auxiliares para trabalhar com datas e horas em JavaScript ao seu alcance. Ele está em desenvolvimento ativo e agora que o Moment.js foi colocado em modo de manutenção, ele é um ótimo substituto para o Moment.js.
Espero que este artigo tenha lhe dado compreensão e inspiração suficiente para dar uma olhada e começar a usá-lo em seus próprios projetos.
Créditos
- Learn date-fns: A Lightweight JavaScript Date Library, escrito originalmente por James Hibbard.
Top comments (0)