2

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!

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
jejjejd
  • 721
  • 1
  • 8
  • 18
  • 5
    A `java.util.Date` doesn't have a time zone at all. It's *just* a number of milliseconds since the Unix epoch. Ignore the result of calling `Date.toString()` - the fact that that converts the value to the local time zone is just annoying :( – Jon Skeet Aug 08 '19 at 15:12
  • @JonSkeet Is Date.toString() called even when inspecting variables during debugging? I checked the value of objects that way – jejjejd Aug 08 '19 at 15:14
  • @JonSkeet i tried printing it and both print Different values, so one is converted and another isnt – jejjejd Aug 08 '19 at 15:17
  • Well `dt` is being created as "2012-05-05 12:13:14 in the system time zone", because you've called `Calendar.getInstance()` which uses the system time zone. Whereas `d2` is 2012-05-05 12:13:14 in UTC. So yes, I'm not surprised they're doing different things, but it's not clear what you're trying to do. – Jon Skeet Aug 08 '19 at 15:23
  • Careful. `zdf.getMonthValue()` [returns a value from 1 to 12](https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/time/ZonedDateTime.html#getMonthValue%28%29), but [Calendar.set expects a value from 0 to 11](https://docs.oracle.com/en/java/javase/12/docs/api/java.base/java/util/Calendar.html#set%28int,int,int,int,int,int%29). – VGR Aug 08 '19 at 15:47
  • This question was asked a couple of days ago, differently worded but with the same code snippet. I wrote a longish answer. Now it seems that that question has been deleted (I cannot find it) and then posted anew. I’m confused. – Ole V.V. Aug 08 '19 at 21:25

2 Answers2

1

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

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
1

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.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154