82

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.

assylias
  • 297,541
  • 71
  • 621
  • 741
Ironluca
  • 2,638
  • 2
  • 20
  • 30

4 Answers4

117

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());
Ahmed Ashour
  • 4,209
  • 10
  • 29
  • 46
JB Nizet
  • 633,450
  • 80
  • 1,108
  • 1,174
  • Oops. Thanks. Fixed now. – JB Nizet Aug 19 '14 at 06:33
  • What if I want to convert it to the same timezone? Date is always UTC I believe, so I can just do ZoneId.UTC? – Didier A. Oct 26 '15 at 18:20
  • 7
    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. – JB Nizet Oct 26 '15 at 18:56
  • 2
    @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. – kevinarpe Oct 04 '17 at 11:04
42

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

Community
  • 1
  • 1
assylias
  • 297,541
  • 71
  • 621
  • 741
  • 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? – Ironluca Aug 20 '14 at 06:08
  • 3
    ZonedDateTime.from() requires a ZoneId, but Instant does not have one. As such, ZonedDateTime.from(instant) throws an exception. – JodaStephen Aug 20 '14 at 22:29
9

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.

Basil Bourque
  • 218,480
  • 72
  • 657
  • 915
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)
TheArchitect
  • 2,061
  • 1
  • 12
  • 16
  • 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. – Basil Bourque Feb 21 '19 at 05:17
  • `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](https://stackoverflow.com/a/25377049/642706) by assylias. All you need to get a `ZonedDateTime` from a `Date` is: `myJavaUtilDate.toInstant().atZone( desiredZoneIdGoesHere )`. – Basil Bourque Feb 21 '19 at 05:24
  • 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. – TheArchitect Feb 21 '19 at 17:18
  • 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. – Basil Bourque Feb 21 '19 at 18:32
  • 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. – Basil Bourque Feb 21 '19 at 18:33
  • 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](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). – Basil Bourque Feb 21 '19 at 18:36
  • 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. – TheArchitect Feb 21 '19 at 22:06
  • 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. – Basil Bourque Feb 21 '19 at 22:17