Setting a timezone does not work like setting a field. When you set a field, the internal # of milliseconds is re-calculated to match the fields. When you set a timezone, the fields are recalculated so that the # of milliseconds remains constant! (That is, when you have a clanedar evaluated to 2:00 PM mountain and you set it to UTC, it does not calculate the milliseconds that represent 2PM UTC, it changes the time to 9PM.)
Now here's where it goes sideways, when you have a pending set
(or add
in your case) the flag that tells Calendar to recalculate the number of milliseconds (turned on by add
) takes precedence over the flag that tells if to recalculate the text fields off the milliseconds (turned on by setTimeZone
)
So scenario 1 Calendar has text value:
Sat Jan 24 2015 2:33:58 PM UTC and because add
was called the flag that says "recompute milliseconds based on 'human' values" is turned on, and it gives you the # you expected.
Scenario 2 Calendar has text value:
Sat Jan 24 2015 2:33:58 PM UTC, but the flag that says to recompute # of milliseconds is not on, so instead the flag turned on by set timezone to "recompute 'human values' based on milliseconds" is turned on and it changes it to 9pm.
If you change your code to:
public void TestCalendar() {
Calendar nextYear = Calendar.getInstance();
nextYear.add(Calendar.YEAR, 1);
nextYear.getTime();
log.info("Next Year: {}", getUTCMilliseconds(nextYear));
Calendar now = Calendar.getInstance();
log.info("Now: {}", getUTCMilliseconds(now));
}
You will see consistent behaviour between the two because calling getTime() clears the flag to recalculate milliseconds prior to setting the timezone.
Now you know why the stock answer to a question like this is 'omg use jodatime'!
The 'safe' way to get what you were expecting to happen is:
protected String getUTCMilliseconds(Calendar cal) {
Calendar utcCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
utcCal.set(Calendar.YEAR, cal.get(Calendar.YEAR));
utcCal.set(Calendar.DAY_OF_YEAR, cal.get(Calendar.DAY_OF_YEAR));
utcCal.set(Calendar.HOUR_OF_DAY, cal.get(Calendar.HOUR_OF_DAY));
utcCal.set(Calendar.MINUTE, cal.get(Calendar.MINUTE));
utcCal.set(Calendar.SECOND, cal.get(Calendar.SECOND));
utcCal.set(Calendar.MILLISECOND, cal.get(Calendar.MILLISECOND));
return String.valueOf(utcCal.getTimeInMillis());
}