Konversi java.util.Date ke java.time.LocalDate

494

Apa cara terbaik untuk mengonversi java.util.Dateobjek ke JDK 8 / JSR-310 yang baru java.time.LocalDate?

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

Jawaban:

828

Jawaban singkat

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

Penjelasan

Meskipun namanya, java.util.Datemewakili instan di garis waktu, bukan "tanggal". Data aktual yang disimpan dalam objek adalah along hitungan milidetik sejak 1970-01-01T00: 00Z (tengah malam pada awal 1970 GMT / UTC).

Kelas setara dengan java.util.Datedi JSR-310 adalah Instant, sehingga ada metode yang mudah toInstant()untuk menyediakan konversi:

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

Sebuah java.util.Dateinstance tidak memiliki konsep zona waktu. Ini mungkin tampak aneh jika Anda memanggil toString()pada java.util.Date, karena toStringrelatif ke-zona waktu. Namun metode itu benar-benar menggunakan zona waktu default Java on the fly untuk memberikan string. Zona waktu bukan bagian dari keadaan aktual java.util.Date.

An Instantjuga tidak mengandung informasi apa pun tentang zona waktu. Jadi, untuk mengonversi dari tanggal Instantke tanggal lokal, perlu menentukan zona waktu. Ini mungkin zona default - ZoneId.systemDefault()- atau mungkin zona waktu yang dikontrol aplikasi Anda, seperti zona waktu dari preferensi pengguna. Gunakan atZone()metode untuk menerapkan zona waktu:

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

A ZonedDateTimeberisi keadaan yang terdiri dari tanggal dan waktu setempat, zona waktu dan offset dari GMT / UTC. Dengan demikian tanggal - LocalDate- dapat dengan mudah diekstraksi menggunakan toLocalDate():

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

Java 9 jawaban

Di Java SE 9, metode baru telah ditambahkan yang sedikit menyederhanakan tugas ini:

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

Alternatif baru ini lebih langsung, menghasilkan lebih sedikit sampah, dan karenanya berkinerja lebih baik.

JodaStephen
sumber
14
Saya LocalDate.from(Instant.ofEpochMilli(date.getTime()))pikir saya itu setara dengan milik Anda, tetapi lebih langsung.
Marko Topolnik
9
@MarkoTopolnik yang mengkompilasi tetapi tidak berjalan. Instan tidak mengandung zona waktu sehingga tidak ada cara untuk mendapatkan LocalDate.
JodaStephen
11
@assylias Cukup gunakan sqlDate.toLocalDate ()!
JodaStephen
25
@JodaStephen Datetidak memiliki konsep zona waktu, Instantjuga tidak mengandung informasi tentang zona waktu. The LocalDateAPI mengatakan "tanggal A tanpa-zona waktu". Lalu mengapa mengkonversi dari Dateke Instantke LocalDatekebutuhan atZone(ZoneId.systemDefault())?
Gustavo
8
@Gustavo: LocalDatedan LocalDateTimejangan "menyimpan atau mewakili waktu atau zona waktu" (ref: javadocs). Meskipun mereka tidak menyimpannya - kelas-kelas tersebut mewakili Localtanggal dan / atau waktu, karenanya konversi ke tanggal / waktu setempat menyiratkan zona waktu.
Cuga
158

Cara yang lebih baik adalah:

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

Keuntungan dari versi ini:

  • berfungsi terlepas dari input yang merupakan turunan dari java.util.Dateatau merupakan subkelas dari java.sql.Date(tidak seperti cara @ JodaStephen). Ini biasa terjadi pada data yang berasal dari JDBC. java.sql.Date.toInstant()selalu melempar pengecualian.

  • itu sama untuk JDK8 dan JDK7 dengan JSR-310 backport

Saya pribadi menggunakan kelas utilitas (tetapi tidak kompatibel dengan 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);
    }

}

The asLocalDate()Metode sini adalah nol-aman, penggunaan toLocalDate(), jika input java.sql.Date(mungkin overriden oleh driver JDBC untuk masalah menghindari zona waktu atau perhitungan yang tidak perlu), jika tidak menggunakan metode tersebut di atas.

Oliv
sumber
12
Jika ini cara yang lebih baik, itu sangat jelek dan bertele-tele. Sangat menyakitkan.
ceklock
3
Lebih baik dibandingkan dengan jawaban yang diterima, saya jelaskan alasannya. Ya, memang jelek, tapi itu sebabnya aja DateConvertUtils.
Oliv
Saya tidak mengerti mengapa mereka tidak menerapkan kelas konversi di API baru.
ceklock
@ceklock, mereka tidak melaksanakan, tidak kelas konversi, tetapi beberapa metode konversi, seperti Date.toInstant().
Ole VV
2
Apakah ada perpustakaan Kotlin yang mengemas ini sebagai fungsi ekstensi sehingga pengembang dapat melakukan hal berikut. Tanggal x = ...; x.asLocalDate ();
Mark Ashworth
20
LocalDate localDate = LocalDate.parse( new SimpleDateFormat("yyyy-MM-dd").format(date) );
ceklock
sumber
7
Di sini, SimpleDateFormatinstance terbatas pada utas saat ini. Ini digunakan dengan cara yang aman. Sekarang, SimpleDateFormatterkenal sebagai 'mahal untuk instantiate' (pada rekening semua struktur data internal yang dibutuhkan) tetapi Anda tidak dapat berbagi satu sebagai 'tunggal' (tanpa sinkronisasi akses ke sana), karena itu memang tidak benang- aman. (Sebuah ThreadLocalsolusi dapat bekerja jika kode 'mencemari' Threaddalam hal ini bertanggung jawab atas siklus hidup utas ... tetapi itu jarang terjadi). Canggung. Menghindari SimpleDateFormatadalah yang alasan untuk menggunakan javax.time.
David Bullock
6
Pendekatan ini memiliki banyak overhead: 'mahal' SimpleDateFormat(yang dibuang), string menengah (yang dibuang), dan biaya penguraian. Ini sebuah solusi, tapi tidak direkomendasikan.
David Bullock
2
@ceklock tapi kemudian Anda mendapatkan masalah bahwa itu tidak lagi aman thread
flup
4
@ceklock SimpleDateFormat hanya dapat memproses satu tanggal pada suatu waktu, jika Anda menggunakannya secara bersamaan, itu akan menghasilkan hasil yang kacau. Jadi ya, itu penting. Jangan membuat satu contoh SimpleDateFormat dalam variabel global.
flup
19

Jika Anda menggunakan Java 8, jawaban @ JodaStephen jelas yang terbaik. Namun, jika Anda bekerja dengan backport JSR-310 , Anda sayangnya harus melakukan sesuatu seperti ini:

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
sumber
4
Tidak benar, jawaban @ JodaStephen masih berfungsi. Anda hanya perlu cara lain untuk mengubah java.util.Date menjadi Instan. Untuk ini, Anda dapat menggunakan org.threeten.bp.DateTimeUtils.toInstant: threeten.org/threetenbp/apidocs/org/threeten/bp/...
Christian Ciach
3
DateTimeUtils tidak tersedia ketika saya menggunakan backport, tetapi Anda benar bahwa itu tersedia untuk siapa saja yang menggunakan ThreeTen Backport 1.0 atau lebih baru. Terima kasih telah menunjukkannya.
dhalsim2
14
LocalDate ld = new java.sql.Date( new java.util.Date().getTime() ).toLocalDate();
Gustavo
sumber
1
Dan itu realisasi paling sederhana: LocalDate.of (getYear () + 1900, getMonth () + 1, getDate ())
Grigory Kislin
Baris yang mengatakan sudah usang: |
TheRealChx101
8

Anda dapat mengonversi dalam satu baris:

public static LocalDate getLocalDateFromDate(Date date){
   return LocalDate.from(Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()));
}
Sahil Chhabra
sumber
Saya mendapatkan kesalahan seperti ini menggunakan metode ini: java.time.DateTimeException: Tidak dapat memperoleh LocalDate dari TemporalAccessor: 2018-01-31T11: 54: 27.964Z dari tipe java.time.Instant
Luigi Rubino
@LuigiRubino terima kasih telah menunjukkan. Silakan lihat jawaban yang diperbarui. Saya lupa menambahkan Zone sebelumnya.
Sahil Chhabra
8

pertama, mudah untuk mengubah Tanggal menjadi Instan

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

Kemudian, Anda dapat mengonversi api Instan ke api tanggal mana saja di jdk 8 menggunakan metode ofInstant ():

LocalDateTime date = LocalDateTime.ofInstant(timestamp, ZoneId.systemDefault()); 
Shedom Wei
sumber
termasuk localDate, localDateTime, localTime
Shedom Wei
Apa artinya menentukan ZoneId di sini. Jika saya mendapatkan Tanggal -> Instan dari API dan saya dapat menganggapnya sebagai milidetik dari tahun 1970 dalam UTC. Ketika saya ingin mendapatkan LocalDate yang sesuai (yyyy-mm-dd) dari perspektif api yang tidak dikonversi ke zona waktu apa pun, saya tidak boleh menggunakan ZoneOffset.UTC untuk tidak memiliki offset instan ini ke zona waktu lokal saya. Bukankah contoh Anda dengan ZoneId.systemDefault () menggeser server formulir tanggal. Ex. instan mewakili 2018-10-20 0:00 dan kemudian bergeser dari UTC ke America / Los_Angeles saya dapatkan di Local Date 2018-10-19 bukannya 2018-10-20?
Michał Ziobro
Itu rusak jika Anda kebetulan ada import java.sql.Datedi file Anda: toInstant()metode java.sql.Dateselalu melempar.
Oliv
4
Date input = new Date();
LocalDateTime  conv=LocalDateTime.ofInstant(input.toInstant(), ZoneId.systemDefault());
LocalDate convDate=conv.toLocalDate();

Mesin Datevirtual juga mengandung waktu bersama dengan tanggal sementara LocalDatetidak. Jadi, pertama-tama Anda dapat mengonversinya menjadi LocalDateTimemenggunakan metodenya ofInstant()maka jika Anda menginginkannya tanpa waktu, maka konversikan instance tersebut menjadi LocalDate.

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

format ini dari 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();
    }
宏杰 李
sumber
0

Saya memiliki masalah dengan implementasi @ JodaStephen di JBoss EAP 6. Jadi, saya menulis ulang konversi mengikuti Tutorial Java Oracle di 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
sumber
-2

Apa yang salah dengan 1 baris sederhana ini?

new LocalDateTime(new Date().getTime()).toLocalDate();
Lawrence
sumber
Java mengeluh bahwa itu tidak bisa memanggil toLocalDate () pada tipe primitif lama. Saya menggunakan objek Date yang sebenarnya; mungkin ada intinya ??
TheGeeko61
-8

Saya memecahkan pertanyaan ini dengan solusi di bawah ini

  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);

Dalam hal ini localDate mencetak tanggal Anda dalam format ini "yyyy-MM-dd"

estevamdf
sumber
1
Pertanyaannya adalah mencari solusi menggunakan java.timekelas - kelas di JDK8, bukan dengan Joda Time.
pioto