I observed a strange behaviour of java.util.GregorianCalendar, and I wonder why it behaves so.
I wanted to get the time in UTC, which is the same instant as 26.10.2014 01:00 CET
and then get UTC midnight for the same day. So first I set the actual CET date, than changed the timezone to UTC, and finally set the HOUR_OF_DAY to 0.
Example:
see junit code below:
@Test
public void testWeird() {
GregorianCalendar date = (GregorianCalendar) GregorianCalendar.getInstance(TimeZone.getTimeZone("CET"));
date.set(2014, 9, 26, 1, 0, 0); //26.10.2014
System.out.println(date.getTime().toGMTString() + " " + date.getTimeInMillis()); // 25 Oct 2014 23:00:00 GMT 1414278000764 (OK)
date.setTimeZone(TimeZone.getTimeZone("UTC"));
//date.get(Calendar.YEAR); // uncomment this line to get different results
System.out.println(date.getTime().toGMTString() + " " + date.getTimeInMillis()); // 25 Oct 2014 23:00:00 GMT 1414278000764 (OK)
date.set(Calendar.HOUR_OF_DAY, 0);
System.out.println(date.getTime().toGMTString() + " " + date.getTimeInMillis()); // 26 Oct 2014 00:00:00 GMT 1414281600764 (NOT OK! why not 25 Oct 2014 00:00:00 GMT 1414195200218 ?)
}
I expected that setting hour=0 on 25.10.2014 23:00 GMT
will give me 25.10.2014 00:00 GMT
, but it changed to 26.10.2014 00:00 GMT
.
However, if I uncomment line date.get(Calendar.YEAR);
, the date seems to be calculated correctly.
The same happens on jdk.1.7.0_10 and jrockit-jdk1.6.0_37.
util. GregorianCalendar. setTimeZone() method is an in-built method in Java which modifies the current time zone to a new time zone according to the parameter passed. Parameters: The method takes one parameter tz of TimeZone object which specifies the new time zone value.
UTC stands for Co-ordinated Universal Time. It is time standard and is commonly used across the world. All timezones are computed comparatively with UTC as offset.
The only difference between the Gregorian and the Julian calendar is the leap year rule. The Julian calendar specifies leap years every four years, whereas the Gregorian calendar omits century years which are not divisible by 400.
Java Calendar setTime() MethodThe setTime () method of java. util. Calendar class is used to set the Time of current calendar object. A Date object id is passed as the parameter into this method.
As GregorianCalender extends Calendar class, it inherits all the features of it. From the Java Doc
set(f, value) changes calendar field f to value. In addition, it sets an internal
member variable to indicate that calendar field f has been changed. Although
calendar field f is changed immediately, the calendar's time value in
milliseconds is not recomputed until the next call to get(), getTime(),
getTimeInMillis(),add(), or roll() is made. Thus, multiple calls to set() do not
trigger multiple, unnecessary computations. As a result of changing a calendar
field using set(), other calendar fields may also change, depending on the calendar
field, the calendar field value, and the calendar system. In addition, get(f) will
not necessarily return value set by the call to the set method after the calendar
fields have been recomputed.
Java Doc Example :
Consider a GregorianCalendar originally set to August 31, 1999. Calling
set(Calendar.MONTH, Calendar.SEPTEMBER) sets the date to September 31, 1999. This
is a temporary internal representation that resolves to October 1, 1999 if
getTime()is then called. However, a call to set(Calendar.DAY_OF_MONTH, 30) before
the call to getTime() sets the date to September 30, 1999, since no recomputation
occurs after set() itself.
Also Calendar class has the following side effect :-
In lenient mode, all of the Calendar fields are normalized.
It means when you call setTimeZone(
) & set(Calendar.HOUR_OF_DAY, 0)
, it sets internal member variable to indicate that Calendar fields are set. But Calendar's time is not recomputed at that time. The Calendar's time is recomputed only after a call to get()
, getTime()
, getTimeInMillis()
, add()
, or roll()
is made.
This is a bug in Calendar class JDK-4827490 : (cal) Doc: Calendar.setTimeZone behavior undocumented
Now, your example is modified to get it work as below :-
public class DateTimeTest {
@SuppressWarnings("deprecation")
public static void main(String[] args) {
GregorianCalendar date = (GregorianCalendar) GregorianCalendar.getInstance(TimeZone
.getTimeZone("CET"));
// 26.10.2014 01:00:00
date.set(2014, 9, 26, 1, 0, 0);
// 25 Oct 2014 23:00:00 GMT 1414278000764
System.out.println("CET to UTC : " + date.getTime().toGMTString() + " "
+ date.getTimeInMillis());
date.setTimeZone(TimeZone.getTimeZone("UTC"));
// date.roll(Calendar.HOUR_OF_DAY, true); //uncomment this line & comment below line & check the different behavior of Calender.
date.get(Calendar.HOUR_OF_DAY);
// 25 Oct 2014 23:00:00 GMT 1414278000764
System.out.println("UTC : " + date.getTime().toGMTString() + " "
+ date.getTimeInMillis());
date.set(Calendar.HOUR_OF_DAY, 0);
// 25 Oct 2014 00:00:00 GMT 1414195200218
System.out.println("UTC Midnight : " + date.getTime().toGMTString() + " "
+ date.getTimeInMillis());
}
}
Output :
CET to UTC : 25 Oct 2014 23:00:00 GMT 1414278000008
UTC : 25 Oct 2014 23:00:00 GMT 1414278000008
UTC Midnight : 25 Oct 2014 00:00:00 GMT 1414195200008
I hope now you will get a clear idea about unpredictable behavior of Calendar class.
You directly change java.util.Date.setTimeZone(TimeZone.getTimeZone("UTC"));
?
Your GregorianCalendar's
timezone is CET
and date's
timezone is UTC
. And you print it out. Change your GregorianCalendar
instance with UTC
timezone.
//date.setTimeZone(TimeZone.getTimeZone("UTC")); <- remove it.
date.set(Calendar.HOUR_OF_DAY, 0);
GregorianCalendar utcCAL = (GregorianCalendar) GregorianCalendar.getInstance(TimeZone.getTimeZone("UTC"));
utcCAL.setTimeInMillis(date.getTimeInMillis());
System.out.println(utcCAL.getTime().toGMTString() + " " + utcCAL.getTimeInMillis());
Output
25 Oct 2014 22:00:00 GMT 1414274400517
Update
You can also use java.util.Date.UTC()
function.
//date.setTimeZone(TimeZone.getTimeZone("UTC"));
date.set(Calendar.HOUR_OF_DAY, 0);
Date utcDate = date.getTime();
utcDate.UTC(2014, 9, 1, 26, 1, 0);
System.out.println(utcDate.toGMTString() + " " + date.getTimeInMillis());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With