Convertire java.util.Date in java.time.LocalDate


494

Qual è il modo migliore per convertire un java.util.Dateoggetto nel nuovo JDK 8 / JSR-310 java.time.LocalDate?

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

Risposte:


828

Risposta breve

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

Spiegazione

Nonostante il suo nome, java.util.Daterappresenta un istante sulla linea temporale, non una "data". I dati effettivi memorizzati nell'oggetto sono longconteggi di millisecondi dal 1970-01-01T00: 00Z (mezzanotte all'inizio del 1970 GMT / UTC).

La classe equivalente a java.util.Datein JSR-310 è Instant, quindi esiste un metodo conveniente toInstant()per fornire la conversione:

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

Un java.util.Dateesempio ha il concetto di fuso orario. Questo potrebbe sembrare strano se si chiama toString()a java.util.Date, perché toStringè relativo a un fuso orario. Tuttavia, questo metodo utilizza effettivamente il fuso orario predefinito di Java al volo per fornire la stringa. Il fuso orario non fa parte dello stato attuale di java.util.Date.

Una Instant, inoltre, non contiene alcuna informazione circa il fuso orario. Pertanto, per convertire da una Instantdata locale è necessario specificare un fuso orario. Questo potrebbe essere il fuso orario predefinito ZoneId.systemDefault()- o potrebbe essere un fuso orario controllato dall'applicazione, ad esempio un fuso orario dalle preferenze dell'utente. Utilizzare il atZone()metodo per applicare il fuso orario:

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

A ZonedDateTimecontiene lo stato costituito da data e ora locali, fuso orario e offset da GMT / UTC. Pertanto la data - LocalDate- può essere facilmente estratta usando toLocalDate():

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

Risposta Java 9

In Java SE 9, è stato aggiunto un nuovo metodo che semplifica leggermente questa attività:

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

Questa nuova alternativa è più diretta, creando meno rifiuti e quindi dovrebbe funzionare meglio.


14
Pensavo fosse LocalDate.from(Instant.ofEpochMilli(date.getTime()))equivalente al tuo, ma più diretto.
Marko Topolnik,

9
@MarkoTopolnik che compila ma non viene eseguito. Un Instant non contiene un fuso orario, quindi non è possibile ottenere LocalDate.
JodaStephen,

11
@assylias Basta usare sqlDate.toLocalDate ()!
JodaStephen,

25
@JodaStephen Datenon ha il concetto di fuso orario, Instantinoltre non contiene informazioni sul fuso orario. L' LocalDateAPI dice "Una data senza fuso orario". Allora perché convertire da Datein Instanta LocalDatebisogni atZone(ZoneId.systemDefault())?
Gustavo,

8
@Gustavo: LocalDatee LocalDateTimenon "memorizzare o rappresentare un orario o un fuso orario" (rif: javadocs). Sebbene non lo memorizzino, le classi rappresentano una Localdata e / o ora, quindi la conversione in data / ora locale implica un fuso orario.
Cuga,

158

Il modo migliore è:

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

Vantaggi di questa versione:

  • funziona indipendentemente dal fatto che l'input sia un'istanza java.util.Dateo una sottoclasse di java.sql.Date(diversamente dal modo di @ JodaStephen). Questo è comune con i dati originati da JDBC. java.sql.Date.toInstant()genera sempre un'eccezione.

  • è lo stesso per JDK8 e JDK7 con backport JSR-310

Personalmente utilizzo una classe di utilità (ma non è compatibile con il 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);
    }

}

Il asLocalDate()metodo qui è null-safe, utilizza toLocalDate(), se l'input è java.sql.Date(potrebbe essere sostituito dal driver JDBC per evitare problemi di fuso orario o calcoli non necessari), altrimenti utilizza il metodo sopra menzionato.


12
Se questo è il modo migliore, è molto brutto e dettagliato. Così doloroso.
Ceklock,

3
È meglio rispetto alla risposta accettata, spiego perché. Sì, è brutto, ma è per questo che ha scritto DateConvertUtils.
Oliv

Non capisco perché non abbiano implementato una classe di conversione nella nuova API.
Ceklock,

@ceklock, hanno fatto realizzare, non è una classe di conversione, ma un paio di metodi di conversione, come Date.toInstant().
Ole VV,

2
Esiste una libreria Kotlin che li confeziona come funzioni di estensione in modo che uno sviluppatore possa fare quanto segue. Data x = ...; x.asLocalDate ();
Mark Ashworth,

20
LocalDate localDate = LocalDate.parse( new SimpleDateFormat("yyyy-MM-dd").format(date) );

7
Qui, l' SimpleDateFormatistanza è limitata al thread corrente. È utilizzato in modo sicuro per i thread. Ora, SimpleDateFormatsi ritiene che sia "costoso da istanziare" (a causa di tutte le strutture di dati interne di cui ha bisogno) ma non puoi condividerne uno come "singleton" (senza sincronizzare l'accesso ad esso), perché in effetti non è thread- sicuro. (Una ThreadLocalsoluzione può funzionare se il codice "inquinante" Threadin questo è responsabile del ciclo di vita del thread ... ma ciò accade raramente). Awkward. Evitare SimpleDateFormatè il motivo dell'uso javax.time.
David Bullock,

6
Questo approccio ha molte spese generali: il "costoso" SimpleDateFormat(che viene gettato via), la stringa intermedia (che viene gettata via) e il costo dell'analisi. È una soluzione, ma non è raccomandato.
David Bullock,

2
@ceklock ma poi hai il problema che non è più sicuro per i thread
flup il

4
@ceklock SimpleDateFormat può elaborare solo una data alla volta, se la usi contemporaneamente, otterrai risultati alterati. Quindi sì, è importante. Non creare una singola istanza di SimpleDateFormat in una variabile globale.
flup

19

Se stai usando Java 8, la risposta di @ JodaStephen è ovviamente la migliore. Tuttavia, se stai lavorando con il backport JSR-310 , sfortunatamente devi fare qualcosa del genere:

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));

4
Non è vero, la risposta di @ JodaStephen funziona ancora. Hai solo bisogno di un altro modo per convertire java.util.Date in un Instant. Per questo puoi usare org.threeten.bp.DateTimeUtils.toInstant: threeten.org/threetenbp/apidocs/org/threeten/bp/…
Christian Ciach

3
DateTimeUtils non era disponibile quando stavo usando il backport, ma hai ragione che è disponibile per chiunque usi ThreeTen Backport 1.0 o successivo. Grazie per segnalarlo.
dhalsim2,

14
LocalDate ld = new java.sql.Date( new java.util.Date().getTime() ).toLocalDate();

1
E la realizzazione più semplice: LocalDate.of (getYear () + 1900, getMonth () + 1, getDate ())
Grigory Kislin

Le righe che dicono deprecate però: |
TheRealChx101

8

Puoi convertire in una riga:

public static LocalDate getLocalDateFromDate(Date date){
   return LocalDate.from(Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()));
}

Ottengo un errore come questo usando questo metodo: java.time.DateTimeException: impossibile ottenere LocalDate da TemporalAccessor: 2018-01-31T11: 54: 27.964Z di tipo java.time.Instant
Luigi Rubino

@LuigiRubino grazie per averlo segnalato. Si prega di consultare la risposta aggiornata. Ho dimenticato di aggiungere prima Zone.
Sahil Chhabra,

8

in primo luogo, è facile convertire una data in un istante

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

Quindi, puoi convertire l'istante in qualsiasi api data in jdk 8 usando il metodo ofInstant ():

LocalDateTime date = LocalDateTime.ofInstant(timestamp, ZoneId.systemDefault()); 

include localDate, localDateTime, localTime
Shedom Wei

Cosa significa specificare ZoneId qui. Se ricevo Date -> Instant dall'API e posso pensarci come millisecondi dal 1970 in UTC. Quando voglio ottenere solo LocalDate corrispondente (aaaa-mm-gg) dalla prospettiva di API non convertita in alcun fuso orario, non dovrei usare ZoneOffset.UTC per non avere questo offset istantaneo sul mio fuso orario locale. Il tuo esempio con ZoneId.systemDefault () non sta spostando la data dal server. Ex. L'istante rappresenta 20-10-2010 0:00 e poi spostato da UTC in America / Los_Angeles ottengo in Data locale 2018-10-19 anziché 20-10-2018?
Michał Ziobro,

Si rompe se ti capita di avere import java.sql.Datenel tuo file: il toInstant()metodo di java.sql.Datesempre getta.
Oliv

4
Date input = new Date();
LocalDateTime  conv=LocalDateTime.ofInstant(input.toInstant(), ZoneId.systemDefault());
LocalDate convDate=conv.toLocalDate();

L' Dateistanza contiene anche l'ora insieme alla data, mentre LocalDatenon lo è. Quindi puoi prima convertirlo in LocalDateTimeusando il suo metodo, ofInstant()quindi se lo desideri senza tempo, converti l'istanza in LocalDate.


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

questo formato proviene da 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();
    }

0

Ho avuto problemi con l'implementazione di @ JodaStephen su JBoss EAP 6. Quindi, ho riscritto la conversione seguendo il tutorial Java di Oracle in 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();

-2

Cosa c'è di sbagliato in questa 1 semplice riga?

new LocalDateTime(new Date().getTime()).toLocalDate();

Java si lamenta di non poter invocare toLocalDate () sul tipo primitivo long. Ho usato un vero oggetto Date; forse c'è il problema ??
TheGeeko61

-8

Ho risolto questa domanda con la soluzione di seguito

  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);

In questo caso localDate stampa la tua data in questo formato "aaaa-MM-gg"


1
La domanda è cercare una soluzione utilizzando le java.timeclassi in JDK8, non con Joda Time.
pioto,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.