LocalDate, LocalTime e LocalDateTime: o que são, trabalho com data

Autor: IT Sectr Publicado: 2026-07-13 Tempo de leitura: 12 min

LocalDate, LocalTime e LocalDateTime são as principais classes do pacote java.time que fornecem manipulação de data e hora sem vinculação a fuso horário. De acordo com a documentação da Oracle (Java 17, 2024), esses tipos são projetados como imutáveis e thread-safe, tornando-os seguros para aplicações multithread. Eles se tornaram disponíveis no Android através de desugaring a partir da API 26 e, para versões mais antigas — através da biblioteca ThreeTenABP.

Pontos-chave

  • LocalDate — uma classe imutável para representar uma data (ano, mês, dia) sem hora e fuso horário.
  • LocalTime — uma classe imutável para representar a hora (hora, minuto, segundo, nanossegundo) sem data e fuso horário.
  • LocalDateTime — uma combinação de LocalDate e LocalTime que armazena data e hora sem vinculação a fuso horário.
  • As três classes suportam operações aritméticas — adição e subtração de dias, meses, horas através dos métodos plus e minus.
  • No Android estes tipos estão disponíveis através de desugaring (API 26+) ou da biblioteca ThreeTenABP (API < 26).

O que são LocalDate, LocalTime e LocalDateTime?

LocalDate — uma classe que representa uma data no formato ano-mês-dia sem informações de hora e fuso horário. É usado para armazenar dados como aniversários, datas de eventos ou datas de validade.

LocalDate armazena um ano no intervalo de -999999999 a +999999999, um mês de 1 a 12 e um dia do mês considerando anos bissextos. A classe é completamente imutável — qualquer operação retorna um novo objeto.

LocalTime representa a hora do dia: horas, minutos, segundos e nanossegundos. A precisão máxima é de até um nanossegundo. LocalTime não contém informações de data ou fuso horário, tornando-o conveniente para armazenar horários de abertura ou duração de processos.

LocalDateTime combina LocalDate e LocalTime em um único objeto. É o tipo mais usado quando você precisa armazenar data e hora, mas a vinculação a fuso horário não é necessária. Por exemplo, a data e hora de um concerto em formato local.

De acordo com Oracle Java Documentation (2024), as três classes são projetadas com base em ideias da biblioteca Joda-Time, mas com arquitetura melhorada e integração total na biblioteca padrão.

Como funciona o pacote java.time?

O pacote java.time foi introduzido no Java 8 como substituto das obsoletas classes Date, Calendar e SimpleDateFormat. Sua arquitetura é construída nos princípios de objetos imutáveis e interface fluente.

Uma característica fundamental — todas as classes principais são value-based. Isso significa que suas instâncias são comparadas por valor, não por referência, e não podem ser herdadas. Para comparar dois objetos, use o método equals, não o operador ==.

O pacote é dividido em várias categorias. Tipos sem fuso horário — LocalDate, LocalTime, LocalDateTime — são usados para datas e horas locais. Tipos com fuso horário — ZonedDateTime, OffsetDateTime, OffsetTime — adicionam informações de deslocamento ou zona. Tipos instantâneos — Instant — representam um ponto na linha do tempo em UTC.

Essa separação resolve um problema inerente à API antiga: o desenvolvedor nunca sabia se um objeto Date continha informações de fuso horário ou não. Em java.time, cada tipo declara explicitamente sua semântica.

LocalDate: trabalho com data

A classe LocalDate fornece muitos métodos para criar, ler e modificar datas. A data atual pode ser obtida através do método estático now(). Uma data específica — através do método of(int year, int month, int dayOfMonth).

Para ler os componentes da data, são usados getters: getYear(), getMonthValue(), getDayOfMonth(), getDayOfWeek(), getDayOfYear(). O método getMonth() retorna o enum Month, e getDayOfWeek() retorna o enum DayOfWeek.

LocalDate suporta verificação de datas. Os métodos isBefore(), isAfter() e isEqual() permitem comparar datas. O método isLeapYear() verifica se o ano é bissexto. O método lengthOfMonth() retorna o número de dias no mês, e lengthOfYear() retorna o número de dias no ano.

Para modificação, use os métodos withYear(), withMonth(), withDayOfMonth(), que retornam um novo objeto com o componente alterado. Os métodos plusDays(), minusMonths() e similares realizam aritmética de datas.

LocalTime: trabalho com hora

LocalTime representa a hora do dia com precisão de nanossegundos. O formato padrão é ISO-8601 (HH:mm:ss.nnnnnnnnn). O valor mínimo é 00:00, o máximo é 23:59:59.999999999.

Você pode criar um objeto LocalTime usando now() para a hora atual, of(int hour, int minute), of(int hour, int minute, int second) ou of(int hour, int minute, int second, int nanoOfSecond). O método parse(CharSequence text) analisa uma string no formato ISO-8601.

Os getters incluem getHour(), getMinute(), getSecond(), getNano(). O método toSecondOfDay() retorna o número de segundos desde o início do dia, e toNanoOfDay() retorna nanossegundos. Isso é útil para calcular a duração dentro de um único dia.

LocalTime suporta as mesmas operações de comparação e modificação que LocalDate: plusHours(), minusMinutes(), withHour(), withMinute(). Os métodos isBefore() e isAfter() funcionam considerando que a hora é cíclica dentro de um dia.

LocalDateTime: combinação de data e hora

LocalDateTime combina as capacidades de LocalDate e LocalTime em uma única classe. Ele armazena data e hora, mas sem fuso horário. É o tipo local mais flexível, mas requer cautela ao ser usado em sistemas distribuídos.

Você pode criar LocalDateTime através dos métodos estáticos now(), of(LocalDate date, LocalTime time), of(int year, Month month, int dayOfMonth, int hour, int minute) e suas sobrecargas. Você também pode combinar LocalDate e LocalTime através do método atTime().

LocalDateTime fornece acesso a todos os campos de data e hora através de getters correspondentes: toLocalDate() e toLocalTime() retornam componentes individuais. O método truncatedTo(TemporalUnit unit) permite arredondar a hora para uma precisão determinada — por exemplo, para minutos.

Para converter para um fuso horário, use o método atZone(ZoneId zone), que retorna ZonedDateTime. Esta é a única maneira de adicionar um fuso horário ao LocalDateTime.

Como criar objetos de data e hora?

As três classes usam um padrão de criação unificado através de métodos estáticos de fábrica. Os construtores das classes são declarados como private — você não pode criar um objeto diretamente com new.

Métodos principais de criação:

  • now() — data/hora atual do relógio do sistema
  • of(...) — a partir de componentes (ano, mês, dia, etc.)
  • parse(String) — a partir de uma string no formato ISO-8601
  • from(TemporalAccessor) — a partir de outro objeto temporal

O método of tem muitas sobrecargas. Para LocalDate, você precisa de ano, mês e dia. Para LocalTime — horas e minutos (opcionalmente segundos e nanossegundos). Para LocalDateTime — ano, mês, dia, horas, minutos. O mês pode ser passado como int (1-12) ou como o enum Month.

kotlin
val today = LocalDate.now()
val specificDate = LocalDate.of(2026, Month.JULY, 21)
val parsedDate = LocalDate.parse("2026-07-21")

val currentTime = LocalTime.now()
val lunchTime = LocalTime.of(13, 30, 0)
val parsedTime = LocalTime.parse("13:30:00")

val now = LocalDateTime.now()
val meeting = LocalDateTime.of(2026, 7, 21, 15, 0)

Conversão entre tipos

As classes java.time são projetadas para conversão conveniente entre si. LocalDate pode ser convertido para LocalDateTime através do método atTime(LocalTime) ou atStartOfDay(). LocalTime — através de atDate(LocalDate).

LocalDateTime pode ser convertido de volta para LocalDate através de toLocalDate() e para LocalTime através de toLocalTime(). Para converter para ZonedDateTime, use o método atZone(ZoneId).

A conversão para java.util.Date (para compatibilidade com código legado) requer uma etapa intermediária através de Instant e um fuso horário. De acordo com Baeldung (2024), esta operação é realizada através de Date.from(instant).

kotlin
val date = LocalDate.of(2026, 7, 21)
val dateTime = date.atTime(LocalTime.of(10, 30))

val time = LocalTime.of(14, 0)
val dateTimeFromTime = time.atDate(date)

val extractedDate = dateTime.toLocalDate()
val extractedTime = dateTime.toLocalTime()

val zoned = dateTime.atZone(ZoneId.of("Europe/Moscow"))

Formatação e análise

Para formatação e análise, a classe DateTimeFormatter é usada. Ela fornece formatos predefinidos através de constantes (ISO_LOCAL_DATE, ISO_LOCAL_TIME, ISO_LOCAL_DATE_TIME) e a capacidade de criar formatos personalizados usando strings de padrão.

Os padrões de formatação usam símbolos: yyyy — ano, MM — mês (dois dígitos), dd — dia, HH — hora (0-23), mm — minuto, ss — segundo. O método format() é chamado no objeto data-hora ou através de DateTimeFormatter.

DateTimeFormatter também suporta localização através dos métodos estáticos ofLocalizedDate(FormatStyle), ofLocalizedTime(FormatStyle) e ofLocalizedDateTime(FormatStyle). Os estilos disponíveis são SHORT, MEDIUM, LONG e FULL.

kotlin
val formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm")
val formatted = LocalDateTime.now().format(formatter)

val parsed = LocalDate.parse(
    "21.07.2026",
    DateTimeFormatter.ofPattern("dd.MM.yyyy")
)

Comparação de objetos data-hora

As três classes implementam a interface Comparable, permitindo que sejam comparadas naturalmente. O método compareTo() retorna um número negativo, zero ou positivo dependendo da ordem. Os métodos isBefore(), isAfter() e isEqual() retornam um booleano.

Para LocalDate, a comparação é cronológica — uma data anterior é considerada menor. Para LocalTime — pela hora do dia. Para LocalDateTime — primeiro pela data, depois pela hora. Todas as comparações consideram corretamente os anos bissextos e o número de dias nos meses.

Uma diferença importante da API antiga: equals() para LocalDate, LocalTime e LocalDateTime compara valores, não referências. Isso significa que dois objetos com os mesmos campos serão iguais, mesmo que sejam instâncias diferentes.

kotlin
val d1 = LocalDate.of(2026, 7, 21)
val d2 = LocalDate.of(2026, 12, 25)

if (d1.isBefore(d2)) {
    Log.d("Data", "d1 é anterior a d2")
}

val sortedDates = listOf(d2, d1).sorted()

Aritmética de data e hora

As três classes suportam operações aritméticas através dos métodos plus e minus. Para LocalDate, estão disponíveis plusDays(), plusWeeks(), plusMonths(), plusYears() e os métodos minus correspondentes. LocalTime suporta plusHours(), plusMinutes(), plusSeconds(), plusNanos().

LocalDateTime herda todas as operações aritméticas de ambos os tipos. Uma característica notável do LocalDate: ao adicionar um mês, os resultados lidam corretamente com diferentes comprimentos de meses. Por exemplo, 31 de janeiro + 1 mês = 28 (29 em ano bissexto) de fevereiro.

Para operações mais complexas, existem as classes Period (para datas) e Duration (para tempo). Os métodos plus(TemporalAmount) e minus(TemporalAmount) aceitam esses objetos.

kotlin
val today = LocalDate.now()
val nextWeek = today.plusDays(7)
val nextMonth = today.plusMonths(1)
val lastYear = today.minusYears(1)

val now = LocalTime.now()
val inTwoHours = now.plusHours(2)
val halfHourAgo = now.minusMinutes(30)

Exemplos de código em Kotlin

Vamos ver um exemplo prático: um aplicativo de registro de turnos de trabalho. Precisamos calcular a duração do turno e determinar se ele cai no período noturno. Usamos LocalTime para os horários de início e fim, LocalDate para a data e LocalDateTime para calcular turnos que ultrapassam a meia-noite.

kotlin
data class Shift(
    val startTime: LocalTime,
    val endTime: LocalTime,
    val date: LocalDate
) {
    fun isOvernight(): Boolean = endTime.isBefore(startTime)

    fun durationInMinutes(): Long {
        val start = LocalDateTime.of(date, startTime)
        val end = LocalDateTime.of(
            if (isOvernight()) date.plusDays(1) else date,
            endTime
        )
        return Duration.between(start, end).toMinutes()
    }
}

Um segundo exemplo — cálculo da idade de um usuário. Usamos LocalDate para a data de nascimento e comparamos com a data atual, considerando o dia e mês de nascimento.

kotlin
fun calculateAge(birthDate: LocalDate): Int {
    val today = LocalDate.now()
    val period = Period.between(birthDate, today)
    return period.years
}

Um terceiro exemplo — trabalho com notificações. LocalDateTime é usado para agendar lembretes. Verificamos se o horário agendado chegou.

kotlin
data class Reminder(
    val id: Long,
    val scheduledAt: LocalDateTime
) {
    fun isDue(): Boolean =
        LocalDateTime.now().isAfter(scheduledAt)
}

Compatibilidade com Android: nível de API e desugaring

O suporte integrado para java.time apareceu no Android a partir da API 26 (Android 8.0 Oreo). Para dispositivos com versões Android mais antigas, é necessário usar desugaring — um mecanismo que adiciona suporte para novas APIs Java em versões anteriores.

O desugaring no Android Gradle Plugin é configurado através de compileOptions no build.gradle. Basta definir isCoreLibraryDesugaringEnabled = true e adicionar a biblioteca desugar_jdk_libs. Depois disso, java.time fica disponível para todos os níveis de API a partir de 14.

Para projetos que não podem usar desugaring (por exemplo, projetos legados com AGP abaixo de 4.0), existe a biblioteca ThreeTenABP — um backport do java.time. Ela fornece as mesmas classes (LocalDate, LocalTime, LocalDateTime), mas no pacote org.threeten.bp.

groovy
@Suppress("UnstableApiUsage")
android {
    compileOptions {
        isCoreLibraryDesugaringEnabled = true
    }
}

dependencies {
    "coreLibraryDesugaring"("com.android.tools:desugar_jdk_libs:2.1.4")
}

Erros comuns e como evitá-los

O primeiro erro comum — usar LocalDateTime em sistemas distribuídos sem considerar fusos horários. Se o servidor estiver em Europe/Moscow e o cliente em Asia/Tokyo, o LocalDateTime será interpretado de forma diferente. Solução: use Instant ou ZonedDateTime para dados globais.

O segundo erro — análise incorreta de strings. Por padrão, LocalDate.parse() espera o formato ISO-8601 (yyyy-MM-dd). Se a string estiver em outro formato, você precisa passar um DateTimeFormatter explicitamente. Você também deve tratar DateTimeParseException para que o aplicativo não falhe com entrada inválida.

O terceiro erro — ignorar a segurança de null. LocalDate, LocalTime e LocalDateTime são objetos que podem ser null. Em Kotlin, é recomendado usar tipos anuláveis com verificações explícitas ou o operador Elvis. Em Java — verifique null antes de chamar métodos.

O quarto erro — confundir LocalDateTime com ZonedDateTime. LocalDateTime não contém informações de fuso horário. Se você precisar passar um momento absoluto no tempo — use tipos zonais. Se a hora local for suficiente — use tipos locais.

Perguntas frequentes

Qual é a diferença entre LocalDate e Date em Java?

Date armazena o número de milissegundos desde 1970-01-01 UTC, enquanto LocalDate armazena o ano, mês e dia sem vinculação a fuso horário. Date é mutável e não thread-safe, LocalDate é imutável e thread-safe. Date está obsoleto desde Java 8.

LocalDateTime pode ser usado em um banco de dados?

Sim, LocalDateTime mapeia bem para o tipo SQL TIMESTAMP WITHOUT TIME ZONE. JPA e Room suportam através de TypeConverter. Para TIMESTAMP WITH TIME ZONE, use ZonedDateTime ou OffsetDateTime.

Como obter o número de dias entre duas datas?

Use ChronoUnit.DAYS.between(startDate, endDate). Este método retorna um long — a diferença em dias. Para um cálculo mais detalhado, use Period.between(), que retorna um Period com anos, meses e dias.

O que fazer se precisar preservar a precisão do tempo até milissegundos?

LocalTime suporta precisão de nanossegundos (9 casas decimais). Se a precisão de milissegundos for suficiente, use truncateTo(ChronoUnit.MILLIS) antes de salvar. Isso evita problemas de arredondamento durante a serialização.

Por que LocalDate.now() retorna datas diferentes em dispositivos diferentes?

O método now() usa o relógio do sistema e o fuso horário padrão do dispositivo. Se os dispositivos estiverem em fusos horários diferentes, a data pode diferir. Para um timestamp unificado, use Instant.now(), que sempre retorna a hora em UTC.

Resumo

  • LocalDate — uma classe imutável para data sem hora e fuso horário. Usado para armazenar aniversários, prazos, datas de eventos.
  • LocalTime — uma classe imutável para hora do dia com precisão de nanossegundos. Adequado para armazenar horários de abertura, durações de processos.
  • LocalDateTime — combinação de data e hora sem vinculação a fuso horário. O tipo local mais flexível, mas não adequado para sistemas distribuídos.
  • As três classes suportam aritmética, comparação, formatação e análise através de uma API unificada baseada em DateTimeFormatter.
  • No Android, java.time está disponível através de suporte integrado desde a API 26 ou através de desugaring para versões mais antigas.
  • Para timestamps globais e dados com fuso horário, use ZonedDateTime ou Instant em vez de tipos locais.
  • Ao analisar strings, sempre passe um DateTimeFormatter para formatos não padronizados e trate DateTimeParseException.

Vamos desenvolver um aplicativo móvel chave na mão

A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.

Discutir o projeto

Leia também