Cách tốt nhất để chuyển đổi một java.util.Date
đối tượng sang JDK 8 / JSR-310 mới là java.time.LocalDate
gì?
Date input = new Date();
LocalDate date = ???
Cách tốt nhất để chuyển đổi một java.util.Date
đối tượng sang JDK 8 / JSR-310 mới là java.time.LocalDate
gì?
Date input = new Date();
LocalDate date = ???
Câu trả lời:
Câu trả lời ngắn
Date input = new Date();
LocalDate date = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
Giải trình
Mặc dù tên của nó, java.util.Date
đại diện cho một tức thời trên dòng thời gian, không phải là "ngày". Dữ liệu thực tế được lưu trữ trong đối tượng là một long
phần nghìn giây kể từ 1970-01-01T00: 00Z (nửa đêm khi bắt đầu 1970 GMT / UTC).
Lớp tương đương với java.util.Date
trong JSR-310 là Instant
, do đó có một phương thức thuận tiện toInstant()
để cung cấp chuyển đổi:
Date input = new Date();
Instant instant = input.toInstant();
Một java.util.Date
thể hiện không có khái niệm về múi giờ. Điều này có vẻ lạ nếu bạn gọi toString()
trên a java.util.Date
, vì toString
nó liên quan đến múi giờ. Tuy nhiên, phương thức đó thực sự sử dụng múi giờ mặc định của Java một cách nhanh chóng để cung cấp chuỗi. Múi giờ không phải là một phần của trạng thái thực tế java.util.Date
.
An Instant
cũng không chứa bất kỳ thông tin nào về múi giờ. Vì vậy, để chuyển đổi từ Instant
ngày sang ngày cục bộ, cần chỉ định múi giờ. Đây có thể là vùng mặc định - ZoneId.systemDefault()
- hoặc có thể là múi giờ mà ứng dụng của bạn kiểm soát, chẳng hạn như múi giờ từ tùy chọn của người dùng. Sử dụng atZone()
phương pháp để áp dụng múi giờ:
Date input = new Date();
Instant instant = input.toInstant();
ZonedDateTime zdt = instant.atZone(ZoneId.systemDefault());
A ZonedDateTime
chứa trạng thái bao gồm ngày và giờ địa phương, múi giờ và phần bù từ GMT / UTC. Như vậy ngày - LocalDate
- có thể dễ dàng trích xuất bằng cách sử dụng toLocalDate()
:
Date input = new Date();
Instant instant = input.toInstant();
ZonedDateTime zdt = instant.atZone(ZoneId.systemDefault());
LocalDate date = zdt.toLocalDate();
Câu trả lời Java 9
Trong Java SE 9, một phương thức mới đã được thêm vào để đơn giản hóa nhiệm vụ này:
Date input = new Date();
LocalDate date = LocalDate.ofInstant(input.toInstant(), ZoneId.systemDefault());
Sự thay thế mới này trực tiếp hơn, tạo ra ít rác hơn và do đó sẽ hoạt động tốt hơn.
Date
không có khái niệm về múi giờ, Instant
cũng không chứa thông tin về múi giờ. Các LocalDate
API cho biết "Ngày Một mà không có một múi giờ". Vậy tại sao chuyển đổi từ Date
để Instant
đến LocalDate
nhu cầu atZone(ZoneId.systemDefault())
?
LocalDate
và LocalDateTime
không "lưu trữ hoặc đại diện cho thời gian hoặc múi giờ" (ref: javadocs). Mặc dù họ không lưu trữ nó - các lớp đại diện cho một Local
ngày và / hoặc thời gian, do đó việc chuyển đổi thành ngày / giờ địa phương ngụ ý múi giờ.
Cách tốt hơn là:
Date date = ...;
Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate()
Ưu điểm của phiên bản này:
hoạt động bất kể đầu vào là một thể hiện của java.util.Date
hoặc đó là một lớp con của java.sql.Date
(không giống như cách của @ JodaStephen). Điều này là phổ biến với dữ liệu có nguồn gốc JDBC. java.sql.Date.toInstant()
luôn luôn ném một ngoại lệ.
JDK8 và JDK7 cũng tương tự với backport JSR-310
Cá nhân tôi sử dụng một lớp tiện ích (nhưng nó không tương thích với 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);
}
}
Các asLocalDate()
phương pháp ở đây là null-an toàn, sử dụng toLocalDate()
, nếu đầu vào là java.sql.Date
(nó có thể được overriden bởi trình điều khiển JDBC để vấn đề tránh múi giờ hay tính toán không cần thiết), nếu không sử dụng phương pháp nêu trên.
DateConvertUtils
.
Date.toInstant()
.
LocalDate localDate = LocalDate.parse( new SimpleDateFormat("yyyy-MM-dd").format(date) );
SimpleDateFormat
thể hiện được giới hạn trong luồng hiện tại. Nó được sử dụng một cách an toàn chủ đề. Bây giờ, SimpleDateFormat
được cho là 'tốn kém để khởi tạo' (trên tài khoản của tất cả các cấu trúc dữ liệu nội bộ mà nó cần) nhưng bạn không thể chia sẻ một cái như một 'đơn lẻ' (mà không đồng bộ hóa quyền truy cập vào nó), vì nó thực sự không phải là luồng an toàn (Một ThreadLocal
giải pháp có thể hoạt động nếu mã 'gây ô nhiễm', Thread
trong đó điều này chịu trách nhiệm cho vòng đời của luồng ... nhưng điều đó hiếm khi xảy ra). Lúng túng. Tránh SimpleDateFormat
là những lý do cho việc sử dụng javax.time
.
SimpleDateFormat
(bị vứt đi), chuỗi trung gian (bị vứt đi) và chi phí phân tích cú pháp. Đó là một giải pháp, nhưng không được khuyến khích.
Nếu bạn đang sử dụng Java 8, câu trả lời của @ JodaStephen rõ ràng là tốt nhất. Tuy nhiên, nếu bạn đang làm việc với backport JSR-310 , bạn không may phải làm một cái gì đó như thế này:
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();
Bạn có thể chuyển đổi trong một dòng:
public static LocalDate getLocalDateFromDate(Date date){
return LocalDate.from(Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()));
}
đầu tiên, thật dễ dàng để chuyển đổi một Ngày thành Tức thì
Instant timestamp = new Date().toInstant();
Sau đó, bạn có thể chuyển đổi Api tức thì sang bất kỳ ngày nào trong jdk 8 bằng phương thức ofInstant ():
LocalDateTime date = LocalDateTime.ofInstant(timestamp, ZoneId.systemDefault());
import java.sql.Date
trong tệp của mình: toInstant()
phương pháp java.sql.Date
luôn luôn ném.
Date input = new Date();
LocalDateTime conv=LocalDateTime.ofInstant(input.toInstant(), ZoneId.systemDefault());
LocalDate convDate=conv.toLocalDate();
Các Date
ví dụ có chứa thời gian quá cùng với ngày trong khi LocalDate
không. Vì vậy, trước tiên bạn có thể chuyển đổi nó thành LocalDateTime
sử dụng phương thức của nó ofInstant()
sau đó nếu bạn muốn nó mà không có thời gian thì chuyển đổi thể hiện thành LocalDate
.
public static LocalDate Date2LocalDate(Date date) {
return LocalDate.parse(date.toString(), DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy"))
định dạng này là từ 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();
}
Tôi đã gặp vấn đề với việc triển khai của @ JodaStephen trên JBoss EAP 6. Vì vậy, tôi đã viết lại chuyển đổi theo Hướng dẫn Java của Oracle trong 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();
Có gì sai với 1 dòng đơn giản này?
new LocalDateTime(new Date().getTime()).toLocalDate();
Tôi đã giải quyết câu hỏi này bằng giải pháp dưới đây
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);
Trong trường hợp này localDate in ngày của bạn ở định dạng này "yyyy-MM-dd"
java.time
lớp trong JDK8, không phải với Joda Time.
LocalDate.from(Instant.ofEpochMilli(date.getTime()))
nghĩ rằng nó tương đương với bạn, nhưng trực tiếp hơn.