"Datas e horários são algo que ensinamos a crianças pequenas. Quão difícil pode ser?"
Muitos programadores já cometeram esse erro, e a experiência que vem depois costuma fazer mal à saúde e à felicidade deles.
Quem faz programação não trivial com datas e horários deveria, no mínimo, se preparar para entender e atenuar possíveis problemas.
Este documento vai tentar dar uma visão geral de como o R lida com datas e horários, mas será necessário consultar a documentação completa para mais detalhes.
Por ser uma linguagem de ciência de dados, o R dá uma importância especial a datas e horários. Grande parte dos dados chega na forma de séries temporais: número diário de pacientes durante um surto de doença, dados mensais de emprego divulgados por governos, populações de aves em uma colônia de nidificação, PIB anual das principais economias, e incontáveis outros exemplos.
Isso significa que o R torna o tratamento de datas e horários simples?
Infelizmente, não!
A funcionalidade de datas e horários no R base tem má reputação, e hoje a maior parte dela raramente é usada.
O pacote lubridate é uma grande melhoria, mas esteja preparado para uma biblioteca grande e complicada, projetada para lidar com uma variedade grande e complicada de problemas.
Originalmente independente, o lubridate agora vem junto com a coleção tidyverse.
Carregue-o com library(lubridate) ou library(tidyverse).
Entre os recursos que ajudam você a trabalhar com o lubridate, estão:
Uma date é apenas um dia, sem nenhuma informação de horário.
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ções de data, horário e fuso horário.
Internamente, ele é 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 para surpresa geral, o R não tem representação interna para valores apenas de horário.
date e datetime
Talvez as necessidades de data e horário mais frequentes sejam:
today()) e/ou o datetime atual (com now()).Existem muitas formas de escrever datas e horários, e elas costumam variar de acordo com a cultura. Datas só com números, como "7/6/23", são ambíguas, confusas e já causaram muitos erros caros em organizações multinacionais (o autor fala isso por amarga experiência pessoal).
O padrão internacional (a-m-d) é definido na ISO 8601, com duas vantagens principais:
No entanto, ciência de dados é trabalhar com os dados que você consegue encontrar, e muitas vezes não adianta reclamar de como outras pessoas os formatam.
O R base tem funções de conversão que recebem uma string de formato (com ISO 8601 como padrão).
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 lembram a função strptime(), que existe há décadas e está incluída em muitas linguagens.
Programadores familiarizados com essa sintaxe podem, opcionalmente, usar a função fast_strptime() do lubridate.
De forma mais útil, o lubridate oferece uma ampla variedade de funções auxiliares, com nomes que representam a ordem dos campos.
Quando as funções sabem quais valores procurar e em que ordem, o parse é 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 seu programa tem os componentes de uma data ou datetime como números separados, eles podem ser montados com make_date() ou make_datetime().
Estes são os componentes que podem ser fornecidos, mostrando sua ordem e seus valores padrão:
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 de ano, mês e dia.
O console do R exibe datas e datetimes automaticamente como uma representação em string. Isso geralmente é conveniente, mas às vezes engana.
Internamente, datas e datetimes são números (mais alguns atributos), não strings. Retornar uma data (ou datetime) quando se espera uma string vai causar problemas, então precisamos saber como fazer a conversão.
Uma abordagem simples é usar as.character() do R base, que gera uma string no formato ISO 8601.
Também existe uma função format_ISO8601() para datetimes, que tem opções para arredondar para a precisão desejada.
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"
Poucas pessoas que não programam querem ver a saída em ISO 8601, então lidar com formatos de data e horário específicos de cada cultura é essencial.
O R base oferece a função format(), que usa strings de formato semelhantes às veneráveis funções strftime() incluídas em muitas outras linguagens.
format(today(), "%A, %d %B %Y")
#> [1] "Thursday, 23 April 2026"
Essa abordagem do R base é feia, mas confiável. Estes são alguns dos códigos mais comuns:
| Código | Resultado |
|---|---|
| %d | Número do dia no 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, domingo é 0) |
| %u | Número do dia da semana (1-7, segunda-feira é 1) |
| %b | Mês abreviado (Jan, Feb, etc.) |
| %B | Mês completo (January, February, etc.) |
| %y | Ano com 2 dígitos (por exemplo, 89) |
| %Y | Ano com 4 dígitos (por exemplo, 1989) |
| %h | horas (relógio de 24 horas) |
| %m | minutos |
| %s | segundos |
| %z | deslocamento em relação ao UTC |
| %Z | Fuso horário (caractere) |
O lubridate busca oferecer uma alternativa mais amigável para humanos com a função stamp(), além das mais específicas stamp_date() e stamp_time().
Elas recebem uma string modelo, que é um exemplo da saída desejada, e criam uma função personalizada para gerar esse formato.
O lubridate tenta adivinhar o formato, mas muitas vezes esbarra em ambiguidades e produz o resultado errado.
Esteja preparado para fornecer strings de formato (como "dmy") para desfazer a ambiguidade.
No Exercism, também pode ser útil especificar locale = "C", para ignorar o locale do seu sistema e trabalhar em inglês (como os testes esperam).
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 oferece muitas funções para obter ou definir elementos de uma data ou datetime: umas óbvias, como year() e second(), e outras mais especializadas, como quarter() e epiweek().
Todas essas funções podem ser usadas para obter um valor, e a maioria também pode 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 padrão é uma saída de número inteiro, mas:
labels = TRUE retorna uma string de caracteres abreviada correspondente ao locale atual.abbr = FALSE retorna 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 tema de outro conceito.
Os valores ficam restritos aos Levels listados, que também mostram a ordem de classificação.
Por padrão, a semana começa no domingo, mas isso pode ser alterado com o argumento week_start (use 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 retornam valores TRUE/FALSE, como leap_year() e dst() (o horário de verão está ativo?).
Os meses variam de duração, então days_in_month() dá o valor inteiro.
Tanto valores date quanto datetime representam um instante na linha do tempo.
Como lidamos com conceitos como "a esta hora ontem" ou "daqui a uma semana"?
Quem já conhece outras linguagens voltadas à ciência pode estar familiarizado com datetime.timedelta do Python ou com o tipo Period do Julia.
Em contrapartida, o R tem nada menos que três formas de representar intervalos de tempo, com pontos fortes e limitações diferentes. Como sempre, escolha a que melhor se adequa ao seu contexto.
Os vários tipos de intervalo podem ser convertidos facilmente 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 um horário em 3 horas.
Irregularidades como o horário de verão 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 o horário de verão, que fazem o horário de relógio se desviar do tempo físico.
Todas as durações adicionam um prefixo d ao período correspondente: dyears, dweeks, etc.
dhours(3)
#> [1] "10800s (~3 hours)"
Um interval é a combinação de um horário de início e um 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 desafiador. A citação abaixo é da documentação do Python, mas é igualmente relevante para o R:
"As regras de ajuste de horário pelo mundo são mais políticas do que racionais, mudam com frequência, e não há padrão adequado para toda aplicação além do UTC."
Alguns pontos gerais sobre fusos horários:
datetime sempre inclui um fuso horário (tz); uma date não tem noção de fuso horário.now() é uma exceção: ela pega o seu horário local do relógio do sistema, incluindo o tz.
O tz local é usado por padrão, mas outros podem ser especificados, 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 de fusos horários com durações é mais confiável do que com períodos.
Trabalhar com datas e datetimes costuma ser muito mais fácil com o lubridate do que com o R base.
Este conceito tentou dar uma visão geral, mas inevitavelmente deixa de fora muitos detalhes.
Com o risco de ser repetitivo: a documentação é sua amiga, então use-a, por favor.
Neste exercício, você vai trabalhar em um agendador de compromissos para um salão de beleza em Nova York que abriu em 15 de setembro de 2012.
Você tem cinco tarefas, e todas elas envolvem datas de compromissos. Os horários estão no formato de 24 horas (sem AM/PM). Para manter as questões de fuso horário o mais simples possível, considere que tudo usa UTC.
Implemente a função schedule_appointment() para converter uma representação textual de uma data de compromisso no formato DateTime correspondente.
Repare que a entrada está no formato mês/dia/ano, ao estilo dos Estados Unidos.
julia> schedule_appointment("07/25/2023 13:45:00")
2023-07-25T13:45:00
Implemente a função has_passed(), que recebe uma data de compromisso e verifica se o compromisso ficou em algum momento no passado:
julia> has_passed(DateTime(1999, 12, 31, 9, 0, 0))
true
Implemente a função is_afternoon_appointment(), que recebe uma data de compromisso e verifica se o compromisso é à tarde (>= 12:00 e < 18:00):
julia> is_afternoon_appointment(DateTime(2023, 3, 29, 15, 0, 0))
true
Implemente a função day_of_week(), que retorna o dia da semana em formato numérico.
O salão fecha aos domingos, então 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 remarcar um compromisso, isso é feito automaticamente para a próxima sexta-feira, no mesmo horário do dia (já que é o dia mais tranquilo da semana).
Implemente a função reschedule(), que recebe o compromisso original (como uma string de texto no formato mês/dia/ano) e retorna um compromisso remarcado.
# a Saturday
reschedule("06/13/2026 12:30:00")
#> [1] "2026-06-19 12:30:00 UTC"
Se o compromisso original era de segunda a quinta-feira, remarque para a sexta-feira desta semana. Para compromissos de sexta ou sábado, remarque para a sexta-feira da próxima semana.
Crie sua conta no Exercism para aprender e dominar R com 21 conceitos111 exercícios e mentoria humana de verdade, tudo de graça.