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.