Como Trabalhar com Data e Hora em Python Usando datetime

Registrar um momento qualquer no tempo é um requisito comum em todo sistema real.

Esse registro pode ser só da data, ou também do horário, com precisão até os milissegundos.

Além disso, também é comum precisar fazer operações com esses dados, para calcular o intervalo entre dois momentos diferentes.

Para resolver esse problema, a maioria dos sistemas gerenciadores de banco de dados disponibiliza os tipos de dados date, time e datetime.

Já na linguagem Python, esses não são tipos de dados primitivos.

No entanto, os módulos datetime e dateutil possuem abstrações que permitem ao programador trabalhar com data e hora de forma eficiente, considerando os diferentes formatos de datas, fusos horários e o grande complicador: o horário de verão.

Veja a seguir quais são os cuidados que você deve tomar e como usar as classes dos módulos datetime e dateultil para realizar as operações mais comuns com datas e horários em Python.

As Dificuldades de Trabalhar com Datas e Horários

Antes de entrar nos detalhes técnicos sobre a manipulação de datas em Python, é preciso entender por que trabalhar com esse tipo de dado não é tão simples quanto parece.

Embora o tempo que todos experimentam seja medido em unidades conhecidas, como anos, dias, horas, minutos e segundos, registrar um momento específico não é tão fácil para um computador.

Isso porque um computador precisa estabelecer:

  • A partir de quando o tempo começou a ser contado, ou seja, qual é a referência de início do tempo.
  • Em que lugar do planeta, ou fuso horário, aquele momento foi registrado.
  • Quais são as influências políticas sobre aquele instante de tempo, como o horário de verão, por exemplo (sim, o horário de verão é uma decisão política!).

Além disso, todas essas referências podem mudar… ao longo do tempo!

Parece piada, mas não é…

Por exemplo, considere os 4 fusos horários (timezones) que o Brasil tem.

Se você quiser saber a diferença entre um horário em São Paulo e outro no Acre, é preciso saber em qual fuso horário está cada um desses estados.

Para piorar, o estado de São Paulo adota o horário de verão e o Acre não, mas isso só acontece nos anos em que se aplica esse horário diferenciado.

E o horário de verão só vale durante alguns meses desses anos…

Se você está achando difícil considerar tudo isso quando usar data e hora nos seus programas, saiba que pode piorar ainda mais…

Em 2008, o Acre mudou de fuso horário, de UTC -5 para UTC -4 (mais sobre horário UTC a seguir).

No entanto, em 2013, o Acre voltou ao fuso horário UTC -5!

Imagino que você já tenha entendido o tamanho do problema…

Então, como resolver isso?

Os Padrões Mundiais para Computar Datas e Horários

Para que seja possível calcular as datas e horários da mesma maneira em todos os computadores, é preciso haver um valor de referência, ou um ponto de partida comum.

Para isso, foram estabelecidos padrões mundiais.

Um desses padrões é o UTC, que é a sigla para Coordinated Universal Time, ou Tempo Universal Coordenado.

Esse é o horário na latitude 0°, mais conhecida como meridiano de Greenwich, que nunca muda.

Por exemplo, o horário de Brasília equivale ao horário UTC -3, ou seja, 3 horas a menos que o horário no marco inicial UTC.

As convenções para os fusos horários em todo o mundo são registradas no banco de dados de uma entidade internacional chamada IANA (Internet Assigned Numbers Authority).

A IANA libera atualizações regulares contendo as alterações previstas nos fusos horários, em média 6 meses antes que elas aconteçam.

No entanto, criar um ponto geográfico de referência para o tempo não é suficiente.

Isso porque é preciso responder a outra pergunta:

Em que momento os computadores consideram o início do tempo?

O início do ano 1 no calendário atual, conhecido como calendário gregoriano, pode parecer uma boa opção, mas tem alguns problemas:

  • Esse calendário só surgiu em 1582 e removeu alguns dias do calendário anterior, fazendo com que eles não tenham uma equivalência perfeita.
  • A maioria dos registros processados pelos computadores se referem a acontecimentos recentes, cerca de 2000 anos depois do ano 1.

Então, se você considerar que cada segundo é contado como uma unidade de um número inteiro, qualquer data por volta do ano 2000 já seria um número superior a 63 bilhões, o que seria um desperdício de memória para a grande maioria das operações.

Por isso, os computadores modernos contam o tempo a partir de um momento mais recente, chamado Era Unix (Unix epoch), que corresponde ao início do dia 1/1/1970.

Mesmo com essas convenções, ainda há problemas causados pelos ajustes entre os diferentes calendários que existiram ao longo da história e pelo Leap Second, que é provocado pela variação da rotação da Terra.

Dessa forma, as classes da biblioteca datetime do Python trabalham com uma versão idealizada do tempo, que considera como premissas:

  • Que todos os dias têm 86.400 segundos.
  • Que as mesmas regras do calendário gregoriano se aplicam para todas as datas no futuro e no passado, mesmo antes de 1582.

Como Lidar com as Convenções de Data e Hora nos seus Programas

Agora que você já viu como o computador resolve os problemas para contar o tempo, entenda como lidar com as limitações que isso gera.

Uma das decisões que você precisa tomar é como você vai gravar os registros de datas e horários.

Para eventos passados, basta salvar a data e a hora UTC, junto com o deslocamento do fuso horário em que o evento aconteceu.

Por exemplo, um evento que ocorreu em 25/12/2019, às 23:00 horas do horário de Brasília, pode ser registrado às 02:00 horas do dia 26/12/2019 no horário UTC, com o fuso horário UTC -3.

Isso porque, desde 2019, o Brasil não adota mais o horário de verão.

No entanto, para datas futuras, a situação é um pouco mais complicada, já que não é possível saber se a regra do horário de verão ou os fusos horários irão mudar.

Lembra que eu falei que essas são decisões políticas?

Pois é…

Usando o exemplo acima, se esse registro tivesse sido gravado no horário UTC em 2018, ou seja, antes da determinação do fim do horário de verão, ele teria uma forma semelhante a 26/12/2019 02:00, UTC -2.

Com isso, o horário calculado para o evento no horário de Brasília seria 26/12/2019 00:00, causando um atraso de uma hora em relação ao horário real que o evento ocorreu.

Nesse caso, a opção que dá o melhor resultado é salvar o registro no horário do local onde o evento vai ocorrer, junto com o fuso horário usado e com a versão do banco de dados de fusos horários publicado pela IANA.

Dessa forma, quando a versão do banco de dados mudar, é preciso reprocessar as datas para determinar se elas também mudaram.

E para você saber quando aquela data foi reprocessada, é recomendável que o registro também tenha uma data de última atualização.

Ufa, parece complicado, não é?

Pois é, mas ainda tem outro ponto que você precisa considerar…

Qual formato de data e hora seu programa deve apresentar?

Se alguém marcasse um encontro com você no segundo 1.600.995.600 depois da Era Unix, você não saberia de imediato que horas isso aconteceria.

Isso porque esse número inteiro é ótimo para os computadores, mas não serve para os seres humanos.

Para facilitar, as pessoas usam convenções de data em formato de texto, com números representando dia, mês, ano, hora e minuto.

Dessa forma, seria muito mais fácil saber que o seu compromisso seria no dia 24/09/2020, às 22:00 horas.

No entanto, no Brasil e nos países da Europa, o padrão para a data é mostrar o dia antes do mês.

Já nos Estados Unidos, o mês vem antes do dia…

Assim, se o seu programa salvar a data de 12/08/2020 em formato texto, isso vai ser 12 de agosto no Brasil e 8 de dezembro nos Estados Unidos!

Em conjuntos de dados que têm datas gravadas em formato texto, isso pode ser um grande problema.

Para resolver isso, também é usado um padrão internacional, chamado ISO 8601, que estabelece o formato de data AAAA-MM-DDTHH:MM:SS.SSS±HH.

É importante lembrar que esse tipo de registro com a data no formato UTC, contendo só o deslocamento do fuso horário, não é confiável para registro de eventos futuros.

Portanto, para que a data sempre seja apresentada de forma correta, é preciso converter seu valor numérico para o formato que corresponde à configuração de localização do computador cliente.

O Módulo datetime em Python

Assim como acontece para outras estruturas de dados, a linguagem Python fornece abstrações prontas para lidar com toda essa complexidade no armazenamento e na representação de datas e horas.

O principal módulo usado para isso é chamado datetime e tem as classes:

  • datetime.date – Usada para representar apenas datas simples, com ano, mês e dia, sem informação de fuso horário.
  • datetime.time – Representa uma versão idealizada do tempo, onde cada dia sempre tem 86.400 segundos, no formato hora, minuto, segundo e microssegundo, além do fuso horário.
  • datetime.datetime – Combina os valores das classes date e time em um objeto só. Cuidado para não fazer confusão com o nome do módulo, que é o mesmo da classe.
  • datetime.timedelta – Usada em cálculos para expressar a diferença em dias entre dois objetos do tipo date, time ou datetime, com precisão de microssegundos.
  • datetime.timezone – É a implementação concreta de datetime.tzinfo, que contém a informação do fuso horário como um deslocamento fixo a partir do horário UTC. Como você viu, essa forma de representação fixa não ajuda muito quando você precisa lidar com mudanças no fuso horário, ou com o horário de verão.

Você pode criar instâncias de objetos do tipo date, time ou datetime passando argumentos como dia, mês, ano, hora, minuto e segundo.

Para combinar objetos date e time em um só objeto do tipo datetime, use o método datetime.combine().

dataSimples = date(year=2020, month=9, day=24) print(dataSimples) # 2020-09-24 hora = time(hour=22, minute=10, second=30) print(hora) # 22:10:30 data = datetime(year=2020, month=9, day=24, hour=22, minute=10, second=30) print(data) # 2020-09-24 22:10:30 dataCompleta = datetime.combine(dataSimples, hora) print(dataCompleta) # 2020-09-24 22:10:30

Você pode omitir os nomes dos parâmetros, mas isso torna o seu código mais difícil de ler.

data = datetime(2020, 9, 24, 22, 10, 30) print(data) # 2020-09-24 22:10:30

Além disso, também é possível criar objetos date e datetime usando métodos úteis como today() e now(), ou a partir de um timestamp, ou seja, o inteiro que representa o número de segundos desde o início da Era Unix.

data = date.today() print(data) # 2020-09-25 data = datetime.now() print(data) # 2020-09-25 21:18:06.694389 timestamp = 1600995600 data = datetime.fromtimestamp(timestamp) print(data) # 2020-09-24 22:00:00

Veja a seguir os usos mais comuns do módulo datetime do Python.

Como Formatar uma Data em Python

Para formatar uma data em Python, use o método datetime.strftime(formato), que retorna uma string representando a data e a hora, de acordo com o formato passado como parâmetro.

Para retornar a data como uma string no format ISO 8601, use o método datetime.isoformat().

data = datetime(year=2020, month=9, day=24, hour=22, minute=10, second=30) print(data.strftime('%d/%m/%Y %H:%M:%S')) # 24/09/2020 22:10:30 print(data.strftime('%d-%m-%Y %H:%M')) # 24-09-2020 22:10 print(data.isoformat()) # 2020-09-24T22:10:30

Como Converter uma String em datetime no Python

Para converter uma string para datetime em Python, use o método datetime.strptime(string, formato), que cria um objeto do tipo datetime a partir da string passada como parâmetro, interpretada segundo o formato especificado.

Se a string estiver no formato ISO 8601, use o método datetime.fromisoformat(string).

Para converter uma string em um objeto do tipo date ou time, use o método datetime.strptime(string, formato) para criar um objeto do tipo datetime e depois use os métodos date() ou time() para retornar o objeto do tipo desejado.

Veja alguns exemplos.

data = datetime.strptime('24/09/2020 22:00', '%d/%m/%Y %H:%M') print(data) # 2020-09-24 22:00:00 data = datetime.fromisoformat('2020-09-24T22:00') print(data) # 2020-09-24 22:00:00 dataSimples = datetime.strptime('13-09-2020', '%d-%m-%Y').date() print(dataSimples) # 2020-09-13 horaSimples = datetime.strptime('11:45:30', '%H:%M:%S').time() print(horaSimples) # 11:45:30

Apesar de terem nomes muito parecidos, é fácil entender a diferença entre os métodos strptime() e strftime().

O método strptime() pode ser lido como string parse time, ou seja, avaliar uma string que deve ser transformada em um objeto datetime.

Já o método strftime() significa string format time, ou seja, formatar uma string a partir de um objeto datetime.

Você pode encontrar todos os formatos que podem ser passados para as funções strptime() e strftime() na documentação oficial do Python.

Como Trabalhar com Timezone em Python

Para trabalhar com timezones em Python, use o módulo dateutil, em vez do módulo datetime, como recomenda a documentação oficial.

A vantagem do módulo dateutil é que ele tem uma interface com o banco de dados de fusos horários da IANA, permitindo que os objetos datetime sejam construídos com os timezones corretos de forma automática, considerando as regiões do planeta, não os deslocamentos a partir do horário UTC.

Veja no exemplo a seguir como é simples criar um objeto datetime com a informação do fuso horário.

from dateutil import tz from datetime import datetime data = datetime(2020, 9, 24, 22, 10, 30, tzinfo=tz.tzlocal()) print(data) # 2020-09-24 22:10:30-03:00 print(data.tzname()) # E. South America Standard Time

Você pode escolher em que fuso horário quer criar um objeto datetime, usando o método tz.gettz().

Então, voltando ao exemplo da mudança de fuso horário do Acre e sabendo que o identificador de fuso horário IANA para aquele estado é America/Eirunepe, veja o que aconteceu no primeiro instante do dia 10/11/2013.

tzAC = tz.gettz('America/Eirunepe') dataAC9 = datetime(2013, 11, 9, 23, 59, 59, tzinfo=tzAC) dataAC10 = datetime(2013, 11, 10, 0, 0, tzinfo=tzAC) print(dataAC9) # 2013-11-09 23:59:59-04:00 print(dataAC210) # 2013-11-10 00:00:00-05:00

Nesse caso, se o seu programa só tivesse guardado o horário UTC e o deslocamento UTC -4, qualquer evento entre 23:00 e 23:59:59 do dia 9/11/2013 teria acontecido duas vezes no Acre.

Esse é o mesmo problema que acontece com o horário de verão.

!

Apesar do módulo ser importado com o nome dateutil, ele deve ser instalado com o nome python-dateutil, usando os gerenciadores de pacotes conda ou pip:

conda install python-dateutil

ou

pip install python-dateutil

Operações com datetime no Python

Para ter o máximo de flexibilidade em operações com datas e horários em Python, use a classe relativedelta do módulo dateutil.relativedelta, que permite calcular a diferença entre objetos do tipo date, time ou datetime, além de outras operações úteis, com base no calendário.

from datetime import date, datetime from dateutil.relativedelta import relativedelta hoje = date.today() umDia = relativedelta(days=+1) amanha = hoje + umDia print(hoje) # 2020-09-25 print(amanha) # 2020-09-26 agora = datetime.now() menosUmaHora = relativedelta(hours=-1) umaHoraAtras = agora + menosUmaHora print(agora) # 2020-09-25 23:00:12.454348 print(umaHoraAtras) # 2020-09-25 22:00:12.454348

A classe datetime.timedelta não é tão flexível, porque só permite somar ou subtrair dias de objetos datetime.

Por isso, cálculos com meses ou anos usando essa classe precisariam ser feitos com um número fixo de dias, como 30 e 365, não levando em consideração a duração correta dos meses e dos anos, produzindo resultados errados.

from datetime import date, timedelta # Fevereiro com 28 dias data = date(2019, 2, 1) # Fevereiro com 29 dias dataBis = date(2020, 2, 1) umMes = timedelta(days=+30) print(data + umMes) # 2019-03-03 print(dataBis + umMes) # 2020-03-02

Agora veja o mesmo exemplo usando a classe relativedelta.

from dateutil.relativedelta import relativedelta data = date(2019, 2, 1) dataBis = date(2020, 2, 1) umMes = relativedelta(months=+1) print(data + umMes) # 2019-03-01 print(dataBis + umMes) # 2020-03-01

Portanto, para realizar cálculos com anos e meses, sempre use a classe relativedelta, do módulo dateutil.relativedelta.

Veja a seguir alguns exemplos de operações com data e hora em Python usando relativedelta.

Como Calcular a Diferença Entre Duas Datas em Python

Para calcular com precisão a diferença entre duas datas em anos no Python, como a idade de uma pessoa, por exemplo, use relativedelta(data_final, data_inicial).years.

from datetime import date from dateutil.relativedelta import relativedelta nascimento = date(1997, 6, 1) hoje = date.today() idade = relativedelta(hoje, nascimento) print(idade.years) # 23 print(idade) # relativedelta(years=+23, months=+3, days=+24)

Como Somar ou Subtrair Horas em Python

Para somar ou subtrair horas em Python, use a classe datetime.timedelta, passando o número de horas a somar ou subtrair.

Isso porque, para operações com apenas dias ou horas, a flexibilidade da classe relativedelta é desnecessária, o que torna o uso de timedelta mais eficiente, já que essa classe faz parte da biblioteca padrão do Python, ou seja, você evita uma dependência externa.

Veja que os cálculos com dias ou horas não têm o mesmo problema que os cálculos com meses nos anos bissextos ou meses com quantidade diferente de dias.

from datetime import datetime, timedelta data = datetime(2019, 2, 28) dataBis = datetime(2020, 2, 28) tDelta = timedelta(hours=+30) print(data + tDelta) # 2019-03-01 06:00:00 print(dataBis + tDelta) # 2020-02-29 06:00:00 tDelta = timedelta(hours=-3) print(dataBis + tDelta) # 2020-02-27 21:00:00

Conclusão

Nesse post você aprendeu a usar as classes dos módulos datetime e dateutil para criar objetos representando datas e horários em Python, respeitando os fusos horários corretos.

Além disso, entendeu como realizar operações com datas e horários.

No entanto, eu espero que tenha ficado claro que o mais importante é conhecer as limitações dos computadores para representar o tempo, os padrões mundiais adotados e como tudo isso influencia a maneira de trabalhar com a data e a hora nos seus programas.

Você já tinha pensado em todos esses detalhes antes? Então comente aí embaixo!

Guilherme Brügger D Amato - Audiência Pública na Comissão Senado do Futuro

Guilherme Brügger D’Amato é servidor concursado de TI na Câmara dos Deputados, onde ocupou o cargo de Diretor de Informática entre 2015 e 2016. Com mais de 25 anos de experiência como programador e executivo de TI, já desenvolveu sites e sistemas usados por dezenas de milhões de pessoas. Conecte-se com ele no LinkedIn.

Deixe um comentário