2015-01-08 24 views
9

Làm cách nào để chuyển đổi tốt nhất một java.util.Date sang Java 8 java.time.YearMonth?Làm thế nào để chuyển đổi java.util.Date sang Java8 java.time.YearMonth

Thật không may là sau ném một DateTimeException:

YearMonth yearMonth = YearMonth.from(date.toInstant()); 

kết quả trong:

java.time.DateTimeException: Unable to obtain YearMonth from TemporalAccessor: 2015-01-08T14:28:39.183Z of type java.time.Instant 
at java.time.YearMonth.from(YearMonth.java:264) 
... 

tôi cần chức năng này vì tôi muốn để lưu trữ YearMonth giá trị trong một cơ sở dữ liệu sử dụng JPA. Hiện nay JPA không hỗ trợ YearMonth 's, vì vậy tôi đã đi lên với các YearMonthConverter sau (nhập khẩu bỏ qua):

// TODO (future): delete when next version of JPA (i.e. Java 9?) supports YearMonth. See https://java.net/jira/browse/JPA_SPEC-63 
@Converter(autoApply = true) 
public class YearMonthConverter implements AttributeConverter<YearMonth, Date> { 

    @Override 
    public Date convertToDatabaseColumn(YearMonth attribute) { 
    // uses default zone since in the end only dates are needed 
    return attribute == null ? null : Date.from(attribute.atDay(1).atStartOfDay(ZoneId.systemDefault()).toInstant()); 
    } 

    @Override 
    public YearMonth convertToEntityAttribute(Date dbData) { 
    // TODO: check if Date -> YearMonth can't be done in a better way 
    if (dbData == null) return null; 
    Calendar calendar = Calendar.getInstance(); 
    calendar.setTime(dbData); 
    return YearMonth.of(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1); 
    } 
} 

Không phải là có một tốt hơn (sạch hơn, ngắn hơn) giải pháp (cho cả hai hướng)?

+1

Cá nhân tôi sẽ tránh lập bản đồ cho 'java.util.Date 'vì a) nó không phải là' java.sql. * '-type, b) bao gồm múi giờ ngầm định (hiệu suất kém và sử dụng mờ của múi giờ hệ thống), c) SQL không định nghĩa kiểu như vậy cho năm-tháng. Bản đồ tốt hơn 'YearMonth' thành số nguyên sử dụng [PROLEPTIC_MONTH] (http://docs.oracle.com/javase/8/docs/api/java/time/temporal/ChronoField.html#PROLEPTIC_MONTH) –

+0

@MenoHochschild: Đó là một ý tưởng hay - chỉ báo trước là nó không thể đọc được khi nhìn vào cơ sở dữ liệu. Không có vấn đề gì trong trường hợp của tôi ... – Sebastian

+0

Vâng, chỉ một số nguyên không thể đọc được để xem trực tiếp nội dung của db, nhưng lưu ý rằng, ánh xạ tới juDate có thể thay đổi ngày và tháng được hiển thị (khi quản trị viên db nhìn thấy nó) do múi giờ chuyển đổi và db/cài đặt máy chủ. –

Trả lời

13

Câu trả lời ngắn:

// From Date to YearMonth 
YearMonth yearMonth = 
     YearMonth.from(date.toInstant() 
          .atZone(ZoneId.systemDefault()) 
          .toLocalDate()); 

// From YearMonth to Date 
// The same as the OP:s answer 
final Date convertedFromYearMonth = 
     Date.from(yearMonth.atDay(1).atStartOfDay(ZoneId.systemDefault()).toInstant()); 

Giải thích:

Các javadoc của YearMonth.from(TemporalAccessor) -method nói:

Việc quy đổi chiết xuất các lĩnh vực NĂM và MONTH_OF_YEAR. Việc khai thác chỉ được phép nếu đối tượng thời gian có một niên đại ISO, hoặc có thể được chuyển đổi thành một LocalDate.

Vì vậy, bạn cần phải hoặc là có thể:

  1. chiết xuất các YEARMONTH_OF_YEAR lĩnh vực, hoặc
  2. bạn nên sử dụng một cái gì đó có thể được chuyển đổi sang một LocalDate.

Hãy dùng thử!

final Date date = new Date(); 
final Instant instant = date.toInstant(); 
instant.get(ChronoField.YEAR); // causes an error 

này là không thể, một ngoại lệ được ném:

java.time.temporal.UnsupportedTemporalTypeException: lĩnh vực được hỗ trợ: Năm tại java.time.Instant.get (Instant.java:571) ...

Điều này có nghĩa là phương án 1 sẽ chuyển ra ngoài cửa sổ. Lý do được giải thích trong câu trả lời tuyệt vời này về how to convert Date to LocalDate.

Mặc dù tên của nó, java.util.Date biểu thị tức thì trên dòng thời gian chứ không phải là "ngày". Dữ liệu thực tế được lưu trữ trong đối tượng là số lượng mili giây dài từ 1970-01-01T00: 00Z (nửa đêm vào đầu năm 1970 GMT/UTC).

Lớp tương đương với java.util.Date trong JSR-310 là Instant, do đó, có phương thức thuận tiện toInstant() để cung cấp chuyển đổi.

Vì vậy, Date có thể được chuyển đổi thành Instant nhưng điều đó không giúp ích cho chúng tôi, phải không?

Phương án 2 tuy nhiên chứng tỏ là thành công. Chuyển đổi Instant thành LocalDate và sau đó sử dụng phương pháp YearMonth.from(TemporalAccessor).

Date date = new Date(); 

    LocalDate localDate = date.toInstant() 
           .atZone(ZoneId.systemDefault()) 
           .toLocalDate(); 

    YearMonth yearMonth = YearMonth.from(localDate); 
    System.out.println("YearMonth: " + yearMonth); 

Đầu ra là (từ mã đã được thực hiện vào tháng giêng năm 2015;):

nămTháng: 2015-01

Các vấn đề liên quan