Konwertuj java.util.Date na java.time.LocalDate

494

Jaki jest najlepszy sposób przekonwertowania java.util.Dateobiektu na nowy JDK 8 / JSR-310 java.time.LocalDate?

Date input = new Date();
LocalDate date = ???
JodaStephen
źródło

Odpowiedzi:

828

Krótka odpowiedź

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

Wyjaśnienie

Pomimo swojej nazwy, java.util.Datereprezentuje natychmiast na linii czasu, a nie „datę”. Rzeczywiste dane przechowywane w obiekcie to longliczba milisekund od 1970-01-01T00: 00Z (północ na początku 1970 GMT / UTC).

Klasa równoważna do java.util.Datew JSR-310 jest Instant, dlatego istnieje wygodna metoda toInstant()zapewnienia konwersji:

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

java.util.DateInstancja ma koncepcji strefy czasowej. Może się to wydawać dziwne, jeśli wywołujesz toString()a java.util.Date, ponieważ toStringdotyczy 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 Instanttakże nie zawiera żadnych informacji o strefie czasowej. Dlatego, aby przekonwertować Instantdatę 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());

ZonedDateTimeZawiera 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.

JodaStephen
źródło
14
Myślałem LocalDate.from(Instant.ofEpochMilli(date.getTime())), że jest to odpowiednik twojego, ale bardziej bezpośredni.
Marko Topolnik,
9
@MarkoTopolnik, który się kompiluje, ale nie działa. Instant nie zawiera strefy czasowej, więc nie ma możliwości uzyskania LocalDate.
JodaStephen
11
@assylias Wystarczy użyć sqlDate.toLocalDate ()!
JodaStephen
25
@JodaStephen Datenie ma pojęcia strefy czasowej, Instantnie zawiera również informacji o strefie czasowej. LocalDateAPI mówi „aktualizuje bez strefy czasowej”. Więc dlaczego konwersji od Datedo Instantdo LocalDatepotrzeb atZone(ZoneId.systemDefault())?
Gustavo
8
@ Gustavo: LocalDatei LocalDateTimenie „przechowuj ani nie reprezentuj czasu ani strefy czasowej” (zob. Javadocs). Chociaż nie przechowują go, klasy reprezentują Localdatę i / lub godzinę, dlatego konwersja na lokalną datę / godzinę implikuje strefę czasową.
Cuga
158

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.Datelub 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.

Oliv
źródło
12
Jeśli jest to lepszy sposób, jest bardzo brzydki i pełny. Bardzo bolesne.
Ceklock
3
Lepiej w porównaniu do przyjętej odpowiedzi, wyjaśniam dlaczego. Tak, to brzydkie, ale dlatego napisałem DateConvertUtils.
Oliv,
Nie rozumiem, dlaczego nie zaimplementowali klasy konwersji w nowym interfejsie API.
Ceklock
@ceklock, oni nie realizować, a nie klasę konwersji, ale kilka metod konwersji, jak Date.toInstant().
Ole VV
2
Czy istnieje biblioteka Kotlin, która pakuje je jako funkcje rozszerzeń, aby programista mógł wykonać następujące czynności. Data x = ...; x.asLocalDate ();
Mark Ashworth
20
LocalDate localDate = LocalDate.parse( new SimpleDateFormat("yyyy-MM-dd").format(date) );
Ceklock
źródło
7
W tym SimpleDateFormatprzypadku instancja jest ograniczona do bieżącego wątku. Jest używany w sposób bezpieczny dla wątków. Teraz SimpleDateFormatuchodzi 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. ( ThreadLocalRozwiązanie może działać, jeśli kod „zanieczyszczający” Threadw tym był odpowiedzialny za cykl życia wątku ... ale to rzadko się zdarza). Niezręczny. Unikanie SimpleDateFormatjest powodem korzystania . javax.time
David Bullock,
6
Takie podejście ma wiele kosztów ogólnych: „drogi” SimpleDateFormat(który jest wyrzucany), łańcuch pośredni (który jest wyrzucany) i koszt parsowania. To rozwiązanie, ale nie jest zalecane.
David Bullock,
2
@ceklock, ale pojawia się problem, że nie jest już bezpieczny dla
wątków
4
@ceklock SimpleDateFormat może przetwarzać tylko jedną datę na raz, jeśli użyjesz jej jednocześnie, da to zniekształcone wyniki. Tak, to jest ważne. Nie twórz pojedynczego wystąpienia SimpleDateFormat w zmiennej globalnej.
flup
19

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));
dhalsim2
źródło
4
Nieprawda, odpowiedź @ JodaStephen nadal działa. Potrzebujesz tylko innego sposobu konwersji pliku java.util.Date na Instant. W tym celu możesz użyć org.threeten.bp.DateTimeUtils.toInstant: threeten.org/threetenbp/apidocs/org/threeten/bp/…
Christian Ciach
3
DateTimeUtils nie był dostępny, gdy korzystałem z backportu, ale masz rację, że jest on dostępny dla każdego, kto korzysta z ThreeTen Backport 1.0 lub nowszego. Dzięki za zwrócenie na to uwagi.
dhalsim2
14
LocalDate ld = new java.sql.Date( new java.util.Date().getTime() ).toLocalDate();
Gustavo
źródło
1
I najprostsza realizacja: LocalDate.of (getYear () + 1900, getMonth () + 1, getDate ())
Grigory Kislin
Linie, które mówią przestarzałe: |
TheRealChx101
8

Możesz przekonwertować w jednym wierszu:

public static LocalDate getLocalDateFromDate(Date date){
   return LocalDate.from(Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()));
}
Sahil Chhabra
źródło
Występuje taki błąd przy użyciu tej metody: java.time.DateTimeException: Nie można uzyskać LocalDate z TemporalAccessor: 2018-01-31T11: 54: 27.964Z typu java.time.Instant
Luigi Rubino
@LuigiRubino dzięki za wskazanie. Zobacz zaktualizowaną odpowiedź. Zapomniałem wcześniej dodać Strefy.
Sahil Chhabra
8

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()); 
Shedom Wei
źródło
obejmuje localDate, localDateTime, localTime
Shedom Wei
Co to znaczy tutaj podać ZoneId. Jeśli dostanę datę -> Instant z API i mogę myśleć o tym jako milisekundach od 1970 roku w UTC. Kiedy chcę uzyskać tylko odpowiednią datę lokalną (rrrr-mm-dd) z perspektywy interfejsu API nie przekonwertowanego na żadną strefę czasową, nie powinienem używać ZoneOffset.UTC, aby nie mieć tego natychmiastowego przesunięcia w stosunku do mojej lokalnej strefy czasowej. Czy twój przykład ze ZoneId.systemDefault () nie zmienia daty z serwera. Dawny. instant reprezentuje 2018-10-20 0:00, a następnie przeniósł się z UTC do Ameryki / Los_Angeles dostaję w lokalnej dacie 2018-10-19 zamiast 2018-10-20?
Michał Ziobro,
Łamie się, jeśli akurat masz import java.sql.Datew swoim pliku: toInstant()metoda java.sql.Datezawsze rzuca.
Oliv,
4
Date input = new Date();
LocalDateTime  conv=LocalDateTime.ofInstant(input.toInstant(), ZoneId.systemDefault());
LocalDate convDate=conv.toLocalDate();

DateInstancji nie zawierają zbyt czasu wraz z datą, gdy LocalDatetego nie robi. Możesz więc najpierw przekonwertować go na LocalDateTimejego metodę, ofInstant()a następnie, jeśli chcesz go bez czasu, przekonwertować instancję na LocalDate.

Arun Raaj
źródło
1
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();
    }
宏杰 李
źródło
0

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();
rogerio_gentil
źródło
-2

Co jest nie tak z tą 1 prostą linią?

new LocalDateTime(new Date().getTime()).toLocalDate();
Lawrence
źródło
Java skarży się, że nie może długo wywoływać funkcji toLocalDate () na typie pierwotnym. Użyłem rzeczywistego obiektu Date; może jest tarcie?
TheGeeko61
-8

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”

estevamdf
źródło
1
Pytanie dotyczy rozwiązania wykorzystującego java.timeklasy w JDK8, a nie Jody Time.
pioto