Jaki jest najlepszy sposób przekonwertowania java.util.Date
obiektu na nowy JDK 8 / JSR-310 java.time.LocalDate
?
Date input = new Date();
LocalDate date = ???
Jaki jest najlepszy sposób przekonwertowania java.util.Date
obiektu na nowy JDK 8 / JSR-310 java.time.LocalDate
?
Date input = new Date();
LocalDate date = ???
Odpowiedzi:
Krótka odpowiedź
Date input = new Date();
LocalDate date = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
Wyjaśnienie
Pomimo swojej nazwy, java.util.Date
reprezentuje natychmiast na linii czasu, a nie „datę”. Rzeczywiste dane przechowywane w obiekcie to long
liczba milisekund od 1970-01-01T00: 00Z (północ na początku 1970 GMT / UTC).
Klasa równoważna do java.util.Date
w JSR-310 jest Instant
, dlatego istnieje wygodna metoda toInstant()
zapewnienia konwersji:
Date input = new Date();
Instant instant = input.toInstant();
java.util.Date
Instancja ma koncepcji strefy czasowej. Może się to wydawać dziwne, jeśli wywołujesz toString()
a java.util.Date
, ponieważ toString
dotyczy ono strefy czasowej. Jednak ta metoda faktycznie używa domyślnej strefy czasowej Javy w locie, aby podać ciąg. Strefa czasowa nie jest częścią rzeczywistego stanu java.util.Date
.
A Instant
także nie zawiera żadnych informacji o strefie czasowej. Dlatego, aby przekonwertować Instant
datę z lokalnej na lokalną, należy określić strefę czasową. Może to być strefa domyślna - ZoneId.systemDefault()
- lub strefa czasowa kontrolowana przez aplikację, na przykład strefa czasowa z preferencji użytkownika. Użyj atZone()
metody, aby zastosować strefę czasową:
Date input = new Date();
Instant instant = input.toInstant();
ZonedDateTime zdt = instant.atZone(ZoneId.systemDefault());
ZonedDateTime
Zawiera stan składają lokalnej daty i godziny, strefy czasowej i przesunięcie od GMT / UTC. W związku z tym datę - LocalDate
- można łatwo wyodrębnić za pomocą toLocalDate()
:
Date input = new Date();
Instant instant = input.toInstant();
ZonedDateTime zdt = instant.atZone(ZoneId.systemDefault());
LocalDate date = zdt.toLocalDate();
Odpowiedź Java 9
W Java SE 9 dodano nową metodę , która nieco upraszcza to zadanie:
Date input = new Date();
LocalDate date = LocalDate.ofInstant(input.toInstant(), ZoneId.systemDefault());
Ta nowa alternatywa jest bardziej bezpośrednia, tworząc mniej śmieci, a zatem powinna działać lepiej.
Date
nie ma pojęcia strefy czasowej, Instant
nie zawiera również informacji o strefie czasowej. LocalDate
API mówi „aktualizuje bez strefy czasowej”. Więc dlaczego konwersji od Date
do Instant
do LocalDate
potrzeb atZone(ZoneId.systemDefault())
?
LocalDate
i LocalDateTime
nie „przechowuj ani nie reprezentuj czasu ani strefy czasowej” (zob. Javadocs). Chociaż nie przechowują go, klasy reprezentują Local
datę i / lub godzinę, dlatego konwersja na lokalną datę / godzinę implikuje strefę czasową.
Lepszym sposobem jest:
Date date = ...;
Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate()
Zalety tej wersji:
działa niezależnie od tego, czy dane wejściowe są instancjami java.util.Date
lub podklasami java.sql.Date
(w przeciwieństwie do sposobu @ JodaStephen). Jest to powszechne w przypadku danych pochodzących z JDBC. java.sql.Date.toInstant()
zawsze zgłasza wyjątek.
to samo dotyczy JDK8 i JDK7 z backportem JSR-310
Ja osobiście korzystam z klasy użytkowej (ale nie jest ona kompatybilna z 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);
}
}
Ta asLocalDate()
metoda jest tutaj zerowo bezpieczna, używa toLocalDate()
, jeśli dane wejściowe są java.sql.Date
(może zostać przesłonięte przez sterownik JDBC, aby uniknąć problemów ze strefą czasową lub niepotrzebnych obliczeń), w przeciwnym razie używa powyższej metody.
DateConvertUtils
.
Date.toInstant()
.
LocalDate localDate = LocalDate.parse( new SimpleDateFormat("yyyy-MM-dd").format(date) );
SimpleDateFormat
przypadku instancja jest ograniczona do bieżącego wątku. Jest używany w sposób bezpieczny dla wątków. Teraz SimpleDateFormat
uchodzi za „drogie instancji” (ze względu na wszystkich wewnętrznych struktur danych potrzebnych), ale nie można dzielić jeden jako „pojedyncza” (bez synchronizacji dostępu do niego), bo to nie jest rzeczywiście thread- bezpieczny. ( ThreadLocal
Rozwiązanie może działać, jeśli kod „zanieczyszczający” Thread
w tym był odpowiedzialny za cykl życia wątku ... ale to rzadko się zdarza). Niezręczny. Unikanie SimpleDateFormat
jest powodem korzystania . javax.time
SimpleDateFormat
(który jest wyrzucany), łańcuch pośredni (który jest wyrzucany) i koszt parsowania. To rozwiązanie, ale nie jest zalecane.
Jeśli używasz Java 8, odpowiedź @ JodaStephen jest oczywiście najlepsza. Jeśli jednak pracujesz z backportem JSR-310 , niestety musisz zrobić coś takiego:
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();
Możesz przekonwertować w jednym wierszu:
public static LocalDate getLocalDateFromDate(Date date){
return LocalDate.from(Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()));
}
po pierwsze, łatwo jest przekonwertować datę na natychmiastową
Instant timestamp = new Date().toInstant();
Następnie możesz przekonwertować Instant na dowolny interfejs API daty w jdk 8 za pomocą metody ofInstant ():
LocalDateTime date = LocalDateTime.ofInstant(timestamp, ZoneId.systemDefault());
import java.sql.Date
w swoim pliku: toInstant()
metoda java.sql.Date
zawsze rzuca.
Date input = new Date();
LocalDateTime conv=LocalDateTime.ofInstant(input.toInstant(), ZoneId.systemDefault());
LocalDate convDate=conv.toLocalDate();
Date
Instancji nie zawierają zbyt czasu wraz z datą, gdy LocalDate
tego nie robi. Możesz więc najpierw przekonwertować go na LocalDateTime
jego metodę, ofInstant()
a następnie, jeśli chcesz go bez czasu, przekonwertować instancję na LocalDate
.
public static LocalDate Date2LocalDate(Date date) {
return LocalDate.parse(date.toString(), DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy"))
ten format pochodzi z 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();
}
Miałem problemy z implementacją @ JodaStephen na JBoss EAP 6. Więc przepisałem konwersję po Samouczku Java Oracle w 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();
Co jest nie tak z tą 1 prostą linią?
new LocalDateTime(new Date().getTime()).toLocalDate();
Rozwiązałem to pytanie za pomocą rozwiązania poniżej
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);
W takim przypadku localDate wydrukuj datę w tym formacie „rrrr-MM-dd”
java.time
klasy w JDK8, a nie Jody Time.
LocalDate.from(Instant.ofEpochMilli(date.getTime()))
, że jest to odpowiednik twojego, ale bardziej bezpośredni.