Wie konvertiert man am besten ein java.util.Date
Objekt in das neue JDK 8/JSR-310 java.time.LocalDate
?
Date input = new Date();
LocalDate date = ???
Kurzantwort
Date input = new Date();
LocalDate date = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
Erläuterung
Trotz seines Namens repräsentiert java.util.Date
einen Zeitpunkt auf der Zeitachse, nicht ein "Datum". Die tatsächlichen Daten, die in dem Objekt gespeichert werden, sind eine "lange" Anzahl von Millisekunden seit 1970-01-01T00:00Z (Mitternacht zu Beginn des Jahres 1970 GMT/UTC).
Die äquivalente Klasse zu java.util.Date
in JSR-310 ist Instant
, daher gibt es eine praktische Methode toInstant()
, um die Konvertierung zu ermöglichen:
Date input = new Date();
Instant instant = input.toInstant();
Eine java.util.Date
-Instanz hat kein Konzept der Zeitzone. Das mag seltsam erscheinen, wenn Sie toString()
auf einem java.util.Date
aufrufen, weil toString
relativ zu einer Zeitzone ist. Diese Methode verwendet jedoch die Standard-Zeitzone von Java, um die Zeichenkette bereitzustellen. Die Zeitzone ist nicht Teil des aktuellen Status von java.util.Date
.
Ein Instant
enthält auch keine Informationen über die Zeitzone. Um von einer Instant
in ein lokales Datum zu konvertieren, ist es daher notwendig, eine Zeitzone anzugeben. Dies kann die Standardzone sein - ZoneId.systemDefault()
- oder es kann eine Zeitzone sein, die Ihre Anwendung kontrolliert, wie z.B. eine Zeitzone aus den Benutzereinstellungen. Verwenden Sie die Methode atZone()
, um die Zeitzone anzuwenden:
Date input = new Date();
Instant instant = input.toInstant();
ZonedDateTime zdt = instant.atZone(ZoneId.systemDefault());
Eine ZonedDateTime
enthält einen Status, der aus dem lokalen Datum und der lokalen Zeit, der Zeitzone und dem Offset von GMT/UTC besteht. Als solches kann das Datum - LocalDate
- leicht mit toLocalDate()
extrahiert werden:
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 etwas vereinfacht:
Date input = new Date();
LocalDate date = LocalDate.ofInstant(input.toInstant(), ZoneId.systemDefault());
Diese neue Alternative ist direkter, erzeugt weniger Müll und sollte daher besser funktionieren.
Der bessere Weg ist:
Date date = ...;
Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate()
Vorteile dieser Version:
funktioniert unabhängig davon, ob die Eingabe eine Instanz von java.util.Date
oder dessen Unterklasse java.sql.Date
ist (im Gegensatz zu @JodaStephen's Weg). Dies ist üblich bei Daten, die von JDBC stammen. java.sql.Date.toInstant()` wirft immer eine Ausnahme.
es ist das gleiche für JDK8 und JDK7 mit JSR-310 Backport
Ich persönlich verwende eine Utility-Klasse (diese ist aber 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 Methode asLocalDate()
hier ist null-sicher, verwendet toLocalDate()
, wenn die Eingabe java.sql.Date
ist (sie kann vom JDBC-Treiber überschrieben werden, um Zeitzonenprobleme oder unnötige Berechnungen zu vermeiden), andernfalls wird die oben genannte Methode verwendet.
Wenn Sie Java 8 verwenden, ist @JodaStephen's Antwort offensichtlich die beste. Wenn Sie jedoch mit dem [JSR-310 Backport] (http://www.threeten.org/threetenbp/) arbeiten, müssen Sie leider etwas wie folgt 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));