Chuyển đổi java.util.Date thành java.time.LocalDate


494

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.LocalDategì?

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

Câu trả lời:


828

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 longphầ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.Datetrong 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.Datethể 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ì toStringnó 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 Instantcũng không chứa bất kỳ thông tin nào về múi giờ. Vì vậy, để chuyển đổi từ Instantngà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 ZonedDateTimechứ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.


14
Tôi đã 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.
Marko Topolnik

9
@MarkoTopolnik biên dịch nhưng không chạy. Một Instant không chứa múi giờ do đó không có cách nào để lấy LocalDate.
JodaStephen

11
@assylias Chỉ cần sử dụng sqlDate.toLocalDate ()!
JodaStephen

25
@JodaStephen Datekhông có khái niệm về múi giờ, Instantcũng không chứa thông tin về múi giờ. Các LocalDateAPI 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 LocalDatenhu cầu atZone(ZoneId.systemDefault())?
Gustavo

8
@Gustavo: LocalDateLocalDateTimekhô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 Localngà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ờ.
Cuga

158

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.Datehoặ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.


12
Nếu đây là cách tốt hơn, nó rất xấu và dài dòng. Rất đau khổ.
ceklock

3
Nó là tốt hơn so với câu trả lời được chấp nhận, tôi giải thích tại sao. Vâng, nó là xấu xí, nhưng đó là lý do tại sao đã viết DateConvertUtils.
Oliv

Tôi không hiểu tại sao họ không triển khai lớp chuyển đổi trong API mới.
ceklock

@ceklock, họ đã thực hiện, không phải là một lớp chuyển đổi, mà là một vài phương thức chuyển đổi, như thế nào Date.toInstant().
Ole VV

2
Có thư viện Kotlin đóng gói chúng dưới dạng các hàm mở rộng để nhà phát triển có thể thực hiện các thao tác sau. Ngày x = ...; x.asLocalDate ();
Mark Ashworth

20
LocalDate localDate = LocalDate.parse( new SimpleDateFormat("yyyy-MM-dd").format(date) );

7
Ở đây, SimpleDateFormatthể 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ì thực sự không phải là luồng an toàn (Một ThreadLocalgiải pháp có thể hoạt động nếu mã 'gây ô nhiễm', Threadtrong đó đ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 SimpleDateFormatnhững lý do cho việc sử dụng javax.time.
David Bullock

6
Cách tiếp cận này có rất nhiều chi phí: 'đắt tiền' 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.
David Bullock

2
@ceklock nhưng sau đó bạn gặp phải vấn đề là nó không còn an toàn cho
chuỗi

4
@ceklock SimpleDateFormat chỉ có thể xử lý một ngày tại một thời điểm, nếu bạn sử dụng đồng thời, nó sẽ mang lại kết quả thu được. Vì vậy, có, nó là quan trọng. Đừng tạo một phiên bản SimpleDateFormat trong một biến toàn cục.
flup

19

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

4
Không đúng, câu trả lời của @ JodaStephen vẫn hoạt động. Bạn chỉ cần một cách khác để chuyển đổi java.util.Date thành Instant. Để làm điều này, bạn có thể sử dụng org.threeten.bp.DateTimeUtils.toInstant: threeten.org/threetenbp/apidocs/org/threeten/bp/ trộm
Christian Ciach

3
DateTimeUtils không khả dụng khi tôi đang sử dụng backport, nhưng bạn đúng là nó có sẵn cho bất kỳ ai sử dụng ThreeTen Backport 1.0 trở lên. Cảm ơn đã chỉ ra điều đó.
dhalsim2 18/03/2016

14
LocalDate ld = new java.sql.Date( new java.util.Date().getTime() ).toLocalDate();

1
Và nó nhận ra đơn giản nhất: LocalDate.of (getYear () + 1900, getMonth () + 1, getDate ())
Grigory Kislin 7/1/2015

Các dòng nói không tán thành: |
TheRealChx101

8

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

Tôi gặp lỗi như thế này khi sử dụng phương thức này: java.time.DateTimeException: Không thể lấy LocalDate từ TemporalAccessor: 2018-01-31T11: 54: 27.964Z loại java.time.Instant
Luigi Rubino

@LuigiRubino cảm ơn vì đã chỉ ra. Xin vui lòng xem câu trả lời cập nhật. Tôi quên thêm Khu vực trước đó.
Sahil Chhabra

8

đầ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()); 

bao gồm localDate, localDateTime, localTime
Shedom Wei

Có nghĩa là gì để chỉ định ZoneId ở đây. Nếu tôi nhận được Ngày -> Tức thì từ API và tôi có thể nghĩ về nó dưới dạng mili giây từ năm 1970 trong UTC. Khi tôi chỉ muốn nhận LocalDate tương ứng (yyyy-mm-dd) từ phối cảnh của api không được chuyển đổi thành bất kỳ múi giờ nào, tôi không nên sử dụng Zone Offerset.UTC để không có phần bù tức thời này cho múi giờ địa phương của mình. Không phải là ví dụ của bạn với ZoneId.systemDefault () đang thay đổi máy chủ biểu mẫu ngày. Ví dụ. tức thì đại diện cho 2018-10-20 0:00 và sau đó chuyển từ UTC sang Mỹ / Los_Angele tôi nhận được vào Ngày địa phương 2018-10-19 thay vì 2018-10-20?
Michał Ziobro

Nó bị hỏng nếu bạn tình cờ có import java.sql.Datetrong tệp của mình: toInstant()phương pháp java.sql.Dateluôn luôn ném.
Oliv

4
Date input = new Date();
LocalDateTime  conv=LocalDateTime.ofInstant(input.toInstant(), ZoneId.systemDefault());
LocalDate convDate=conv.toLocalDate();

Các Dateví dụ có chứa thời gian quá cùng với ngày trong khi LocalDatekhông. Vì vậy, trước tiên bạn có thể chuyển đổi nó thành LocalDateTimesử 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.


1
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();
    }

0

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

-2

Có gì sai với 1 dòng đơn giản này?

new LocalDateTime(new Date().getTime()).toLocalDate();

Java phàn nàn rằng nó không thể gọi toLocalDate () trên kiểu nguyên thủy dài. Tôi đã sử dụng một đối tượng Ngày thực tế; có lẽ có chà?
TheGeeko61

-8

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"


1
Câu hỏi đang tìm kiếm một giải pháp bằng cách sử dụng các java.timelớp trong JDK8, không phải với Joda Time.
pioto
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.