"As datas e as horas são coisas que ensinamos a crianças pequenas. Que dificuldade pode haver?"
Muitos programadores cometeram esse erro, e a experiência que se segue costuma ter um efeito negativo na sua saúde e na sua felicidade.
Quem faz programação não trivial com datas e horas deve, no mínimo, estar preparado para compreender e mitigar potenciais problemas.
Este documento vai tentar dar uma visão geral de como o R trata as datas e as horas, mas será necessário consultar a documentação completa para mais detalhes.
Enquanto linguagem de ciência de dados, as datas e as horas têm uma importância particular no R. Grande parte dos dados chega na forma de séries temporais: o número diário de doentes durante um surto de doença, os números mensais do emprego reportados pelos governos, populações de aves numa colónia de nidificação, o PIB anual das principais economias, e infindáveis exemplos mais.
Isto significa que o R torna o tratamento de datas e horas simples?
Infelizmente, não!
A funcionalidade de datas e horas no Base R tem má reputação e, atualmente, grande parte já é raramente usada.
O pacote lubridate é uma grande melhoria, mas prepara-te para uma biblioteca grande e complicada, concebida para lidar com uma grande e complicada variedade de problemas.
Originalmente independente, o lubridate está agora incluído na coleção tidyverse.
Carrega-o com library(lubridate) ou library(tidyverse).
Alguns recursos para te ajudar a trabalhar com o lubridate:
Uma date é apenas um dia, sem informação de horas.
Internamente, as datas são armazenadas como o número (com sinal) de dias desde 1970-01-01.
today()
#> [1] "2026-04-22"
Um datetime combina informação de data, hora e fuso horário.
Internamente, é armazenado como o número de segundos desde o início de 1970, no fuso horário UTC.
now()
#> [1] "2026-04-22 15:37:11 MST" # mid-afternoon in Arizona
Talvez surpreendentemente, o R não tem nenhuma representação interna para valores apenas de hora.
date e datetime
As necessidades mais frequentes com datas e horas são, provavelmente:
today()) e/ou o datetime atual (com now()).Há muitas formas de escrever datas e horas, que tendem a ser específicas de cada cultura. As datas só com números, como "7/6/23", são ambíguas, confusas, e já levaram a muitos erros dispendiosos em organizações multinacionais (o autor fala por amarga experiência pessoal).
O padrão internacional (ano-mês-dia) está definido na ISO 8601, com duas vantagens principais:
No entanto, a ciência de dados consiste em trabalhar com os dados que consegues encontrar, e muitas vezes não vale a pena queixarmo-nos de como as outras pessoas os formatam.
O Base R tem funções de conversão que recebem uma string de formato (com ISO 8601 como valor predefinido).
as.Date("2026-04-22")
#> [1] "2026-04-22"
as.Date("4/22/26", format = "%m/%d/%y")
#> [1] "2026-04-22"
as.POSIXct("2026-04-22 15:30:09 MST")
#> [1] "2026-04-22 15:30:09 MST"
As strings de formato são semelhantes à função strptime(), com décadas de existência, incluída em muitas linguagens.
Os programadores familiarizados com esta sintaxe podem, opcionalmente, usar a função fast_strptime() do lubridate.
De forma mais útil, o lubridate disponibiliza uma vasta gama de funções auxiliares, com nomes que representam a ordem dos campos.
Assim que as funções sabem que valores procurar, e em que ordem, a interpretação é impressionantemente flexível.
mdy("4/22/26")
#> [1] "2026-04-22"
mdy("April 22nd 26")
#> [1] "2026-04-22"
mdy("Wednesday, April 22nd 2026")
#> [1] "2026-04-22"
mdy_hm("Wednesday, April 22nd 2026, 4:14 pm")
#> [1] "2026-04-22 16:14:00 UTC" # defaults to your OS timezone
Quando o teu programa tem os componentes de uma data ou de um datetime como números separados, podem ser reunidos com make_date() ou make_datetime().
Estes são os componentes que podem ser fornecidos, mostrando a sua ordem e os valores predefinidos:
make_datetime(
year = 1970L,
month = 1L,
day = 1L,
hour = 0L,
min = 0L,
sec = 0,
tz = "UTC"
)
#> [1] "1970-01-01 UTC"
make_datetime(2026, 4, 22, 17)
#> [1] "2026-04-22 17:00:00 UTC"
Naturalmente, make_date() recebe apenas os argumentos year, month e day.
A consola do R apresenta automaticamente as datas e os datetimes como uma representação em string. Isto é habitualmente prático, mas por vezes enganador.
Internamente, as datas e os datetimes são números (mais alguns atributos), não strings. Devolver uma data ou um datetime quando se espera uma string vai causar problemas, por isso precisamos de saber como fazer a conversão.
Uma abordagem simples é usar as.character() do Base R, que devolve uma string no formato ISO 8601.
Existe também uma função format_ISO8601() para datetimes, que tem opções para arredondar para a precisão pretendida.
today() |> as.character() # a date
#> [1] "2026-04-23"
now() |> as.character() # a datetime
#> [1] "2026-04-23 09:07:09.779319"
now() |> format_ISO8601() # defaults to nearest second
#> [1] "2026-04-23T09:07:38"
Poucos não-programadores querem ver resultados em ISO 8601, por isso é essencial lidar com formatos de data e hora específicos de cada cultura.
O Base R disponibiliza a função format(), que usa strings de formato semelhantes às venerandas funções strftime() incluídas em muitas outras linguagens.
format(today(), "%A, %d %B %Y")
#> [1] "Thursday, 23 April 2026"
Essa abordagem do Base R é feia, mas fiável. Estes são alguns dos códigos mais comuns:
| Code | Result |
|---|---|
| %d | Número do dia do mês (5, 17, 28, etc.) |
| %a | Dia da semana abreviado (Mon, Tue, Wed, etc.) |
| %A | Dia da semana completo (Monday, Tuesday, etc.) |
| %w | Número do dia da semana (0-6, o domingo é 0) |
| %u | Número do dia da semana (1-7, a segunda-feira é 1) |
| %b | Mês abreviado (Jan, Feb, etc.) |
| %B | Mês completo (January, February, etc.) |
| %y | Ano com 2 algarismos (por exemplo, 89) |
| %Y | Ano com 4 algarismos (por exemplo, 1989) |
| %h | horas (relógio de 24 horas) |
| %m | minutos |
| %s | segundos |
| %z | desvio em relação ao UTC |
| %Z | Fuso horário (caráter) |
O lubridate procura oferecer uma alternativa mais amiga do utilizador com a função stamp(), e com as mais específicas stamp_date() e stamp_time().
Estas recebem uma string de modelo que é um exemplo do resultado pretendido, e criam uma função personalizada para gerar esse formato.
O lubridate tenta adivinhar o formato, mas depara-se frequentemente com ambiguidades e produz o resultado errado.
Prepara-te para fornecer strings de formato (como "dmy") para desfazer ambiguidades.
No Exercism, também pode ser útil especificar locale = "C", para ignorar o locale do teu sistema e trabalhar em inglês (como os testes esperam).
Os locales são complicados!
sf <- stamp("Saturday, June 27, 2026 at 14:45", locale = "C")
#> Multiple formats matched: "%A, %B %d, %Y at %H:%M"(1), "Saturday, %Om %d, %Y at %H:%M"(1), "Saturday, %B %d, %Y at %H:%M"(1), "%A, %Om %d, %Y at %H:%M"(0)
#> Using: "%A, %B %d, %Y at %H:%M"
sf(make_datetime(2023, 3, 29, 15, 0, 0))
#> [1] "Wednesday, March 29, 2023 at 15:00"
O lubridate disponibiliza muitas funções para obter ou definir elementos de uma data ou datetime: umas óbvias, como year() e second(), mas também outras mais especializadas, como quarter() e epiweek().
Todas estas funções podem ser usadas para obter um valor, e a maioria também pode ser usada para definir o valor.
dt <- now()
dt
#> [1] "2026-04-23 15:47:11 MST"
# get
month(dt)
#> [1] 4
# set
month(dt) <- 11
dt
#> [1] "2026-11-23 15:47:11 MST"
Para wday() (dia da semana) e month(), o resultado predefinido é um número inteiro, mas:
labels = TRUE devolve uma string de carateres abreviada correspondente ao locale atual.abbr = FALSE devolve o nome completo.d <- today()
d
#> [1] "2026-04-23"
wday(d)
#> [1] 5
wday(d, label = TRUE)
#> [1] Thu
#> Levels: Sun < Mon < Tue < Wed < Thu < Fri < Sat
wday(d, label = TRUE, abbr = FALSE)
#> [1] Thursday
#> Levels: Sunday < Monday < Tuesday < Wednesday < Thursday < Friday < Saturday
Os rótulos são categorical variables, que serão o tema de outro Conceito.
Os valores estão restritos aos Levels listados, que também mostram a ordem de ordenação.
A semana começa ao domingo por predefinição, mas isto pode ser alterado com o argumento week_start (usa 1 para segunda-feira).
dt <- make_datetime(2026, 4, 23)
# default behavior
wday(dt)
#> [1] 5
wday(dt, label = T)
#> [1] Thu
# set week_start to Monday
wday(dt, week_start = 1)
#> [1] 4
Algumas funções utilitárias devolvem valores TRUE/FALSE, como leap_year() e dst() (a hora de verão está ativa?).
Os meses têm durações diferentes, por isso days_in_month() dá o valor inteiro.
Tanto os valores date como os datetime representam um instante ao longo da linha do tempo.
Como lidamos com conceitos como "a esta hora ontem" ou "daqui a uma semana"?
Os alunos familiarizados com outras linguagens orientadas para a ciência podem conhecer o datetime.timedelta do Python ou o tipo Period do Julia.
Em contrapartida, o R tem nada menos do que três formas de representar intervalos de tempo, com diferentes pontos fortes e limitações. Como sempre, escolhe a que melhor se adapta ao teu contexto.
Os vários tipos de intervalo podem ser facilmente convertidos entre si, com as.period(), as_duration() e as.interval().
Os tipos de intervalo podem ser verificados com is.period(), etc.
Um period, como hours(3), pode ser usado para incrementar uma hora de relógio em 3 horas.
As irregularidades, como a hora de verão (DST), são em grande parte ignoradas.
Todos os períodos são o nome de uma unidade no plural: years, weeks, etc.
d <- today()
d
#> [1] "2026-04-25"
d + weeks(2)
#> [1] "2026-05-09"
# dates are coerced to datetimes at midnight if necessary
d - hours(20)
#> [1] "2026-04-24 04:00:00 UTC"
hours(3)
#> [1] "3H 0M 0S"
Uma duration, como dhours(3), incrementa o tempo físico em 3 horas, respeitando irregularidades como a hora de verão que fazem a hora de relógio divergir do tempo físico.
Todas as durações acrescentam o prefixo d ao período correspondente: dyears, dweeks, etc.
dhours(3)
#> [1] "10800s (~3 hours)"
Um interval é uma combinação de uma hora de início e de fim, criado com a função interval() ou com o operador infixo %--%.
# the next 2 lines were run about 33s apart, so now() is different
dt1 <- now()
dt2 <- now() + hours(1)
# interval(0 function)
ivl <- interval(dt1, dt2)
ivl
#> [1] 2026-04-24 16:24:54 MST--2026-04-24 17:25:27 MST
# infix operator
iv2 <- dt1 %--% dt2
iv2 == iv1
#> [1] TRUE
Lidar com fusos horários pode ser um desafio. A citação seguinte é da documentação do Python, mas é igualmente relevante para o R:
"As regras de ajuste da hora em todo o mundo são mais políticas do que racionais, mudam com frequência, e não há nenhum padrão adequado a todas as aplicações além do UTC."
Alguns pontos gerais sobre fusos horários:
datetime inclui sempre um fuso horário (tz); uma date não tem noção de fuso horário.now() é uma exceção: obtém a tua hora local a partir do relógio do sistema, incluindo o tz.
O tz local é usado por predefinição, mas podem especificar-se outros, como em now("UTC").# timezone defaults to UTC
local <- ymd_hm("2026-04-25 10:32")
local
#> [1] "2026-04-25 10:32:00 UTC"
# timezone can be specified
AZ <- ymd_hm("2026-04-25 10:32", tz = "America/Phoenix")
AZ
#> [1] "2026-04-25 10:32:00 MST"
# same clocktime, different tz
local - AZ
#> Time difference of -7 hours
A aritmética com fusos horários é mais fiável com durações do que com períodos.
Trabalhar com datas e datetimes é habitualmente muito mais fácil com o lubridate do que com o Base R.
Este Conceito tentou dar uma visão geral, mas deixa inevitavelmente de fora muitíssimos detalhes.
Correndo o risco de repetir: a documentação é tua amiga, por isso usa-a.
Neste exercício vais trabalhar num agendador de marcações para um salão de beleza em Nova Iorque que abriu a 15 de setembro de 2012.
Tens cinco tarefas, todas relacionadas com datas de marcações. As horas estão em formato de 24 horas (sem AM/PM). Para manter as questões de fuso horário o mais simples possível, assume que tudo usa UTC.
Implementa a função schedule_appointment() para interpretar uma representação textual de uma data de marcação para o formato DateTime correspondente.
Repara que o valor de entrada está na ordem mês/dia/ano, ao estilo dos Estados Unidos.
julia> schedule_appointment("07/25/2023 13:45:00")
2023-07-25T13:45:00
Implementa a função has_passed(), que recebe uma data de marcação e verifica se a marcação se situa algures no passado:
julia> has_passed(DateTime(1999, 12, 31, 9, 0, 0))
true
Implementa a função is_afternoon_appointment(), que recebe uma data de marcação e verifica se a marcação é à tarde (>= 12:00 e < 18:00):
julia> is_afternoon_appointment(DateTime(2023, 3, 29, 15, 0, 0))
true
Implementa a função day_of_week(), que devolve o dia da semana numérico.
O salão está fechado aos domingos, por isso segunda-feira é o dia 1 e sábado é o dia 6.
# a Friday
day_of_week(make_datetime(2026, 06, 05, 12, 30, 00))
#> [1] 5
Quando um cliente precisa de reagendar uma marcação, esta é automaticamente marcada para a sexta-feira seguinte, à mesma hora do dia (por ser o dia mais calmo da semana).
Implementa a função reschedule(), que recebe a marcação original (como string de texto na ordem mês/dia/ano) e devolve uma marcação reagendada.
# a Saturday
reschedule("06/13/2026 12:30:00")
#> [1] "2026-06-19 12:30:00 UTC"
Se a marcação original era de segunda a quinta-feira, reagenda para a sexta-feira desta semana. Para marcações de sexta-feira ou sábado, reagenda para a sexta-feira da semana seguinte.
Inscreve-te no Exercism para aprenderes e dominares R com 21 conceitos111 exercícios, e mentoria humana real, tudo grátis.