Converta java.util.Date em java.time.LocalDate

494

Qual é a melhor maneira de converter um java.util.Dateobjeto no novo JDK 8 / JSR-310 java.time.LocalDate?

Date input = new Date();
LocalDate date = ???
JodaStephen
fonte

Respostas:

828

Resposta curta

Date input = new Date();
LocalDate date = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

Explicação

Apesar do nome, java.util.Daterepresenta um instante na linha do tempo, não uma "data". Os dados reais armazenados no objeto são uma longcontagem de milissegundos desde 01-01-2009 - 00: 00Z (meia-noite no início de 1970 GMT / UTC).

A classe equivalente a java.util.Dateno JSR-310 é Instant, portanto, existe um método conveniente toInstant()para fornecer a conversão:

Date input = new Date();
Instant instant = input.toInstant();

Uma java.util.Dateinstância não tem conceito de fuso horário. Isso pode parecer estranho se você chamar toString()um java.util.Date, porque toStringé relativo a um fuso horário. No entanto, esse método realmente usa o fuso horário padrão de Java rapidamente para fornecer a string. O fuso horário não faz parte do estado real de java.util.Date.

Um Instanttambém não contém nenhuma informação sobre o fuso horário. Assim, para converter de uma Instantdata para uma local, é necessário especificar um fuso horário. Esse pode ser o fuso horário padrão - ZoneId.systemDefault()- ou pode ser um fuso horário que seu aplicativo controla, como um fuso horário nas preferências do usuário. Use o atZone()método para aplicar o fuso horário:

Date input = new Date();
Instant instant = input.toInstant();
ZonedDateTime zdt = instant.atZone(ZoneId.systemDefault());

A ZonedDateTimecontém o estado que consiste na data e hora locais, no fuso horário e no deslocamento de GMT / UTC. Como tal, a data - LocalDate- pode ser facilmente extraída usando toLocalDate():

Date input = new Date();
Instant instant = input.toInstant();
ZonedDateTime zdt = instant.atZone(ZoneId.systemDefault());
LocalDate date = zdt.toLocalDate();

Resposta Java 9

No Java SE 9, foi adicionado um novo método que simplifica levemente esta tarefa:

Date input = new Date();
LocalDate date = LocalDate.ofInstant(input.toInstant(), ZoneId.systemDefault());

Essa nova alternativa é mais direta, criando menos lixo e, portanto, deve ter um desempenho melhor.

JodaStephen
fonte
14
Eu LocalDate.from(Instant.ofEpochMilli(date.getTime()))acho que é equivalente ao seu, mas mais direto.
Marko Topolnik
9
@ MarkoTopolnik que compila, mas não executa. Um Instant não contém um fuso horário, portanto não há como obter o LocalDate.
JodaStephen
11
@assylias Basta usar sqlDate.toLocalDate ()!
JodaStephen
25
@JodaStephen Datenão tem conceito de fuso horário, Instanttambém não contém informações sobre fuso horário. A LocalDateAPI diz "Uma data sem fuso horário". Então, por que converter de Datepara Instantem LocalDatenecessidades atZone(ZoneId.systemDefault())?
22315 Gustavo
8
@Gustavo: LocalDatee LocalDateTimenão "armazena ou representa uma hora ou um fuso horário" (ref: javadocs). Enquanto eles não o armazenam - as classes representam uma Localdata e / ou hora, portanto, a conversão para data / hora local implica um fuso horário.
Cuga
158

Melhor maneira é:

Date date = ...;
Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate()

Vantagens desta versão:

  • funciona independentemente da entrada ser uma instância java.util.Dateou subclasse de java.sql.Date(diferente da maneira de @ JodaStephen). Isso é comum com dados originados por JDBC. java.sql.Date.toInstant()sempre lança uma exceção.

  • é o mesmo para o JDK8 e o JDK7 com backport JSR-310

Eu pessoalmente uso uma classe de utilitário (mas não é compatível com backport):

/**
 * Utilities for conversion between the old and new JDK date types 
 * (between {@code java.util.Date} and {@code java.time.*}).
 * 
 * <p>
 * All methods are null-safe.
 */
public class DateConvertUtils {

    /**
     * Calls {@link #asLocalDate(Date, ZoneId)} with the system default time zone.
     */
    public static LocalDate asLocalDate(java.util.Date date) {
        return asLocalDate(date, ZoneId.systemDefault());
    }

    /**
     * Creates {@link LocalDate} from {@code java.util.Date} or it's subclasses. Null-safe.
     */
    public static LocalDate asLocalDate(java.util.Date date, ZoneId zone) {
        if (date == null)
            return null;

        if (date instanceof java.sql.Date)
            return ((java.sql.Date) date).toLocalDate();
        else
            return Instant.ofEpochMilli(date.getTime()).atZone(zone).toLocalDate();
    }

    /**
     * Calls {@link #asLocalDateTime(Date, ZoneId)} with the system default time zone.
     */
    public static LocalDateTime asLocalDateTime(java.util.Date date) {
        return asLocalDateTime(date, ZoneId.systemDefault());
    }

    /**
     * Creates {@link LocalDateTime} from {@code java.util.Date} or it's subclasses. Null-safe.
     */
    public static LocalDateTime asLocalDateTime(java.util.Date date, ZoneId zone) {
        if (date == null)
            return null;

        if (date instanceof java.sql.Timestamp)
            return ((java.sql.Timestamp) date).toLocalDateTime();
        else
            return Instant.ofEpochMilli(date.getTime()).atZone(zone).toLocalDateTime();
    }

    /**
     * Calls {@link #asUtilDate(Object, ZoneId)} with the system default time zone.
     */
    public static java.util.Date asUtilDate(Object date) {
        return asUtilDate(date, ZoneId.systemDefault());
    }

    /**
     * Creates a {@link java.util.Date} from various date objects. Is null-safe. Currently supports:<ul>
     * <li>{@link java.util.Date}
     * <li>{@link java.sql.Date}
     * <li>{@link java.sql.Timestamp}
     * <li>{@link java.time.LocalDate}
     * <li>{@link java.time.LocalDateTime}
     * <li>{@link java.time.ZonedDateTime}
     * <li>{@link java.time.Instant}
     * </ul>
     * 
     * @param zone Time zone, used only if the input object is LocalDate or LocalDateTime.
     * 
     * @return {@link java.util.Date} (exactly this class, not a subclass, such as java.sql.Date)
     */
    public static java.util.Date asUtilDate(Object date, ZoneId zone) {
        if (date == null)
            return null;

        if (date instanceof java.sql.Date || date instanceof java.sql.Timestamp)
            return new java.util.Date(((java.util.Date) date).getTime());
        if (date instanceof java.util.Date)
            return (java.util.Date) date;
        if (date instanceof LocalDate)
            return java.util.Date.from(((LocalDate) date).atStartOfDay(zone).toInstant());
        if (date instanceof LocalDateTime)
            return java.util.Date.from(((LocalDateTime) date).atZone(zone).toInstant());
        if (date instanceof ZonedDateTime)
            return java.util.Date.from(((ZonedDateTime) date).toInstant());
        if (date instanceof Instant)
            return java.util.Date.from((Instant) date);

        throw new UnsupportedOperationException("Don't know hot to convert " + date.getClass().getName() + " to java.util.Date");
    }

    /**
     * Creates an {@link Instant} from {@code java.util.Date} or it's subclasses. Null-safe.
     */
    public static Instant asInstant(Date date) {
        if (date == null)
            return null;
        else
            return Instant.ofEpochMilli(date.getTime());
    }

    /**
     * Calls {@link #asZonedDateTime(Date, ZoneId)} with the system default time zone.
     */
    public static ZonedDateTime asZonedDateTime(Date date) {
        return asZonedDateTime(date, ZoneId.systemDefault());
    }

    /**
     * Creates {@link ZonedDateTime} from {@code java.util.Date} or it's subclasses. Null-safe.
     */
    public static ZonedDateTime asZonedDateTime(Date date, ZoneId zone) {
        if (date == null)
            return null;
        else
            return asInstant(date).atZone(zone);
    }

}

O asLocalDate()método aqui é seguro para nulos, usa toLocalDate(), se a entrada for java.sql.Date(pode ser substituída pelo driver JDBC para evitar problemas de fuso horário ou cálculos desnecessários), caso contrário, usa o método mencionado acima.

Oliv
fonte
12
Se esse é o melhor caminho, é muito feio e detalhado. Tão doloroso.
ceklock
3
É melhor em comparação com a resposta aceita, eu explico o porquê. Sim, é feio, mas é por isso que escreveu DateConvertUtils.
Oliv
Não entendo por que eles não implementaram uma classe de conversão na nova API.
precisa saber é
@ceklock, eles se implementar, não uma classe de conversão, mas um par de métodos de conversão, como Date.toInstant().
Ole VV
2
Existe uma biblioteca Kotlin que os empacota como funções de extensão, para que um desenvolvedor possa fazer o seguinte. Data x = ...; x.asLocalDate ();
Mark-Ashworth-
20
LocalDate localDate = LocalDate.parse( new SimpleDateFormat("yyyy-MM-dd").format(date) );
ceklock
fonte
7
Aqui, a SimpleDateFormatinstância está confinada ao encadeamento atual. É usado de maneira segura para threads. Agora, SimpleDateFormattem a fama de ser 'caro para instanciar' (por conta de todas as estruturas de dados internas de que necessita), mas você não pode compartilhar um como um 'solteirão' (sem sincronizar o acesso a ele), porque ele na verdade não é thread- seguro. (Uma ThreadLocalsolução pode funcionar se o código 'poluente') Threadfor responsável pelo ciclo de vida do encadeamento ... mas isso raramente acontece). Desajeitado. Evitar SimpleDateFormaté o motivo do uso javax.time.
David Bullock
6
Essa abordagem tem muita sobrecarga: a 'cara' SimpleDateFormat(que é jogada fora), a cadeia intermediária (que é jogada fora) e o custo da análise. É uma solução, mas não é recomendado.
David Bullock
2
@ceklock mas depois chegar o problema que ele não é mais thread-safe
FLUP
4
@ceklock, o SimpleDateFormat pode processar apenas uma data de cada vez; se você usá-lo simultaneamente, ele produzirá resultados confusos. Então, sim, é importante. Não crie uma única instância de SimpleDateFormat em uma variável global.
Flup 20/16
19

Se você estiver usando o Java 8, a resposta do @ JodaStephen é obviamente a melhor. No entanto, se você estiver trabalhando com o backport JSR-310 , infelizmente precisará fazer algo assim:

Date input = new Date();
Calendar cal = Calendar.getInstance();
cal.setTime(input);
LocalDate date = LocalDate.of(cal.get(Calendar.YEAR),
        cal.get(Calendar.MONTH) + 1,
        cal.get(Calendar.DAY_OF_MONTH));
dhalsim2
fonte
4
Não é verdade, a resposta de @ JodaStephen ainda funciona. Você só precisa de outra maneira de converter java.util.Date em um instante. Para isso, você pode usar org.threeten.bp.DateTimeUtils.toInstant: threeten.org/threetenbp/apidocs/org/threeten/bp/...
Christian Ciach
3
O DateTimeUtils não estava disponível quando eu estava usando o backport, mas você está certo de que ele está disponível para qualquer pessoa que use o ThreeTen Backport 1.0 ou posterior. Obrigado por apontar isso.
dhalsim2
14
LocalDate ld = new java.sql.Date( new java.util.Date().getTime() ).toLocalDate();
Gustavo
fonte
1
E realização mais simples: LocalDate.of (getYear () + 1900, getMonth () + 1, getDate ())
Grigory Kislin
As linhas que dizem obsoletas: |
TheRealChx101
8

Você pode converter em uma linha:

public static LocalDate getLocalDateFromDate(Date date){
   return LocalDate.from(Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()));
}
Sahil Chhabra
fonte
Eu recebo um erro como este usando este método: java.time.DateTimeException: Não é possível obter o LocalDate do TemporalAccessor: 2018-01-31T11: 54: 27.964Z do tipo java.time.Instant
Luigi Rubino
@LuigiRubino obrigado por apontar. Por favor, veja a resposta atualizada. Esqueci de adicionar o Zone anteriormente.
Sahil Chhabra 31/01
8

primeiro, é fácil converter uma data em um instante

Instant timestamp = new Date().toInstant(); 

Em seguida, você pode converter a API do Instant para qualquer data no jdk 8 usando o método ofInstant ():

LocalDateTime date = LocalDateTime.ofInstant(timestamp, ZoneId.systemDefault()); 
Shedom Wei
fonte
inclui localDate, localDateTime, localTime
Shedom Wei
O que significa especificar aqui o ZoneId. Se eu receber Date -> Instant da API e posso pensar em milissegundos de 1970 no UTC. Quando eu quero obter apenas o LocalDate correspondente (aaaa-mm-dd) da perspectiva da API não convertida em nenhum fuso horário, não devo usar o ZoneOffset.UTC para não ter esse deslocamento instantâneo no fuso horário local. Não é o seu exemplo com o ZoneId.systemDefault () que está mudando o servidor de formulários de data. Ex. instant representa 2018-10-20 0:00 e depois mudou do UTC para America / Los_Angeles que eu recebo na data local 2018-10-19 em vez de 2018-10-20?
Michał Ziobro 16/11
Ele quebra se você tiver import java.sql.Dateem seu arquivo: o toInstant()método de java.sql.Datesempre lança.
23419 Oliv
4
Date input = new Date();
LocalDateTime  conv=LocalDateTime.ofInstant(input.toInstant(), ZoneId.systemDefault());
LocalDate convDate=conv.toLocalDate();

A Dateinstância contém tempo também, juntamente com a data, enquanto LocalDatenão. Portanto, você pode convertê-lo primeiro em LocalDateTimeusar seu método ofInstant()e, se desejar, sem tempo, em seguida, converter a instância em LocalDate.

Arun Raaj
fonte
1
public static LocalDate Date2LocalDate(Date date) {
        return LocalDate.parse(date.toString(), DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy"))

esse formato é de Date#tostring

    public String toString() {
        // "EEE MMM dd HH:mm:ss zzz yyyy";
        BaseCalendar.Date date = normalize();
        StringBuilder sb = new StringBuilder(28);
        int index = date.getDayOfWeek();
        if (index == BaseCalendar.SUNDAY) {
            index = 8;
        }
        convertToAbbr(sb, wtb[index]).append(' ');                        // EEE
        convertToAbbr(sb, wtb[date.getMonth() - 1 + 2 + 7]).append(' ');  // MMM
        CalendarUtils.sprintf0d(sb, date.getDayOfMonth(), 2).append(' '); // dd

        CalendarUtils.sprintf0d(sb, date.getHours(), 2).append(':');   // HH
        CalendarUtils.sprintf0d(sb, date.getMinutes(), 2).append(':'); // mm
        CalendarUtils.sprintf0d(sb, date.getSeconds(), 2).append(' '); // ss
        TimeZone zi = date.getZone();
        if (zi != null) {
            sb.append(zi.getDisplayName(date.isDaylightTime(), TimeZone.SHORT, Locale.US)); // zzz
        } else {
            sb.append("GMT");
        }
        sb.append(' ').append(date.getYear());  // yyyy
        return sb.toString();
    }
宏杰 李
fonte
0

Eu tive problemas com a implementação do @ JodaStephen no JBoss EAP 6. Portanto, reescrevi a conversão seguindo o Tutorial Java do Oracle em http://docs.oracle.com/javase/tutorial/datetime/iso/legacy.html .

    Date input = new Date();
    GregorianCalendar gregorianCalendar = (GregorianCalendar) Calendar.getInstance();
    gregorianCalendar.setTime(input);
    ZonedDateTime zonedDateTime = gregorianCalendar.toZonedDateTime();
    zonedDateTime.toLocalDate();
rogerio_gentil
fonte
-2

O que há de errado com esta 1 linha simples?

new LocalDateTime(new Date().getTime()).toLocalDate();
Lawrence
fonte
Java reclama que não pode chamar toLocalDate () no tipo primitivo por muito tempo. Eu usei um objeto Date real; talvez haja o atrito ??
TheGeeko61
-8

Resolvi esta questão com a solução abaixo

  import org.joda.time.LocalDate;
  Date myDate = new Date();
  LocalDate localDate = LocalDate.fromDateFields(myDate);
  System.out.println("My date using Date" Nov 18 11:23:33 BRST 2016);
  System.out.println("My date using joda.time LocalTime" 2016-11-18);

Nesse caso, localDate imprima sua data neste formato "aaaa-MM-dd"

estevamdf
fonte
1
A questão está procurando uma solução usando as java.timeclasses no JDK8, não no Joda Time.
pioto 21/12/16