How to convert java.util.Date to Java8 java.time.YearMonth

后端 未结 1 696
野趣味
野趣味 2021-02-19 07:03

How can I best convert a java.util.Date to a Java 8 java.time.YearMonth?

Unfortunately the following throws a DateTimeException:

1条回答
  •  挽巷
    挽巷 (楼主)
    2021-02-19 07:40

    Short answer:

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

    Explanation:

    The JavaDoc of the YearMonth.from(TemporalAccessor)-method says:

    The conversion extracts the YEAR and MONTH_OF_YEAR fields. The extraction is only permitted if the temporal object has an ISO chronology, or can be converted to a LocalDate.

    So, you need to either be able to:

    1. extract the YEAR and MONTH_OF_YEAR fields, or
    2. you should use something that can be converted to a LocalDate.

    Lets try it!

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

    This is not possible, an exception is thrown:

    java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: Year at java.time.Instant.get(Instant.java:571) ...

    This means that alternative 1 goes out the window. The reason for is explained in this excellent answer about how to convert Date to LocalDate.

    Despite its name, java.util.Date represents an instant on the time-line, not a "date". The actual data stored within the object is a long count of milliseconds since 1970-01-01T00:00Z (midnight at the start of 1970 GMT/UTC).

    The equivalent class to java.util.Date in JSR-310 is Instant, thus there is a convenient method toInstant() to provide the conversion.

    So, a Date can be converted to an Instant but that did not help us, did it?

    Alternative 2 however proves to be successful. Convert the Instant to a LocalDate and then use the YearMonth.from(TemporalAccessor)-method.

        Date date = new Date();
    
        LocalDate localDate = date.toInstant()
                                  .atZone(ZoneId.systemDefault())
                                  .toLocalDate();
    
        YearMonth yearMonth = YearMonth.from(localDate);
        System.out.println("YearMonth: " + yearMonth);
    

    The output is (since the code was executed in January 2015 ;):

    YearMonth: 2015-01

    0 讨论(0)
提交回复
热议问题