Java8 java.util.Date conversion to java.time.ZonedDateTime
Asked Answered
R

4

107

I am getting the following exception while trying to convert java.util.Date to java.time.LocalDate.

java.time.DateTimeException: Unable to obtain ZonedDateTime from TemporalAccessor: 2014-08-19T05:28:16.768Z of type java.time.Instant

The code is as follow:

public static Date getNearestQuarterStartDate(Date calculateFromDate){

    int[] quaterStartMonths={1,4,7,10};     
    Date startDate=null;

    ZonedDateTime d=ZonedDateTime.from(calculateFromDate.toInstant());
    int frmDateMonth=d.getMonth().getValue();

Is there something wrong in the way I am using the ZonedDateTime class?

As per documentation, this should convert a java.util.Date object to ZonedDateTime. The date format above is standard Date?

Do I have to fallback on Joda time?

If someone could provide some suggestion, it would be great.

Recurve answered 19/8, 2014 at 5:31 Comment(0)
F
147

To transform an Instant to a ZonedDateTime, ZonedDateTime offers the method ZonedDateTime.ofInstant(Instant, ZoneId). So

So, assuming you want a ZonedDateTime in the default timezone, your code should be

ZonedDateTime d = ZonedDateTime.ofInstant(calculateFromDate.toInstant(),
                                          ZoneId.systemDefault());
Fowler answered 19/8, 2014 at 6:12 Comment(3)
What if I want to convert it to the same timezone? Date is always UTC I believe, so I can just do ZoneId.UTC?Lefthanded
A Date doesn't have a timezone at all. It's just a number of millisecods elapsed since a precise moment (1970-01-01T00:00 UTC). There is no "same" timezone. If you want the ZonedDateTime to be in the UTC timezone, you must pass ZoneOffset.UTC.Fowler
@DidierA. A Date is similar to an Instant, which are both UNIX timestamps. Also, it is common practice to say that UNIX timestamps are UTC time zone.Ops
O
44

To obtain a ZonedDateTime from a Date you can use:

calculateFromDate.toInstant().atZone(ZoneId.systemDefault())

You can then call the toLocalDate method if you need a LocalDate. See also: Convert java.util.Date to java.time.LocalDate

Obregon answered 19/8, 2014 at 6:36 Comment(2)
The above solution also works out for me and the conversion to LocalDate was very useful. One question, from design perspective ZonedDateTime.from() should behave as expected as long as it is provided with an java.time.Instant. In this case, it is not as expected. Is this some kind of inconsistency?Recurve
ZonedDateTime.from() requires a ZoneId, but Instant does not have one. As such, ZonedDateTime.from(instant) throws an exception.Meliamelic
W
17

The Answer by assylias and the Answer by JB Nizet are both correct:

  1. Call the new conversion method added to the legacy class, java.util.Date::toInstant.
  2. Call Instant::atZone, passing a ZoneId, resulting in a ZonedDateTime.

enter image description here

But your code example is aimed at quarters. For that, read on.

Quarters

No need to roll-your-own handling of quarters. Use a class already written and tested.

org.threeten.extra.YearQuarter

The java.time classes are extended by the ThreeTen-Extra project. Among the many handy classes provided in that library you will find Quarter and YearQuarter.

First get your ZonedDateTime.

ZonedId z = ZoneID.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = myJavaUtilDate.toInstant().atZone( z ) ;

Determine the year-quarter for that particular date.

YearQuarter yq = YearQuarter.from( zdt ) ;

Next we need the start date of that quarter.

LocalDate quarterStart = yq.atDay( 1 ) ;

While I do not necessarily recommend doing so, you could use a single line of code rather than implement a method.

LocalDate quarterStart =                    // Represent a date-only, without time-of-day and without time zone.
    YearQuarter                             // Represent a specific quarter using the ThreeTen-Extra class `org.threeten.extra.YearQuarter`. 
    .from(                                  // Given a moment, determine its year-quarter.
        myJavaUtilDate                      // Terrible legacy class `java.util.Date` represents a moment in UTC as a count of milliseconds since the epoch of 1970-01-01T00:00:00Z. Avoid using this class if at all possible.
        .toInstant()                        // New method on old class to convert from legacy to modern. `Instant` represents a moment in UTC as a count of nanoseconds since the epoch of 1970-01-01T00:00:00Z. 
        .atZone(                            // Adjust from UTC to the wall-clock time used by the people of a particular region (a time zone). Same moment, same point on the timeline, different wall-clock time.
            ZoneID.of( "Africa/Tunis" )     // Specify a time zone using proper `Continent/Region` format. Never use 2-4 letter pseudo-zone such as `PST` or `EST` or `IST`. 
        )                                   // Returns a `ZonedDateTime` object.
    )                                       // Returns a `YearQuarter` object.
    .atDay( 1 )                             // Returns a `LocalDate` object, the first day of the quarter. 
;

By the way, if you can phase out your use of java.util.Date altogether, do so. It is a terrible class, along with its siblings such as Calendar. Use Date only where you must, when you are interfacing with old code not yet updated to java.time.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Woodley answered 21/2, 2019 at 5:41 Comment(0)
B
0

The answer didn't work for me on Java 10 storing util.Date in UTC.

Date.toInstant() seems to convert the EpochMillis into the local time zone of the server.

ZDT.ofInstant(instant, zoneId) and instant.atZone(zoneId) seem to just tag on a TZ on the instant, but it's already messed up with.

I couldn't find a way to prevent Date.toInstant() from messing with the UTC time with the system time zone.

The only way I found to work around this was to go through the sql.Timestamp class:

new java.sql.Timestamp(date.getTime()).toLocalDateTime()
                                      .atZone(ZoneId.of("UTC"))
                                      .withZoneSameInstant(desiredTZ)
Burden answered 21/2, 2019 at 1:37 Comment(8)
No, there is no conversion with Date::toInstant. Both a java.util.Date and a java.time.Instant are in UTC, by definition, always in UTC. Date represents a count of milliseconds since the epoch of 1970-01-01T00:00:00Z, while Instant is also a count since the same epoch but in finer resolution of nanoseconds.Woodley
LocalDateTime is exactly the wrong class to be using here. That class purposely lacks any concept of time zone or offset-from-UTC. As such, it is incapable of representing a moment, as noted in its class JavaDoc. Converting to a LocalDateTime is discarding valuable information with nothing gained. This Answer is misleading and incorrect. See the correct Answer by assylias. All you need to get a ZonedDateTime from a Date is: myJavaUtilDate.toInstant().atZone( desiredZoneIdGoesHere ).Woodley
Basil, I 100% agree with your expectation but this is sadly not how we witnessed Java 10 behave with a UTC Date in a PST JVM. We're only using the LocalDateTime here as an intermediate var to not get the UTC time messed up before we tag a TZ on it.Burden
There is no reason to ever use java.sql.Timestamp again, now replaced by java.sql.OffsetDateTime -- as of JDBC 4.2 we can directly exchange some java.time types with the database.Woodley
I suggest that instead of posting this Answer, you post your own Question delineating the problem you encountered, along with example code. We should be able to sort that out for you.Woodley
Regarding your “PST JVM”… You can, and should, write code with java.time that never relies on your JVM’s current default time zone. Instead, you should generally work in UTC (java.time.Instant, or OffsetDateTime set to ZoneOffset.UTC), and always pass the optional ZoneId/ZoneOffset. Another issue, never use the pseudo-time zone “PST”. Proper time zone names are in Continent/Region format, such as America/Los_Angeles. See Wikipedia for list.Woodley
Basil, the question was about converting a util.Date so I was answering that. java.time types are better I agree but that was not the question again. I think the issue we experienced with util.Date.toInstant() is relevant and will be helpful to others who run into it. If you can work exclusively with java.time types than good for you. No need to troll here.Burden
No trolling here. In my suggestion for you to post a Question, I was sincerely offering to work out a solution to the problem you encountered. I suspect the problem and solution would be interesting and valuable to others.Woodley

© 2022 - 2024 — McMap. All rights reserved.