4
Calendar c = Calendar.getInstance();
System.out.println(c.getTime());
c.set(2007, 0, 1);
System.out.println(c.getTime());

Output:

Tue Sep 12 12:36:24 IST 2017

Mon Jan 01 12:36:24 IST 2007

But, When I use the same code in a different environment, Output changes to below:

Output:

Tue Sep 12 12:36:24 IST 2017

Mon Jan 01 12:36:24 GMT 2007

FYI, I tried to print the timezone of the calendar instance, before and after setting the values and both are in "IST".

I want to know the root cause of this.

Dawood ibn Kareem
  • 77,785
  • 15
  • 98
  • 110
Dinesh Babu
  • 73
  • 1
  • 1
  • 7
  • 1
    Then the default timezone is different on the other machine. Check the system's timezone configuration. – Luciano van der Veekens Sep 12 '17 at 07:18
  • Date does not have timezone, you will need to use `SimpleDateFormat` – Kenneth Clark Sep 12 '17 at 07:18
  • 2
    Possible duplicate of [Calendar returns date in wrong time zone](https://stackoverflow.com/questions/26678030/calendar-returns-date-in-wrong-time-zone) – Kenneth Clark Sep 12 '17 at 07:19
  • If you want to change TimeZone of Calendar you cau use calendar.setTimeZone(TimeZone). If it helps you. – SachinSarawgi Sep 12 '17 at 07:22
  • @KennethClark That didn't quite answer my question. I need to know why the same code snippet is behaving differently. Now, I see the issue in same environment and different classes of the code. – Dinesh Babu Sep 12 '17 at 07:22
  • @SachinSarawgi calendar instance's timezone is already in "IST". – Dinesh Babu Sep 12 '17 at 07:23
  • if you want to get the timezone `Calendar.getInstance(TimeZone.getTimeZone("IST"));` – Kenneth Clark Sep 12 '17 at 07:24
  • Thanks @KennethClark. As mentioned earlier, calaender instance's timezone is already in "IST" – Dinesh Babu Sep 12 '17 at 07:30
  • The first suggestion is to skip the long outdated classes `Calendar` and `Date` and start using the [modern Java date and time API known as JSR-310 or `java.time`](https://docs.oracle.com/javase/tutorial/datetime/). This is only one out of countless examples of where the old classes show surprising and unwanted behaviour. The modern ones are so much nicer to work with. – Ole V.V. Sep 12 '17 at 08:06
  • `c.getTime()` returns a `Date` and a `Date` *hasn’t got any time zone in it*. If you opt to continue using `Date`, a must-read is [All about java.util.Date](https://codeblog.jonskeet.uk/2017/04/23/all-about-java-util-date/). – Ole V.V. Sep 12 '17 at 08:09
  • I haven’t got enough context to be sure, but immediately it seems that you would need a `LocalDate` object. This represents a date without time-of-day. Something like `LocalDate ld = LocalDate.of(2007, Month.JANUARY, 1);`. – Ole V.V. Sep 12 '17 at 08:12
  • Are you sure you can reproduce the behaviour in that other environment? It seems strange that printing two `Date` objects (implicitly invoking their `toString()`) would produce output in two different time zones. I didn’t find anything in the documentation that says it’s impossible, though. – Ole V.V. Sep 12 '17 at 08:24
  • 1
    @OleV.V. I'm completely sure and cross checked the scenarios. Can't disclose where, but I cant still see the same behavior. i.e., two different outputs of getTime() in two different java classes running on same* JVM – Dinesh Babu Sep 12 '17 at 08:27
  • Very interesting indeed. What happens if you insert `System.out.println(TimeZone.getDefault().getID());` before *and* after your code snippet? Asking because I would normally expect that the time zone ID thus printed would agree with the time zone used for printing the `Date`s. – Ole V.V. Sep 12 '17 at 08:33
  • @OleV.V. Both the IDs are same before and after the code snippet. But, the date object returned doesn't fail to get converted into GMT. – Dinesh Babu Sep 12 '17 at 09:03
  • Either your JVM understand IST as either Irish Standard Time or Irish Summer Time — or there seems to be a bug in the `Date` class. If IST was either Israel or India Standard Time, the time would not still be 12:36:24 after conversion to GMT. The implementation of `Date.toString()` does contain an `if` statement where it prints `GMT` if there is no time zone information; this might be part of the explanation of the bug. Pure speculation, of course. – Ole V.V. Sep 12 '17 at 09:20
  • Time would still be 12:36:24 coz, it's not getting overridden. The only mystery is why there is change in Timezone. If the timezone is Europe/Dublin, the value even after conversion should be 11:36:24 GMT instead of 12:36:24 GMT – Dinesh Babu Sep 12 '17 at 09:32
  • No, the time is the same. Remember that on January 1, 2007 Ireland was not on summer time (daylight saving time). I am assuming the `Calendar` uses Irish time too. – Ole V.V. Sep 12 '17 at 10:03

3 Answers3

8

The second output in your question is the correct and expected behaviour on a JVM running Irish time (Europe/Dublin). On September 12, 2017 Ireland is on summer time (DST). While it is not clearly documented, Date.toString() (which you invoke implicitly when printing the Date you get from c.getTime()) prints the date and time in the JVM’s time zone, which in September is rendered as IST for Irish Summer Time.

When you set the date on the Calendar object also using Irish time, the hour of day is preserved; in your case you get Jan 01 2007 12:36:24 Irish standard time. Now imagine the confusion if both Irish Summer Time and Irish Standard Time were rendered as IST. You would not be able to distinguish. Instead, since Irish standard time coincides with GMT, this is what Date.toString() prints when the date is not in the summer time part of the year (which January isn’t).

My guess is that your first output is from a JVM running India time. It too is rendered as IST, and since India doesn’t use summer time, the same abbreviation is given summer and winter.

java.time

Before understanding the explanation for the behaviour you observed, I posted a comment about the outdated and the modern Java date and time classes. I still don’t think the comment is way off, though. This is the modern equivalent of your code:

    ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Europe/Dublin"));
    System.out.println(zdt);
    zdt = zdt.with(LocalDate.of(2007, Month.JANUARY, 1));
    System.out.println(zdt);

It prints

2017-09-12T11:45:33.921+01:00[Europe/Dublin]
2007-01-01T11:45:33.921Z[Europe/Dublin]

If you want to use the JVM’s time zone setting, use ZoneId.systemDefault() instead of ZoneId.of("Europe/Dublin"). As the name states, contrary to Date, ZonedDateTime does include a time zone. It corresponds more to the old Calendar class. As you can see, its toString method prints the offset from UTC (Z meaning zero offset) and the time zone name in the unambiguous region/city format. I believe that this leaves a lot less room for confusion. If you want to print the date in a specific format, use a DateTimeFormatter.

Appendix: sample output from your code

For the sake of completeness, here are the outputs from your code when running different time zones that may be rendered as IST:

  • Europe/Dublin (agrees with your second output)

    Tue Sep 12 11:19:28 IST 2017
    Mon Jan 01 11:19:28 GMT 2007
    
  • Asia/Tel_Aviv

    Tue Sep 12 13:19:28 IDT 2017
    Mon Jan 01 13:19:28 IST 2007
    
  • Asia/Kolkata (agrees with your first output)

    Tue Sep 12 15:49:28 IST 2017
    Mon Jan 01 15:49:28 IST 2007
    
Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • I get you now. But, can you explain the same with a code snippet where the Calendar.getInstance().getTime() actually prints the date in GMT and in IST. – Dinesh Babu Sep 12 '17 at 10:22
  • Thank you soo much for sticking around with my annoying doubts and giving a wonderful clarification. Thanks a Million buddy. – Dinesh Babu Sep 12 '17 at 10:30
  • You’re welcome. This one was tricky and fun (I also upvoted the question). I can probably provide a code snippet, but I am not sure exactly what you want from it? @DineshBabu – Ole V.V. Sep 12 '17 at 10:41
7

You need to set time zone and you will get desired result.

TimeZone.setDefault(TimeZone.getTimeZone("IST"));

Here is a working code.

import java.util.Calendar;
import java.util.TimeZone;  
public class Cal {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        TimeZone.setDefault(TimeZone.getTimeZone("IST")); // Add this before print
        Calendar c = Calendar.getInstance();    
        System.out.println(c.getTime());
        c.set(2007, 0, 1);
        System.out.println(c.getTime());
    }

}

As per Doc "Typically, you get a TimeZone using getDefault which creates a TimeZone based on the time zone where the program is running. For example, for a program running in Japan, getDefault creates a TimeZone object based on Japanese Standard Time."

SO when you running in different timezone it is using as default timezone. Hope you clear now. I attach doc. please read.

UDID
  • 2,350
  • 3
  • 16
  • 31
  • thanks for the response. In the first case, I didn't set the timezone. But, Still I can print the date object in the default Timezone. I need to know why it is different in both the cases. I'm expected to get RootCause of the difference. – Dinesh Babu Sep 12 '17 at 07:29
  • I have this approach as my backup workaround through this issue. But, I actually intend to know the root cause of this. Thanks again for your quick responses :) – Dinesh Babu Sep 12 '17 at 07:36
  • Yes, you can change your JVM’s default time zone. And thereby break functionality in your own program and in other programs running in the JVM. Be warned. – Ole V.V. Sep 12 '17 at 08:04
  • Thanks for idea sir @OleV.V. let me try this. – UDID Sep 12 '17 at 08:07
  • @UDID I’m afraid we don’t understand each other correctly. The idea was yours, you wrote `TimeZone.setDefault(TimeZone.getTimeZone("IST"));`. I am warning against it. – Ole V.V. Sep 12 '17 at 08:20
  • Also avoid the three letter time zone abbreviations. Some JVMs understand IST as Israel Standard Time, others as India Standard Time. Instead I recommend the time zone names in the form *region/city*, for example `Asia/Tel_Aviv`. – Ole V.V. Sep 12 '17 at 08:25
  • @UDID Also, apologize me for not bringing this up earlier. I can't hard code the value to "IST". The timezone should be the default TimeZone based on where the JVM is. I.e., If the code snippet is run in india, It should be IST(indian standard time) If it's Israel, It should be IST(Israel standard time) and so on.. – Dinesh Babu Sep 12 '17 at 08:37
0

To talk about this interesting behaviour:

The source code from the Calendar class:

public final void set(int year, int month, int date)
{
    set(YEAR, year);
    set(MONTH, month);
    set(DATE, date);
}

Which leads to the set method:

public void set(int field, int value)
{
    // If the fields are partially normalized, calculate all the
    // fields before changing any fields.
    if (areFieldsSet && !areAllFieldsSet) {
        computeFields();
    }
    internalSet(field, value);
    isTimeSet = false;
    areFieldsSet = false;
    isSet[field] = true;
    stamp[field] = nextStamp++;
    if (nextStamp == Integer.MAX_VALUE) {
        adjustStamp();
    }
}

The interesting part here is the computeFields() method which has two implementation (one for Gregorian and one for Japenese calendar). These methods are quite complex but as far as I can see this is the only place where your Calendar instance may change time zone in your usecase.

Zsolt V
  • 517
  • 3
  • 8
  • Interesting, but not relevant. The `Calendar` object holds a time zone, but the `Date` object returned by `getTime()` doesn’t hold the same time zone — simply because it doesn’t hold any time zone at all. – Ole V.V. Sep 12 '17 at 08:18
  • @OleV.V. Maybe I am missing something: between the two print statement somewhere the time zone changes (at least the output show something like that). Three thing happens: 1. call the calendar's set(int,int,int) method 2. call the calendars getTime() method 3. implicit call on the Date's toString() method. I wrote that the inner state of time zone could be changed by the first call. – Zsolt V Sep 12 '17 at 09:41
  • Or maybe I am. If we assume that the `Calendar`’s inner time zone state is changed, I still fail to see how that could influence how the `Date`’s `toString()` method behaves. – Ole V.V. Sep 12 '17 at 09:45
  • 1
    @OleV.V. Ok. You were right as the accepted answer clear the issue. The details of Calendar class are irrelevant here. – Zsolt V Sep 12 '17 at 10:37