jejjejd
jejjejd

Reputation: 761

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!

Upvotes: 2

Views: 122

Answers (2)

Basil Bourque
Basil Bourque

Reputation: 340108

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 V.V. 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.

Table of date-time types in Java (both legacy & modern) and in standard SQL.

Upvotes: 1

Anonymous
Anonymous

Reputation: 86379

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

Upvotes: 1

Related Questions