Bir java.util.Date
nesnesini yeni JDK 8/JSR-310 java.time.LocalDate
nesnesine dönüştürmenin en iyi yolu nedir?
Date input = new Date();
LocalDate date = ???
Kısa cevap
Date input = new Date();
LocalDate date = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
Açıklama
Adına rağmen, java.util.Date
bir "date" değil, zaman çizgisi üzerinde bir anı temsil eder. Nesne içinde saklanan gerçek veri, 1970-01-01T00:00Z (1970 GMT/UTC başlangıcında gece yarısı) tarihinden bu yana geçen milisaniyelerin long
sayısıdır.
JSR-310'daki java.util.Date
sınıfına eşdeğer sınıf Instant
tır, bu nedenle dönüşümü sağlamak için uygun bir toInstant()
yöntemi vardır:
Date input = new Date();
Instant instant = input.toInstant();
Bir java.util.Date
örneğinin zaman dilimi kavramı yoktur. Bir java.util.Date
üzerinde toString()
işlevini çağırırsanız bu garip görünebilir, çünkü toString
bir zaman dilimine görelidir. Ancak bu yöntem aslında dizeyi sağlamak için Java'nın varsayılan zaman dilimini anında kullanır. Zaman dilimi java.util.Date
in gerçek durumunun bir parçası değildir.
Bir Instant
ayrıca zaman dilimi hakkında herhangi bir bilgi içermez. Bu nedenle, bir Anlık
tan yerel bir tarihe dönüştürmek için bir zaman dilimi belirtmek gerekir. Bu varsayılan bölge olabilir - ZoneId.systemDefault()
- ya da kullanıcı tercihlerindeki bir zaman dilimi gibi uygulamanızın kontrol ettiği bir zaman dilimi olabilir. Zaman dilimini uygulamak için atZone()
yöntemini kullanın:
Date input = new Date();
Instant instant = input.toInstant();
ZonedDateTime zdt = instant.atZone(ZoneId.systemDefault());
Bir ZonedDateTime
, yerel tarih ve saat, zaman dilimi ve GMT/UTC'den ofsetten oluşan durumu içerir. Bu nedenle tarih - LocalDate
- toLocalDate()
kullanılarak kolayca çıkarılabilir:
Date input = new Date();
Instant instant = input.toInstant();
ZonedDateTime zdt = instant.atZone(ZoneId.systemDefault());
LocalDate date = zdt.toLocalDate();
Java 9 cevabı
Java SE 9'da, bu görevi biraz basitleştiren bir yeni yöntem eklenmiştir:
Date input = new Date();
LocalDate date = LocalDate.ofInstant(input.toInstant(), ZoneId.systemDefault());
Bu yeni alternatif daha doğrudan, daha az çöp yaratıyor ve bu nedenle daha iyi performans göstermesi gerekiyor.
Daha iyi bir yol:
Date date = ...;
Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate()
Bu versiyonun avantajları:
Girdinin java.util.Date
veya onun alt sınıfı olan java.sql.Date
örneği olmasına bakılmaksızın çalışır (@JodaStephen'in yönteminden farklı olarak). Bu, JDBC kaynaklı verilerde yaygındır. java.sql.Date.toInstant()` her zaman bir istisna atar.
JSR-310 backport ile JDK8 ve JDK7 için aynıdır
Ben şahsen bir yardımcı sınıf kullanıyorum (ancak bu backport uyumlu değil):
/**
* 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);
}
}
Buradaki asLocalDate()
yöntemi null-safe'dir, girdi java.sql.Date
ise toLocalDate()
yöntemini kullanır (zaman dilimi sorunlarını veya gereksiz hesaplamaları önlemek için JDBC sürücüsü tarafından geçersiz kılınabilir), aksi takdirde yukarıda belirtilen yöntemi kullanır.
Eğer Java 8 kullanıyorsanız, @JodaStephen'in cevabı kesinlikle en iyisidir. Ancak, JSR-310 backport ile çalışıyorsanız, maalesef böyle bir şey yapmak zorundasınız:
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));