48

I would like to change the TIMEZONE value in a Java Calendar instance at runtime. I tried below. But the output is the same in both instances:

    Calendar cSchedStartCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
    System.out.println(cSchedStartCal.getTime().getTime());
    cSchedStartCal.setTimeZone(TimeZone.getTimeZone("Asia/Calcutta"));
    System.out.println(cSchedStartCal.getTime().getTime());

OUTPUT:
1353402486773
1353402486773

I have tried this also but the output is still the same:

    Calendar cSchedStartCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
    System.out.println(cSchedStartCal.getTime());

    Calendar cSchedStartCal1 = Calendar.getInstance(TimeZone.getTimeZone("Asia/Calcutta"));
    cSchedStartCal1.setTime(cSchedStartCal.getTime());
    System.out.println(cSchedStartCal.getTime());

In the API I am seeing the below comment but I am not able to understand much of it:

     * calls: cal.setTimeZone(EST); cal.set(HOUR, 1); cal.setTimeZone(PST).
     * Is cal set to 1 o'clock EST or 1 o'clock PST?  Answer: PST.  More
     * generally, a call to setTimeZone() affects calls to set() BEFORE AND
     * AFTER it up to the next call to complete().

Could you please help me?

One Possible Solution :

    Calendar cSchedStartCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
    long gmtTime = cSchedStartCal.getTime().getTime();

    long timezoneAlteredTime = gmtTime + TimeZone.getTimeZone("Asia/Calcutta").getRawOffset();
    Calendar cSchedStartCal1 = Calendar.getInstance(TimeZone.getTimeZone("Asia/Calcutta"));
    cSchedStartCal1.setTimeInMillis(timezoneAlteredTime);

Is this solution ok?

Celt
  • 2,469
  • 2
  • 26
  • 43
Kanagavelu Sugumar
  • 18,766
  • 20
  • 94
  • 101
  • 1
    Possible duplicate of [stackoverflow.com/questions/7670355/](http://stackoverflow.com/questions/7670355/convert-date-time-for-given-timezone-java) – KeyNone Nov 20 '12 at 10:12
  • @BastiM the link is giving solution; only if we know the offset value. I don't want that since i have only timezone value. Could you please tell me what is the wrong on the above? or Solution? – Kanagavelu Sugumar Nov 20 '12 at 10:38
  • What do you expect to happen? Both `Calendar` objects refer to the same absolute time since the epoch. This is correct behavior. – augurar Apr 16 '16 at 08:40

3 Answers3

59

In Java, Dates are internally represented in UTC milliseconds since the epoch (so timezones are not taken into account, that's why you get the same results, as getTime() gives you the mentioned milliseconds).
In your solution:

Calendar cSchedStartCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
long gmtTime = cSchedStartCal.getTime().getTime();

long timezoneAlteredTime = gmtTime + TimeZone.getTimeZone("Asia/Calcutta").getRawOffset();
Calendar cSchedStartCal1 = Calendar.getInstance(TimeZone.getTimeZone("Asia/Calcutta"));
cSchedStartCal1.setTimeInMillis(timezoneAlteredTime);

you just add the offset from GMT to the specified timezone ("Asia/Calcutta" in your example) in milliseconds, so this should work fine.

Another possible solution would be to utilise the static fields of the Calendar class:

//instantiates a calendar using the current time in the specified timezone
Calendar cSchedStartCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
//change the timezone
cSchedStartCal.setTimeZone(TimeZone.getTimeZone("Asia/Calcutta"));
//get the current hour of the day in the new timezone
cSchedStartCal.get(Calendar.HOUR_OF_DAY);

Refer to stackoverflow.com/questions/7695859/ for a more in-depth explanation.

Community
  • 1
  • 1
KeyNone
  • 8,745
  • 4
  • 34
  • 51
  • 7
    The first half of this answer is utterly wrong. Milliseconds since epoch specifies an absolute point in time with no timezone information. Adjusting the calendar's millisecond time will change the calendar to represent a different point in time. The OP clearly wants to represent the *same* absolute point in time, in different time zones. – augurar Apr 16 '16 at 08:44
  • 1
    What @augurar said. Also, for people just copying and pasting the second part of the answer, be aware that not all time zones are offset with whole hours. – Roy Solberg Jan 09 '17 at 13:01
  • 1
    You should also include use of the getDSTSavings() TimeZone method to ensure that Daylight Savings Time is taken into consideration. – dazed Jul 21 '17 at 16:10
  • be Aware that the getRawOffset() does not consider day light saving rules. – behzad May 03 '20 at 17:11
16
  1. The class Date/Timestamp represents a specific instant in time, with millisecond precision, since January 1, 1970, 00:00:00 GMT. So this time difference (from epoch to current time) will be same in all computers across the world with irrespective of Timezone.

  2. Date/Timestamp doesn't know about the given time is on which timezone.

  3. If we want the time based on timezone we should go for the Calendar or SimpleDateFormat classes in java.

  4. If you try to print a Date/Timestamp object using toString(), it will convert and print the time with the default timezone of your machine.

  5. So we can say (Date/Timestamp).getTime() object will always have UTC (time in milliseconds)

  6. To conclude Date.getTime() will give UTC time, but toString() is on locale specific timezone, not UTC.

Now how will I create/change time on specified timezone?

The below code gives you a date (time in milliseconds) with specified timezones. The only problem here is you have to give date in string format.

   DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
   dateFormatLocal.setTimeZone(timeZone);
   java.util.Date parsedDate = dateFormatLocal.parse(date);

Use dateFormat.format for taking input Date (which is always UTC), timezone and return date as String.

How to store UTC/GMT time in DB:

If you print the parsedDate object, the time will be in default timezone. But you can store the UTC time in DB like below.

        Calendar calGMT = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
        Timestamp tsSchedStartTime = new Timestamp (parsedDate.getTime());
        if (tsSchedStartTime != null) {
            stmt.setTimestamp(11, tsSchedStartTime, calGMT );
        } else {
            stmt.setNull(11, java.sql.Types.DATE);
        }
Community
  • 1
  • 1
Kanagavelu Sugumar
  • 18,766
  • 20
  • 94
  • 101
0

Even though this is a very old question, it still seems relevant, for example on Android where you never really know what a timestamp means (or rather where it comes from). It also seems to me that the other answers didn't understand the question completely (although the accepted answer did, but the code in second half will not work with all timezones).

The problem is that a timestamp carries no timezone information, so if a device lives in a timezone but is unaware of it, the timestamp it reports for any given moment is different than it would've been if the device was in GMT, or if it knew it isn't and added the offset. And of course this works the other way too, if we have a timestamp that we know is in GMT, but we want to work with the time in the timezone of the device, we need to take the offset into account as well.

So yes, the solution provided in the question should work. In my case I do it roughly as follows:

TimeZone timeZone = TimeZone.getDefault(); // the local timezone, may as well be TimeZone.getTimeZone("Asia/Calcutta") 
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(timestampInGmt);
calendar.add(Calendar.MILLISECOND, timeZone.getOffset(timestamp))
long timestampInLocalTimeZone = calendar.getTimeInMillis()

To do it the opposite way, I simply subtract the offset. And instantiate timeZone as TimeZone.getTimeZone("GMT");

NoHarmDan
  • 492
  • 5
  • 16
  • This is helpful. But to clarify, for variable `timestamp` above, should that instead be the same variable used in the prior line, `timestampInGmt`? Also, for completeness, is this how you got `timestampInGmt`?: long timestampInGmt = System.currentTimeMillis(); – Woodchuck Feb 17 '23 at 17:07