Data e hora em Java: os tipos LocalDate, LocalDateTime e Instant

Foto do autor Nelio Alves
Nelio Alves
Compartilhar
Compartilhar no LinkedInCompartilhar no FacebookCompartilhar no XCompartilhar no WhatsApp
Imagem banner do post

Trabalhar com data e hora em Java parece simples até você precisar lidar com fuso horário, horários de verão, integração com APIs e formatos padronizados. Neste post, você vai organizar de vez o seu “mapa mental” sobre datas: como o Java representa tempo, quando usar LocalDate, LocalDateTime e Instant, e como o padrão ISO 8601 conecta tudo isso de forma consistente.

O “território” de data e hora em Java

Hoje, a forma recomendada de trabalhar com datas em Java é a API java.time (introduzida no Java 8), que substitui a antiga API java.util.Date / Calendar.

Ao falar de data e hora, você sempre está lidando com alguma combinação destes conceitos:

  • Data (ano/mês/dia)
  • Hora (hora/minuto/segundo/nanosegundo)
  • Fuso horário (offset e regras de um timezone)
  • Instante absoluto (um ponto exato na linha do tempo)
  • Formato de texto (como representar data/hora em strings)

A grande sacada é: existem tipos diferentes porque existem necessidades diferentes.

Linha do tempo, fuso horário e “tempo civil”

Tempo absoluto: o que é um instante?

Um instante é um ponto único e absoluto na linha do tempo, independente de qualquer fuso horário. É o tipo de coisa que você usa para registrar eventos:

  • “o pagamento foi confirmado às 13:45:02Z”
  • “o log foi gerado nesse exato momento”

Em Java, esse conceito é representado pelo tipo Instant.

Tempo civil: data e hora que as pessoas usam

Quando você marca um compromisso, você pensa em algo como:

  • “dia 10/03 às 14:00”

Isso é tempo civil: uma data e/ou hora que depende do contexto (do fuso local). Nesse cenário, existem tipos como:

  • LocalDate (somente data)
  • LocalDateTime (data + hora, sem fuso)

Timezones: offset vs zona

Existem dois jeitos comuns de falar de fuso:

  • Offset: diferença fixa em relação ao UTC, como -03:00
  • Zona (timezone): um conjunto de regras, como America/Sao_Paulo

A zona pode mudar ao longo do ano (horário de verão) ou por decisões políticas. Já o offset é “apenas um número” naquele instante.

Em Java, você lida com isso através de classes como:

  • ZoneId (zona)
  • ZoneOffset (offset)
  • ZonedDateTime (data/hora com zona)
  • OffsetDateTime (data/hora com offset)

Mesmo que o foco deste post sejam LocalDate, LocalDateTime e Instant, você precisa entender timezone para saber quando não usar LocalDateTime sozinho.

ISO 8601: o padrão que você vai ver em todo lugar

O ISO 8601 é o padrão mais comum para representar datas e horas como texto em sistemas.

Data

Formato básico:

  • YYYY-MM-DD

Exemplos:

  • 2026-02-25
  • 1991-07-13

Em Java, LocalDate usa esse formato como padrão quando você faz toString().

Copiar
import java.time.LocalDate;

public class Main {
    public static void main(String[] args) {
        LocalDate d = LocalDate.of(2026, 2, 25);
        System.out.println(d); // 2026-02-25
    }
}

Data e hora

Formato básico:

  • YYYY-MM-DDTHH:MM:SS

Exemplos:

  • 2026-02-25T09:30:00
  • 2026-02-25T09:30:00.123

Em Java, LocalDateTime também segue isso no toString().

Copiar
import java.time.LocalDateTime;

public class Main {
    public static void main(String[] args) {
        LocalDateTime dt = LocalDateTime.of(2026, 2, 25, 9, 30, 0);
        System.out.println(dt); // 2026-02-25T09:30
    }
}

Observe que o toString() pode omitir segundos/nanos se forem zero. Isso é normal.

Data e hora com offset

Quando você inclui o offset, você está dizendo como aquele horário se relaciona com o UTC.

Exemplos:

  • 2026-02-25T09:30:00-03:00
  • 2026-02-25T12:30:00Z (Z significa UTC)

Esse formato é típico em APIs.

Instante (UTC) com Z

Quando você vê um timestamp como:

  • 2026-02-25T12:30:00Z

Isso é um instante em UTC. Em Java, isso se conecta naturalmente com Instant.

Copiar
import java.time.Instant;

public class Main {
    public static void main(String[] args) {
        Instant inst = Instant.parse("2026-02-25T12:30:00Z");
        System.out.println(inst); // 2026-02-25T12:30:00Z
    }
}

LocalDate: somente data (sem hora, sem fuso)

LocalDate representa uma data do calendário, como “2026-02-25”.

Quando usar

Use LocalDate quando a hora não importa:

  • data de nascimento
  • validade de documento
  • data de vencimento (quando a regra é por dia)
  • feriados

Como instanciar

Copiar
import java.time.LocalDate;

public class Main {
    public static void main(String[] args) {

        // 1) Data específica
        LocalDate d1 = LocalDate.of(2026, 2, 25);

        // 2) Data atual (do relógio do sistema)
        LocalDate d2 = LocalDate.now();

        // 3) Parse de ISO 8601
        LocalDate d3 = LocalDate.parse("2026-02-25");

        System.out.println(d1);
        System.out.println(d2);
        System.out.println(d3);
    }
}

Operações comuns

Copiar
import java.time.LocalDate;

public class Main {
    public static void main(String[] args) {
        LocalDate d = LocalDate.parse("2026-02-25");

        LocalDate plusDays = d.plusDays(7);
        LocalDate minusMonths = d.minusMonths(1);
        LocalDate nextYear = d.plusYears(1);

        System.out.println(plusDays);    // 2026-03-04
        System.out.println(minusMonths); // 2026-01-25
        System.out.println(nextYear);    // 2027-02-25
    }
}

Comparação

Copiar
import java.time.LocalDate;

public class Main {
    public static void main(String[] args) {
        LocalDate a = LocalDate.parse("2026-02-25");
        LocalDate b = LocalDate.parse("2026-03-01");

        System.out.println(a.isBefore(b)); // true
        System.out.println(a.isAfter(b));  // false
        System.out.println(a.equals(b));   // false
    }
}

LocalDateTime: data e hora, mas sem fuso

LocalDateTime representa data e hora sem qualquer informação de fuso horário.

Isso é exatamente o que causa confusão: ele não “sabe” se aquele horário é Brasil, Japão ou UTC.

Quando usar

Use LocalDateTime quando você está modelando um horário local que faz sentido “por si só”, como:

  • agendamento em horário local de uma empresa (com timezone conhecido externamente)
  • data/hora exibida ao usuário no fuso dele
  • regras de negócio que acontecem “às 08:00” (no contexto de uma zona)

Mas evite usar LocalDateTime como timestamp de log ou evento global, porque ele não é um instante absoluto.

Como instanciar

Copiar
import java.time.LocalDateTime;

public class Main {
    public static void main(String[] args) {

        // 1) Data/hora específica
        LocalDateTime dt1 = LocalDateTime.of(2026, 2, 25, 9, 30);

        // 2) Agora (relógio do sistema)
        LocalDateTime dt2 = LocalDateTime.now();

        // 3) Parse ISO 8601 (sem offset)
        LocalDateTime dt3 = LocalDateTime.parse("2026-02-25T09:30:00");

        System.out.println(dt1);
        System.out.println(dt2);
        System.out.println(dt3);
    }
}

Operações comuns

Copiar
import java.time.LocalDateTime;

public class Main {
    public static void main(String[] args) {
        LocalDateTime dt = LocalDateTime.parse("2026-02-25T09:30:00");

        System.out.println(dt.plusHours(2));
        System.out.println(dt.plusDays(1));
        System.out.println(dt.minusMinutes(45));
    }
}

Convertendo LocalDateTime para um instante: você precisa de timezone

Para transformar um LocalDateTime em um Instant, você precisa saber em qual zona aquele horário deve ser interpretado.

Copiar
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;

public class Main {
    public static void main(String[] args) {
        LocalDateTime dt = LocalDateTime.parse("2026-02-25T09:30:00");

        ZoneId zone = ZoneId.of("America/Sao_Paulo");
        Instant inst = dt.atZone(zone).toInstant();

        System.out.println(inst);
    }
}

Perceba o ponto: sem ZoneId, não existe conversão correta, porque LocalDateTime não é absoluto.

Instant: um ponto exato na linha do tempo

Instant representa um instante em UTC (com precisão de nanos), ideal para:

  • timestamps de logs
  • eventos globais
  • persistência de data/hora “universal”
  • integrações com APIs

Como instanciar

Copiar
import java.time.Instant;

public class Main {
    public static void main(String[] args) {

        // 1) Agora
        Instant now = Instant.now();

        // 2) Parse ISO 8601 com Z
        Instant inst1 = Instant.parse("2026-02-25T12:30:00Z");

        // 3) Instante a partir de epoch
        Instant inst2 = Instant.ofEpochMilli(0);

        System.out.println(now);
        System.out.println(inst1);
        System.out.println(inst2);
    }
}

Epoch time (a base de tudo)

Por baixo dos panos, um Instant é essencialmente:

  • quantidade de segundos e nanos desde 1970-01-01T00:00:00Z

Esse ponto é chamado de Unix Epoch.

Copiar
import java.time.Instant;

public class Main {
    public static void main(String[] args) {
        Instant inst = Instant.now();

        long epochSeconds = inst.getEpochSecond();
        int nano = inst.getNano();

        System.out.println(epochSeconds);
        System.out.println(nano);
    }
}

Exibindo um Instant no horário local

Como Instant é UTC, para mostrar isso ao usuário você converte para uma zona.

Copiar
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class Main {
    public static void main(String[] args) {
        Instant inst = Instant.parse("2026-02-25T12:30:00Z");

        ZonedDateTime sp = inst.atZone(ZoneId.of("America/Sao_Paulo"));
        ZonedDateTime tokyo = inst.atZone(ZoneId.of("Asia/Tokyo"));

        System.out.println(sp);
        System.out.println(tokyo);
    }
}

O instante é o mesmo, mas a “leitura” muda conforme o fuso.

Boas práticas: qual tipo usar?

Algumas regras práticas ajudam muito:

  • Só data? Use LocalDate.
  • Data e hora “local”, sem precisar ser global? Use LocalDateTime.
  • Timestamp global, logs, eventos, integração? Use Instant.

Uma forma de pensar:

  • LocalDate e LocalDateTime modelam tempo civil.
  • Instant modela tempo absoluto.

Formatação e parsing além do padrão ISO

Até aqui usamos parse() com ISO 8601. Mas, em aplicações reais, você precisa formatar e ler datas em padrões específicos.

Em Java, isso é feito com DateTimeFormatter.

Copiar
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) {
        LocalDate d = LocalDate.parse("2026-02-25");

        DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/yyyy");
        String text = d.format(fmt);

        System.out.println(text); // 25/02/2026

        // Parse no formato dd/MM/yyyy
        LocalDate d2 = LocalDate.parse("01/03/2026", fmt);
        System.out.println(d2); // 2026-03-01
    }
}

Clareza de representação evita bugs e confusão

Datas são difíceis porque misturam percepção humana (tempo civil) com realidade técnica (instantes absolutos). Quando você escolhe o tipo certo (LocalDate, LocalDateTime, Instant) e respeita timezone e ISO 8601, você ganha:

  • clareza no modelo
  • menos ambiguidade
  • integração mais simples com APIs
  • persistência mais confiável
Foto do autor Nelio Alves
Nelio Alves
Desenvolvedor e Professor
Olá, meu nome é Nelio Alves. Sou graduado em Ciência da Computação e possuo mestrado e doutorado em Engenharia de Software pela Universidade Federal de Uberlândia. Trabalho como desenvolvedor e professor de programação há mais de 20 anos, e sou um dos educadores de tecnologia mais influentes da Internet com mais de 500 mil alunos online.