日期转换为 java.time. ZonedDateTime

在尝试将 java.util.Date转换为 java.time.LocalDate时,出现了以下异常。

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

密码如下:

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

我使用 ZonedDateTime类的方式有什么问题吗?

根据文档,这应该将 java.util.Date对象转换为 ZonedDateTime。上面的日期格式是标准日期?

我必须要依靠 Joda 的时间吗?

如果有人能提供一些建议,那就太好了。

97808 次浏览

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

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

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)

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.