Randhir Ray
Randhir Ray

Reputation: 590

Date Format with different Timezone in Java

I am confused with Timezone conversions in Java. I have a few cases which I will list out below.

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
   // sdf.setTimeZone(TimeZone.getTimeZone("Asia/kolkata"));
    Date date1 = sdf.parse("2021-01-31");

    System.out.println(date1);   //. O/P - Sun Jan 31 00:00:00 IST 2021

Now lets uncomment the Timezone part and see the time difference

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    sdf.setTimeZone(TimeZone.getTimeZone("Asia/kolkata"));
    Date date1 = sdf.parse("2021-01-31");

    System.out.println(date1);  // O/P - Sun Jan 31 05:30:00 IST 2021 

Now lets set the TimeZone to IST and see the time difference

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    sdf.setTimeZone(TimeZone.getTimeZone("IST"));
    Date date1 = sdf.parse("2021-01-31");

    System.out.println(date1);   // O/P - Sun Jan 31 00:00:00 IST 2021

Now lets set the TimeZone to UTC and see the time difference

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
    Date date1 = sdf.parse("2021-01-31");

    System.out.println(date1);  // O/P - Sun Jan 31 05:30:00 IST 2021
  1. Can anybody please explain me why this shift in time is happening (+- 5:30) when I change the Timezone?
  2. For IST and Asia/Kolkata, time should have remain same because they are same Timezone, but why the shift?
  3. Why When using the UTC Timezone, time gets increased by 5:30 hours? What I understand is IST is 5:30 hrs ahead of UTC, so cnverting to UTC should have decreased the time by 5:30 hrs
  4. Why even after converting to UTC, my time displays IST 2021?

I still have confusion here.

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
    Date date1 = sdf.parse("2021-01-31");
    System.out.println(date1.getTime()); // 1612051200000


    SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
    sdf1.setTimeZone(TimeZone.getTimeZone("Asia/Kolkata"));
    Date date2 = sdf1.parse("2021-01-31");
    System.out.println(date2.getTime());  // 1612031400000

Why instant of time in UTC is greater than instant of time in Asia/Kolkata ?

Upvotes: 2

Views: 8274

Answers (4)

Ali Gelenler
Ali Gelenler

Reputation: 241

As an addition to the accepted answer, for the last part of your question;

Why instant of time in UTC is greater than instant of time in Asia/Kolkata in below code?

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date1 = sdf.parse("2021-01-31");
System.out.println(date1.getTime()); // 1612051200000


SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
sdf1.setTimeZone(TimeZone.getTimeZone("Asia/Kolkata"));
Date date2 = sdf1.parse("2021-01-31");
System.out.println(date2.getTime());  // 1612031400000
  • First, you have a point T in time regardless of timezone. In our example T=2021-01-31 00:00:00.
  • When we set timezone as UTC and print the time using java.util.Date.getTime() method, it will print milliseconds since the Unix epoch, which occurred at midnight January 1st 1970, UTC. So it will print 1612051200000. As you see the epoch and our date has the same timezone which is UTC. So the time is printed directly, no adjustment necessary for timezone.
  • Now, when we set timezone as Asia/Kolkata, during SimpleDateFormat.parse, timezone information will be added to date. That means +5:30h(19800000ms) will be added to time T. Therefore our time T is increased by 19800000ms. However T must be pointing to the same point in time. How do we fix that? It is fixed on SimpleDateFormat.parse method by subtracting 19800000ms from the time 1612051200000ms so that getTime() method will now show 1612031400000ms so that our actual time T will still show the same point in time(which is 1612051200000ms) because in this date object we have an extra 19800000ms which comes from timezone.

Upvotes: 0

Anonymous
Anonymous

Reputation: 86389

java time

I recommend you use java.time, the modern Java date and time API, for your date work

    LocalDate date = LocalDate.parse("2021-01-31");
    System.out.println(date);

Output is:

2021-01-31

A LocalDate is a date without time of day and without time zone or UTC offset, so using it frees you completely from all time zone trouble. Furthermore we don’t need any explicit formatter. Your string is in ISO 8601 format, and LocalDate parses the most common ISO 8601 variant as its default. As you can see, it also prints the same ISO 8601 format back when we print it, implicitly calling its toString method.

What went wrong in your code?

The SimpleDateFormat, TimeZone and Date classes that you are using are poorly designed and long outdated. No wonder that their behaviour confuses you.

I am assuming that Asia/Kolkata (or Asia/Colombo or Asia/Calcutta) is the default time zone of your JVM. In your first example the SimpleDateFormat is using your default time zone and is parsing the string into the first moment of the day in that time zone.

In your second example, as Elavya has spotted so well, you have got a lower case k in Asia/kolkata which causes TimeZone not to recognize the intended time zone. And this is where TimeZone excels in bad design: it just tacitly gives you GMT instead. Next the Date class is poorly designed too and still prints the time in the default time zone of the JVM, giving the illusion that the Date object contains a time zone. This has confused very many. The start of the day in GMT is the same point in time as 05:30:00 IST, so this is what you get.

In your third and fourth example, even though the three letter time zone abbreviations are deprecated, IST (contrary to what Eklavya said) is interpreted as Asia/Kolkata and UTC as Etc/UTC. Even though as Eklavya also said, IST is ambiguous.

So in short:

  1. The change happens because the start of the day is a different point in time in different time zones.
  2. Because of your typo in Asia/kolkata. Time zone IDs are case sensitive.
  3. You are not converting to UTC. You are parsing in UTC thereby converting from UTC, and Date.toString() further converts to Asia/Kolkata (IST) as the output also says.
  4. Because the Date object hasn’t got a time zone and because Date.toString() grabs the default time zone of your JVM and uses it for rendering the string to be returned.

Links

Upvotes: 2

Eklavya
Eklavya

Reputation: 18480

Java doc for getTimeZone

ID - the ID for a TimeZone, either an abbreviation such as "PST", a full name such as "America/Los_Angeles", or a custom ID such as "GMT-8:00". Note that the support of abbreviations is for JDK 1.1.x compatibility only and full names should be used.

TimeZone abbreviation is not supported. So you can't use IST

And in TimeZone Doc for Three-letter time zone IDs

For compatibility with JDK 1.1.x, some other three-letter time zone IDs (such as "PST", "CTT", "AST") are also supported. However, their use is deprecated because the same abbreviation is often used for multiple time zones (for example, "CST" could be U.S. "Central Standard Time" and "China Standard Time"), and the Java platform can then only recognize one of them.

Problem is IST abbreviation is used for multiple time zones like Irish Standard Time, Isreal Standrad Time, Indian Standard Time. And you mistyped Asia/Kolkata as Asia/kolkata.

So, the GMT zone will return if the given ID cannot be understood from TimeZone.getTimeZone()

Upvotes: 1

Sweeper
Sweeper

Reputation: 274835

Here are some things for you to note:

  • When a Date is printed, it will be formatted in your computer's local timezone (that's what Date.toString does). Presumably, your computer is in the Asia/Kolkata timezone, so the output is always displayed as a date & time in that timezone.

  • A Date represents a point in time (i.e. an instant). It is not a tuple of year, month, day, hour, minute, seconds and timezone

  • Since there are no time in your input string, the time 00:00:00 is used for the time when parsing.

  • Just a date and a time is not enough to produce a point in time. You also need a timezone to specify a point in time. Since there is no timezone in your input string, the local timezone of your computer is used, or if you have set it, sdf.getTimeZone().

  • Although a timezone is used in parsing the date, the timezone is not part of the Date object.

Can anybody please explain me why this shift in time is happening (+- 5:30) when I change the Timezone?

When you use the "IST" timezone (first and third code snippet), sdf gets the following pieces of information:

  • Date: 2021-01-31
  • Time: 00:00:00
  • TimeZone: Asia/Kolkata

With these pieces of information, it can produce a point in time, represented by a number of milliseconds since the Java Epoch - 1970-01-01 00:00:00 UTC. This is the Date object. Then you print the Date object, which gets formatted to your local timezone. Your local timezone just so happens to be the same as the one that sdf is provided with, so you see Sun Jan 31 00:00:00 IST 2021.

When you use UTC (second and fourth code snippets), these information are provided to sdf:

  • Date: 2021-01-31
  • Time: 00:00:00
  • TimeZone: UTC

That represents a different point in time than 2021-01-31T00:00:00 in Kolkata. How different? 2021-01-31T00:00:00 in UTC is exactly 5 and a half hours later than 2021-01-31T00:00:00 in Kolkata. Recall that to convert a UTC time to Kolkata, you add 5 and a half hours.

For IST and Asia/Kolkata, time should have remain same because they are same Timezone, but why the shift?

Because you have misspelled Asia/Kolkata. The first "K" in "Kolkata" should be capitalised. Unknown zone IDs are treated as UTC by the TimeZone class. This is why you should move to the new java.time classes. ZoneId throws an exception if you supply it with an unknown zone ID.

Why When using the UTC Timezone, time gets increased by 5:30 hours? What I understand is IST is 5:30 hrs ahead of UTC, so converting to UTC should have decreased the time by 5:30 hrs

You are thinking about formatting dates, not parsing, because remember that the timezone is not part of Date, but part of SimpleDateFormat. Your code does not format Date, only parses them. Without formatting, Dates are always printed in your local timezone.

To see your desired behaviour using SimpleDateFormat, you'd first parse the date string once, and then format it using SimpleDateFormats with different timezones.


Really though, you should change to java.time. Using that API, your zone changing code could be written like so:

ZonedDateTime zdt = LocalDate.parse("2021-01-31")
        .atStartOfDay()
        .atZone(ZoneId.of("Asia/Kolkata"));
System.out.println(zdt);
ZonedDateTime utcDateTime = zdt.withZoneSameInstant(ZoneId.of("UTC"));
System.out.println(utcDateTime);

// output:
// 2021-01-31T00:00+05:30[Asia/Kolkata]
// 2021-01-30T18:30Z[UTC]

Upvotes: 4

Related Questions