Java.time.LocalDateTime ve java.util.Date arasında dönüştürme


484

Java 8'de tarih ve saat için tamamen yeni bir API var. Bu API'daki en kullanışlı sınıflardan biri LocalDateTime, saat diliminden bağımsız bir tarih ve saat değeri tutmaktır.

java.util.DateBu amaç için eski sınıfı kullanan muhtemelen milyonlarca kod satırı vardır . Bu nedenle, eski ve yeni kodu birbirine bağlarken ikisi arasında dönüştürme ihtiyacı olacaktır. Bunu başarmak için doğrudan bir yöntem olmadığı gibi, bu nasıl yapılabilir?




Yanıtlar:


706

Kısa cevap:

Date in = new Date();
LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault());
Date out = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());

Açıklama: ( hakkında bu soruya dayanarak LocalDate)

Adına rağmen, java.util.Datebir "tarih" değil, zaman çizgisinde bir anı temsil eder. Nesne içinde saklanan gerçek veriler, long1970-01-01T00: 00Z'den (1970 GMT / UTC'nin başında gece yarısı) bu yana geçen milisaniye sayısıdır.

java.util.DateJSR-310'daki eşdeğer sınıf şöyledir Instant, bu nedenle ve sonra dönüşümü sağlamak için uygun yöntemler vardır:

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

Bir java.util.Dateörneğin saat dilimi kavramı yoktur. Aradığınızda, bu garip görünebilir toString()bir üzerinde java.util.Dateçünkü toStringbir zaman dilimine göre yapılır. Ancak bu yöntem aslında dizeyi sağlamak için Java'nın varsayılan saat dilimini anında kullanır. Saat dilimi, gerçek durumunun bir parçası değildir java.util.Date.

Bir Instantde zaman dilimine hakkında herhangi bir bilgi içermez. Bu nedenle, 'den Instantyerel bir tarih-saate dönüştürmek için bir saat dilimi belirtmek gerekir. Bu varsayılan bölge olabilir - ZoneId.systemDefault()- veya uygulamanızın kontrol ettiği bir saat dilimi olabilir, örneğin kullanıcı tercihlerinden bir saat dilimi. LocalDateTimehem anında hem de saat dilimini alan uygun bir fabrika yöntemine sahiptir:

Date in = new Date();
LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault());

Tersine, LocalDateTimesaat dilimi atZone(ZoneId)yöntem çağrılarak belirtilir . Daha ZonedDateTimesonra doğrudan bir şeye dönüştürülebilir Instant:

LocalDateTime ldt = ...
ZonedDateTime zdt = ldt.atZone(ZoneId.systemDefault());
Date output = Date.from(zdt.toInstant());

Not dönüşüm olduğunu LocalDateTimeiçin ZonedDateTimebeklenmeyen davranışlar tanıtmak potansiyeline sahiptir. Bunun nedeni, Yaz Saati Uygulaması nedeniyle her yerel tarih-saatin mevcut olmamasıdır. Sonbahar / sonbaharda, yerel zaman çizgisinde aynı yerel tarih saatinin iki kez oluştuğu bir çakışma vardır. İlkbaharda, bir saatin kaybolduğu bir boşluk var. atZone(ZoneId)Dönüşümün ne yapacağına ilişkin daha fazla tanım için Javadoc'a bakın .

Özet olarak, java.util.Datea LocalDateTimeve a arasında bir dönüş java.util.Dateyaparsanız, Yaz Saati Uygulaması nedeniyle farklı bir anlık sonuç elde edebilirsiniz.

Ek bilgi: Çok eski tarihleri ​​etkileyecek başka bir fark daha var. java.util.Date15 Ekim 1582'de, Gregoryen takvimi yerine Julian takvimini kullanmaktan önceki tarihlerle değişen bir takvim kullanır. Buna karşılık, java.time.*ISO takvim sistemini (Gregoryen'e eşdeğer) her zaman kullanır. Çoğu kullanım durumunda, ISO takvim sistemi istediğiniz şeydir, ancak 1582 yılından önceki tarihleri ​​karşılaştırırken garip etkiler görebilirsiniz.


5
Açık açıklama için çok teşekkürler. Özellikle neden java.util.Datesaat dilimi içermiyor, ancak sırasında yazdırıyor toString(). Etkinlik resmi belgeleri , yayınlarken bunu açıkça belirtmez.
Kiraz

Uyarı: LocalDateTime.ofInstant(date.toInstant()... safça beklendiği gibi davranmaz. Mesela bu yaklaşımı new Date(1111-1900,11-1,11,0,0,0);kullanacak 1111-11-17 23:53:28. java.sql.Timestamp#toLocalDateTime()Sonucun bir 1111-11-11 00:00:00önceki örnekte olması gerekiyorsa uygulamasına bir göz atın .
köpek

2
Çok eski tarihler hakkında bir bölüm ekledim (1582 öncesi). FWIW, önerilen düzeltme yanlış olabilir, çünkü java.util'de 1111-11-11.Tarih, farklı takvim sistemleri nedeniyle tarihte 1111-11-18 ile aynı gerçek gündür (6.5 dakikalık fark) 1900'den önce birçok zaman diliminde gerçekleşir)
JodaStephen

2
Ayrıca java.sql.Date#toInstantatar bir not atar UnsupportedOperationException. Bu yüzden toInstantbir RowMapper'da kullanmayın java.sql.ResultSet#getDate.
LazerBass

132

İşte geldiğim şey (ve tüm Tarih Saati bilmeceleri gibi, bazı tuhaf saat dilimi-leapyear-gün ışığı ayarlamasına dayanarak muhtemelen onaylanmayacak: D)

Yuvarlak açma: Date<<->>LocalDateTime

Verilen: Date date = [some date]

(1) LocalDateTime<< Instant<<Date

    Instant instant = Instant.ofEpochMilli(date.getTime());
    LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);

(2) Date<< Instant<<LocalDateTime

    Instant instant = ldt.toInstant(ZoneOffset.UTC);
    Date date = Date.from(instant);

Misal:

Verilen:

Date date = new Date();
System.out.println(date + " long: " + date.getTime());

(1) LocalDateTime<< Instant<< Date:

Oluşturma Instantdan Date:

Instant instant = Instant.ofEpochMilli(date.getTime());
System.out.println("Instant from Date:\n" + instant);

Create Datedan Instant(gerekli değildir, ancak gösterim amacıyla):

date = Date.from(instant);
System.out.println("Date from Instant:\n" + date + " long: " + date.getTime());

oluşturma LocalDateTimedanInstant

LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);
System.out.println("LocalDateTime from Instant:\n" + ldt);

(2) Date<< Instant<<LocalDateTime

Oluşturma Instantdan LocalDateTime:

instant = ldt.toInstant(ZoneOffset.UTC);
System.out.println("Instant from LocalDateTime:\n" + instant);

Oluşturma Datedan Instant:

date = Date.from(instant);
System.out.println("Date from Instant:\n" + date + " long: " + date.getTime());

Çıktı:

Fri Nov 01 07:13:04 PDT 2013 long: 1383315184574

Instant from Date:
2013-11-01T14:13:04.574Z

Date from Instant:
Fri Nov 01 07:13:04 PDT 2013 long: 1383315184574

LocalDateTime from Instant:
2013-11-01T14:13:04.574

Instant from LocalDateTime:
2013-11-01T14:13:04.574Z

Date from Instant:
Fri Nov 01 07:13:04 PDT 2013 long: 1383315184574

2
@scottb bana mı soruyu soran kişiye mi hitap ediyorsun? Düşüncelerimde, gelince de, olurdu güzel dönüşüm jdk 8 API açıkça belirttiğimiz üzere - Onlar en azından yapmış olabilir. Her durumda, yeni Java-8 özelliklerini dahil etmek ve nasıl yapılacağını bilmek için yeniden faktörlendirilecek çok sayıda kütüphane vardır.
Koordinatör

4
@scottb 3. taraf davası neden nadir? Çok yaygın. Bir örnek: JDBC 4 ve daha azı (umarım 5 değil).
Raman

5
Genel bir JSR-310 kuralı olarak, epoch-millis kullanarak türler arasında dönüştürme yapmaya gerek yoktur. Nesneleri kullanarak daha iyi alternatifler mevcuttur, aşağıdaki cevabımın tamamına bakın. Yukarıdaki yanıt yalnızca UTC gibi bir bölge uzaklığı kullanılıyorsa da tam olarak geçerlidir - yanıtın bazı bölümleri Amerika / New_York gibi tam bir zaman dilimi için çalışmaz.
JodaStephen

2
Yerine Instant.ofEpochMilli(date.getTime())dodate.toInstant()
keçi

1
@ goat toInstant()güzel görünüyor, ancak başarısız java.sql.Date, arggggh! Sonunda kullanımı daha kolay Instant.ofEpochMilli(date.getTime()).
vadipp

22

Varsayılan bir saat dilimine ihtiyacınız olduğundan eminseniz çok daha kullanışlı bir yol:

Date d = java.sql.Timestamp.valueOf( myLocalDateTime );

25
Tabii, daha kolay, ama jdbc ile ilgili şeyleri basit Tarih işleme ile karıştırmayı sevmiyorum, en azından IMHO.
Enrico Giurin

9
Üzgünüm, bu basit bir soruna korkunç bir çözüm. 5'i Olimpiyat logosundaki halka sayısına eşit olarak tanımlamak gibi.
Madbreaks

7

yeni API LocalDateTime'dan java.util.date dosyasına dönüştürürken aşağıdaki işe yarar:

Date.from(ZonedDateTime.of({time as LocalDateTime}, ZoneId.systemDefault()).toInstant());

ters dönüşüm (umarım) benzer şekilde elde edilebilir ...

Umarım yardımcı olur...


5

Her şey burada: http://blog.progs.be/542/date-to-java-time

"Yuvarlak açma" ile cevap kesin değildir:

LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);

sistem saat diliminiz UTC / GMT değilse saati değiştirirsiniz!


Sadece yılda 1 saat boyunca, sonbaharda, LocalDateTime'dan iki kez üst üste geldiğinde yaparsanız. Yay ileri hareketi bir soruna neden olmaz. Çoğu zaman her iki yönü de düzgün bir şekilde dönüştürür.
David


3

Bunun en basit veya en iyi yol olup olmadığından veya herhangi bir tuzak olup olmadığından emin değilim, ama işe yarıyor:

static public LocalDateTime toLdt(Date date) {
    GregorianCalendar cal = new GregorianCalendar();
    cal.setTime(date);
    ZonedDateTime zdt = cal.toZonedDateTime();
    return zdt.toLocalDateTime();
}

static public Date fromLdt(LocalDateTime ldt) {
    ZonedDateTime zdt = ZonedDateTime.of(ldt, ZoneId.systemDefault());
    GregorianCalendar cal = GregorianCalendar.from(zdt);
    return cal.getTime();
}

3
Giden bir sıkan kesinlikle var LocalDateTimeiçin Date. Yaz saati geçişlerinde, a LocalDateTimemevcut olmayabilir veya iki kez olabilir. Her durumda ne olmasını istediğinizi bulmanız gerekir.
Jon Skeet

1
BTW, GregorianCalendaryeni java.timeAPI'nin değiştirmeyi amaçladığı eski garip API'ye ait
Vadzim

3

Eğer android ve threetenbp kullanıyorsanız kullanabilirsinizDateTimeUtils bunun yerine .

örn:

Date date = DateTimeUtils.toDate(localDateTime.atZone(ZoneId.systemDefault()).toInstant());

Date.fromyalnızca api 26 ve sonraki sürümlerinde desteklendiğinden kullanamazsınız

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.