How can I best convert a java.util.Date
to a Java 8 java.time.YearMonth
?
Unfortunately the following throws a DateTimeException
:
YearMonth yearMonth = YearMonth.from(date.toInstant());
results in:
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)
...
I need this functionality since I want to store YearMonth
values in a database using JPA. Currently JPA does not support YearMonth
's, so I've come up with the following YearMonthConverter (imports omitted):
// 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);
}
}
Isn't there a better (cleaner, shorter) solution (for both directions)?
The Java Date Time API was added from Java version 8. instant() method of Clock class returns a current instant of Clock object as Instant Class Object. Instant generates a timestamp to represent machine time. So this method generates a timestamp for clock object.
In case you want it in string, SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String formattedDate = sdf. format(new Date()); where new Date() is todays date, whatever date object you put, the sdf object ( java.
The standard alternate is using the Calendar Object. Calendar has one dangerous point (for the unwary) and that is the after / before methods. They take an Object but will only handle Calendar Objects correctly. Be sure to read the Javadoc for these methods closely before using them.
You can make use of the following DateFormat. SimpleDateFormat myDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); myDate. setTimeZone(TimeZone. getTimeZone("UTC")); Date newDate = myDate.
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:
YEAR
and MONTH_OF_YEAR
fields, orLocalDate
. 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With