简体   繁体   中英

Why isn't Date object representing date in my JVM's timezone?

I have UTC date and I need to create Date object with exact same value as UTC ( legacy reasons ).

I have managed to do it:

String date = "2012-05-05 12:13:14";
TemporalAccessor formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
        .withZone(ZoneId.of("UTC"))
        .parse(date);
Instant instant = Instant.from(
        formatter
); //
ZonedDateTime zdf = ZonedDateTime.ofInstant(instant,ZoneId.of("UTC"));
Calendar calendar = Calendar.getInstance();
calendar.set(zdf.getYear(),zdf.getMonthValue(),zdf.getDayOfMonth(),zdf.getHour(),zdf.getMinute(),zdf.getSecond());
Date dt = calendar.getTime();

Date d2 = Date.from(instant);

However, what bothers me -

When I create date object, it should display the date in my JVM's default timezone. But here dt has exact same value as my input UTC date, but dt2 has same date represented in my default timezone, why did this happen? Why is one not converted and another is?

Thanks for explaining!

Preserving the hour of day versus preserving the moment

I'll explain the central lines one by one.

    Calendar calendar = Calendar.getInstance();

This creates a Calendar with the same time zone as your JVM's default time zone. Unlike Date a Calendar has got a time zone.

    calendar.set(zdf.getYear(),zdf.getMonthValue(),zdf.getDayOfMonth(),
            zdf.getHour(),zdf.getMinute(),zdf.getSecond());

This sets your Calendar to the same wall clock time as your ZonedDateTime , namely 12:13:14. Since the ZonedDateTime and the Calendar have different time zones (UTC and your local time zone, respectively), this results in a different moment.

Also @VGR is correct: While the date of the ZonedDateTIme was in May (month 5), you are setting the Calendar month to June because Calendar months are confusingly 0-based, from 0 for January through 11 for December.

    Date d2 = Date.from(instant);

This is the correct conversion from Instant to Date and gives you the same point in time, the same moment as the Instant . And therefore not the same moment as the Calendar and dt .

And it was probably understood in your question, but for anyone reading along I would like to state directly: Date and Calendar are poorly designed and long outdated. You should not use them except when necessary for interacting with a legacy API that you cannot change or don't want to upgrade just now. For all other uses stick to java.time, the modern Java date and time API.

Links

tl;dr

  • As others said, a java.util.Date represents a moment in UTC. Its toString method lies to you by dynamically applying the JVM's current default time zone while generating is poorly-formatted string. One of many reasons to never use this class.
  • Do not waste you time on trying to understand the terrible classes Date & Calendar . They are now legacy , to be avoided.
  • Use only java.time classes for all your date-time handling .

Details

The Answer by Ole VV is correct and should be accepted. I'll add a few thoughts.

You are mixing the terrible legacy date-time classes ( Date , Calendar ) with their modern replacements ( Instant , ZonedDateTime ). Do not mix these. The legacy classes are entirely supplanted by the java.time classes with the adoption of JSR 310 .

There is no need to ever use Date or Calendar again. Do not waste your time trying to understand them. They were replaced for a reason – many reasons, actually. Use your brains and time for more productive work.

If you must use the legacy classes to interoperate with old code not yet updated to java.time , convert to-and-fro by calling the new to… / from… conversion methods added to the old classes. Perform your business logic, data exchange, and data storage all using the java.time classes.

The java.util.Date class is replaced by Instant .

java.util.Date d = Date.from( instant ) ;     // From  modern to legacy.
Instant instant = d.toInstant() ;             // From legacy to modern.

The Calendar class, or rather its commonly used subclass GregorianCalendar , is replaced by ZonedDateTime . Assuming your Calendar object is really a GregorianCalendar underneath, you can cast, then convert.

Calendar c = GregorianCalendar.from( zonedDateTime ) ;                        // From  modern to legacy.
ZonedDateTime zonedDateTime = ( (GregorianCalendar) c ).toZonedDateTime() ;   // From legacy to modern.

Here is a chart mapping the legacy classes to the modern ones.

Java(旧版和现代版)和标准 SQL 中的日期时间类型表。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM