Konvertieren Sie java.util.Date in java.time.LocalDate

494

Was ist der beste Weg, um ein java.util.DateObjekt in das neue JDK 8 / JSR-310 zu konvertieren java.time.LocalDate?

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

Antworten:

828

Kurze Antwort

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

Erläuterung

Stellt trotz seines Namens java.util.Dateeinen Moment auf der Zeitachse dar, kein "Datum". Die tatsächlich im Objekt gespeicherten Daten sind longMillisekunden seit 1970-01-01T00: 00Z (Mitternacht zu Beginn von 1970 GMT / UTC).

Die äquivalente Klasse zu java.util.Datein JSR-310 ist Instant, daher gibt es eine bequeme Methode toInstant(), um die Konvertierung bereitzustellen:

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

Eine java.util.DateInstanz hat kein Konzept der Zeitzone. Dies mag seltsam erscheinen , wenn Sie rufen toString()auf ein java.util.Date, weil die toStringrelativ zu einer Zeitzone ist. Diese Methode verwendet jedoch tatsächlich die Standardzeitzone von Java im laufenden Betrieb, um die Zeichenfolge bereitzustellen. Die Zeitzone ist nicht Teil des aktuellen Zustands von java.util.Date.

Ein Instantenthält auch keine Informationen über die Zeitzone. Um von einem Instantin ein lokales Datum zu konvertieren, muss daher eine Zeitzone angegeben werden. Dies kann die Standardzone sein - ZoneId.systemDefault()- oder eine Zeitzone, die Ihre Anwendung steuert, z. B. eine Zeitzone aus den Benutzereinstellungen. Verwenden Sie die atZone()Methode, um die Zeitzone anzuwenden:

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

A ZonedDateTimeenthält einen Status, der aus dem lokalen Datum und der Uhrzeit, der Zeitzone und dem Versatz von GMT / UTC besteht. Als solches kann das Datum - LocalDate- leicht extrahiert werden mit toLocalDate():

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

Java 9 Antwort

In Java SE 9 wurde eine neue Methode hinzugefügt, die diese Aufgabe leicht vereinfacht:

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

Diese neue Alternative ist direkter, verursacht weniger Müll und sollte daher eine bessere Leistung erbringen.

JodaStephen
quelle
14
Ich hatte LocalDate.from(Instant.ofEpochMilli(date.getTime()))ich denke, es ist gleichbedeutend mit deinem, aber direkter.
Marko Topolnik
9
@ MarkoTopolnik, der kompiliert, aber nicht ausgeführt wird. Ein Instant enthält keine Zeitzone, daher gibt es keine Möglichkeit, das LocalDate abzurufen.
JodaStephen
11
@assylias Verwenden Sie einfach sqlDate.toLocalDate ()!
JodaStephen
25
@JodaStephen Datehat kein Konzept der Zeitzone und enthält Instantauch keine Informationen zur Zeitzone. Die LocalDateAPI sagt "Ein Datum ohne Zeitzone". Warum dann von Datezu Instantzu LocalDateBedarf konvertieren atZone(ZoneId.systemDefault())?
Gustavo
8
@Gustavo: LocalDateund " LocalDateTimekeine Zeit oder Zeitzone speichern oder darstellen" (ref: javadocs). Während sie es nicht speichern, stellen die Klassen ein LocalDatum und / oder eine Uhrzeit dar, daher impliziert die Konvertierung in lokales Datum / Uhrzeit eine Zeitzone.
Cuga
158

Besser ist:

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

Vorteile dieser Version:

  • funktioniert unabhängig java.util.Datedavon, java.sql.Dateob die Eingabe eine Instanz oder eine Unterklasse von ist (im Gegensatz zu @ JodaStephen). Dies ist bei JDBC-Daten üblich. java.sql.Date.toInstant()löst immer eine Ausnahme aus.

  • Dies gilt auch für JDK8 und JDK7 mit JSR-310-Backport

Ich persönlich verwende eine Utility-Klasse (diese ist jedoch nicht Backport-kompatibel):

/**
 * 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);
    }

}

Die asLocalDate()hier verwendete Methode ist nullsicher und wird verwendet toLocalDate(), wenn die Eingabe erfolgt java.sql.Date(sie kann vom JDBC-Treiber überschrieben werden, um Zeitzonenprobleme oder unnötige Berechnungen zu vermeiden). Andernfalls wird die oben genannte Methode verwendet.

Oliv
quelle
12
Wenn dies der bessere Weg ist, ist es sehr hässlich und ausführlich. So schmerzhaft.
ceklock
3
Es ist besser im Vergleich zu der akzeptierten Antwort, ich erkläre warum. Ja, es ist hässlich, aber deshalb geschrieben DateConvertUtils.
Oliv
Ich verstehe nicht, warum sie keine Konvertierungsklasse in der neuen API implementiert haben.
ceklock
@ceklock, sie haben keine Konvertierungsklasse implementiert, sondern ein paar Konvertierungsmethoden, wie z Date.toInstant().
Ole VV
2
Gibt es eine Kotlin-Bibliothek, die diese als Erweiterungsfunktionen verpackt, damit ein Entwickler Folgendes tun kann? Datum x = ...; x.asLocalDate ();
Mark Ashworth
20
LocalDate localDate = LocalDate.parse( new SimpleDateFormat("yyyy-MM-dd").format(date) );
ceklock
quelle
7
Hier ist die SimpleDateFormatInstanz auf den aktuellen Thread beschränkt. Es wird verwendet in einem Thread-sicher. Nun SimpleDateFormatist angeblich zu sein ‚teuer instantiate‘ (wegen aller internen Datenstrukturen es braucht) , aber man kann nicht teilen ein als ‚Singleton‘ (ohne Zugang zu ihm zu synchronisieren), weil es in der Tat nicht Gewinde- ist sicher. (Eine ThreadLocalLösung kann funktionieren, wenn der Code, der den darin enthaltenen Code verschmutzt, Threadfür den Lebenszyklus des Threads verantwortlich ist ... aber das kommt selten vor). Peinlich. Vermeiden SimpleDateFormatist der Grund für die Verwendung javax.time.
David Bullock
6
Dieser Ansatz hat viel Aufwand: die "teure" SimpleDateFormat(die weggeworfen wird), die Zwischenzeichenfolge (die weggeworfen wird) und die Kosten für das Parsen. Es ist eine Lösung, aber nicht zu empfehlen.
David Bullock
2
@ceklock aber dann bekommst du das Problem, dass es nicht mehr threadsicher ist
flup
4
@ceklock Das SimpleDateFormat kann jeweils nur ein Datum verarbeiten. Wenn Sie es gleichzeitig verwenden, werden fehlerhafte Ergebnisse erzielt. Also ja, es ist wichtig. Erstellen Sie keine einzelne Instanz von SimpleDateFormat in einer globalen Variablen.
Flup
19

Wenn Sie Java 8 verwenden, ist die Antwort von @ JodaStephen offensichtlich die beste. Wenn Sie jedoch mit dem JSR-310-Backport arbeiten , müssen Sie leider Folgendes tun:

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
quelle
4
Nicht wahr, die Antwort von @ JodaStephen funktioniert immer noch. Sie brauchen nur eine andere Möglichkeit, um java.util.Date in einen Instant zu konvertieren. Hierfür können Sie org.threeten.bp.DateTimeUtils.toInstant verwenden: threeten.org/threetenbp/apidocs/org/threeten/bp/…
Christian Ciach
3
DateTimeUtils war nicht verfügbar, als ich den Backport verwendet habe, aber Sie haben Recht, dass er für alle verfügbar ist, die ThreeTen Backport 1.0 oder höher verwenden. Vielen Dank für den Hinweis.
Dhalsim2
14
LocalDate ld = new java.sql.Date( new java.util.Date().getTime() ).toLocalDate();
Gustavo
quelle
1
Und es ist die einfachste Realisierung: LocalDate.of (getYear () + 1900, getMonth () + 1, getDate ())
Grigory Kislin
Die Zeilen mit der Aufschrift "veraltet": |
TheRealChx101
8

Sie können in einer Zeile konvertieren:

public static LocalDate getLocalDateFromDate(Date date){
   return LocalDate.from(Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()));
}
Sahil Chhabra
quelle
Ich erhalte einen Fehler wie diesen mit dieser Methode: java.time.DateTimeException: LocalDate kann nicht von TemporalAccessor abgerufen werden: 2018-01-31T11: 54: 27.964Z vom Typ java.time.Instant
Luigi Rubino
@LuigiRubino danke für den Hinweis. Bitte beachten Sie die aktualisierte Antwort. Ich habe früher vergessen, Zone hinzuzufügen.
Sahil Chhabra
8

Erstens ist es einfach, ein Datum in einen Instant umzuwandeln

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

Anschließend können Sie den Instant mit der Methode ofInstant () in eine beliebige Datums-API in JDK 8 konvertieren:

LocalDateTime date = LocalDateTime.ofInstant(timestamp, ZoneId.systemDefault()); 
Shedom Wei
quelle
enthält localDate, localDateTime, localTime
Shedom Wei
Was bedeutet es, hier ZoneId anzugeben? Wenn ich Datum -> Sofort von der API erhalte und ich es mir als Millisekunden ab 1970 in UTC vorstellen kann. Wenn ich nur das entsprechende LocalDate (JJJJ-MM-TT) aus der Perspektive der API erhalten möchte, die nicht in eine Zeitzone konvertiert wurde, sollte ich ZoneOffset.UTC nicht verwenden, um diesen sofortigen Versatz nicht zu meiner lokalen Zeitzone zu erhalten. Ist nicht Ihr Beispiel mit ZoneId.systemDefault () verschiebt Datumsform Server. Ex. Instant steht für 2018-10-20 0:00 und wurde dann von UTC nach America / Los_Angeles verschoben. Ich erhalte das lokale Datum 2018-10-19 anstelle von 2018-10-20?
Michał Ziobro
Es bricht ab, wenn Sie zufällig import java.sql.Datein Ihrer Datei haben: die toInstant()Methode, java.sql.Dateimmer zu werfen.
Oliv
4
Date input = new Date();
LocalDateTime  conv=LocalDateTime.ofInstant(input.toInstant(), ZoneId.systemDefault());
LocalDate convDate=conv.toLocalDate();

Die DateInstanz enthält neben dem Datum auch die Uhrzeit, während LocalDatedies nicht der Fall ist. Sie können es also zuerst LocalDateTimemit seiner Methode konvertieren und ofInstant()dann, wenn Sie es ohne Zeit möchten , die Instanz in konvertieren LocalDate.

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

Dieses Format ist von 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();
    }
宏杰 李
quelle
0

Ich hatte Probleme mit der Implementierung von @ JodaStephen in JBoss EAP 6. Daher habe ich die Konvertierung nach dem Java-Tutorial von Oracle unter http://docs.oracle.com/javase/tutorial/datetime/iso/legacy.html neu geschrieben .

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

Was ist los mit dieser 1 einfachen Zeile?

new LocalDateTime(new Date().getTime()).toLocalDate();
Lawrence
quelle
Java beschwert sich, dass es toLocalDate () für den primitiven Typ nicht lange aufrufen kann. Ich habe ein aktuelles Datumsobjekt verwendet. Vielleicht gibt es die Reibung?
TheGeeko61
-8

Ich habe diese Frage mit der folgenden Lösung gelöst

  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 diesem Fall drucken localDate Ihr Datum in diesem Format "JJJJ-MM-TT".

estevamdf
quelle
1
Die Frage ist nach einer Lösung mit den java.timeKlassen in JDK8, nicht mit Joda Time.
Pioto