Qual è il modo migliore per convertire un java.util.Date
oggetto nel nuovo JDK 8 / JSR-310 java.time.LocalDate
?
Date input = new Date();
LocalDate date = ???
Qual è il modo migliore per convertire un java.util.Date
oggetto nel nuovo JDK 8 / JSR-310 java.time.LocalDate
?
Date input = new Date();
LocalDate date = ???
Risposte:
Risposta breve
Date input = new Date();
LocalDate date = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
Spiegazione
Nonostante il suo nome, java.util.Date
rappresenta un istante sulla linea temporale, non una "data". I dati effettivi memorizzati nell'oggetto sono long
conteggi di millisecondi dal 1970-01-01T00: 00Z (mezzanotte all'inizio del 1970 GMT / UTC).
La classe equivalente a java.util.Date
in JSR-310 è Instant
, quindi esiste un metodo conveniente toInstant()
per fornire la conversione:
Date input = new Date();
Instant instant = input.toInstant();
Un java.util.Date
esempio 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 Instant
data 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 ZonedDateTime
contiene 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.
Date
non ha il concetto di fuso orario, Instant
inoltre non contiene informazioni sul fuso orario. L' LocalDate
API dice "Una data senza fuso orario". Allora perché convertire da Date
in Instant
a LocalDate
bisogni atZone(ZoneId.systemDefault())
?
LocalDate
e LocalDateTime
non "memorizzare o rappresentare un orario o un fuso orario" (rif: javadocs). Sebbene non lo memorizzino, le classi rappresentano una Local
data e / o ora, quindi la conversione in data / ora locale implica un fuso orario.
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.Date
o 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.
DateConvertUtils
.
Date.toInstant()
.
LocalDate localDate = LocalDate.parse( new SimpleDateFormat("yyyy-MM-dd").format(date) );
SimpleDateFormat
istanza è limitata al thread corrente. È utilizzato in modo sicuro per i thread. Ora, SimpleDateFormat
si 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 ThreadLocal
soluzione può funzionare se il codice "inquinante" Thread
in questo è responsabile del ciclo di vita del thread ... ma ciò accade raramente). Awkward. Evitare SimpleDateFormat
è il motivo dell'uso javax.time
.
SimpleDateFormat
(che viene gettato via), la stringa intermedia (che viene gettata via) e il costo dell'analisi. È una soluzione, ma non è raccomandato.
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));
LocalDate ld = new java.sql.Date( new java.util.Date().getTime() ).toLocalDate();
Puoi convertire in una riga:
public static LocalDate getLocalDateFromDate(Date date){
return LocalDate.from(Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()));
}
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());
import java.sql.Date
nel tuo file: il toInstant()
metodo di java.sql.Date
sempre getta.
Date input = new Date();
LocalDateTime conv=LocalDateTime.ofInstant(input.toInstant(), ZoneId.systemDefault());
LocalDate convDate=conv.toLocalDate();
L' Date
istanza contiene anche l'ora insieme alla data, mentre LocalDate
non lo è. Quindi puoi prima convertirlo in LocalDateTime
usando il suo metodo, ofInstant()
quindi se lo desideri senza tempo, converti l'istanza in LocalDate
.
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();
}
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();
Cosa c'è di sbagliato in questa 1 semplice riga?
new LocalDateTime(new Date().getTime()).toLocalDate();
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"
java.time
classi in JDK8, non con Joda Time.
LocalDate.from(Instant.ofEpochMilli(date.getTime()))
equivalente al tuo, ma più diretto.