Solved

Gregorian Calendar one hour off

Posted on 2006-07-13
16
517 Views
Last Modified: 2010-05-18
Hi, I have a small question:

The following works:

             // 2005-10-30
            Date theDate = new Date (1130623200000L);
            // 07:21:35
            Time theTime = new Time (22895000L);
            GregorianCalendar calendar = new GregorianCalendar();
            calendar.setTime(theDate);
            calendar.set(Calendar.HOUR, theTime.getHours());
            calendar.set(Calendar.MINUTE, theTime.getMinutes());
            calendar.set(Calendar.SECOND, theTime.getSeconds());

            assertTrue (calendar.get (Calendar.HOUR) == 7);

The following does not work (resulting hour is 6 instead of 7)

             // 2005-10-30
            Date theDate = new Date (1130623200000L);
            // 07:21:35
            Time theTime = new Time (22895000L);
            GregorianCalendar calendar = new GregorianCalendar();
            calendar.setTime(theDate);
            calendar.add(Calendar.HOUR, theTime.getHours());
            calendar.add(Calendar.MINUTE, theTime.getMinutes());
            calendar.add(Calendar.SECOND, theTime.getSeconds());

            assertTrue (calendar.get (Calendar.HOUR) == 7);

My guess is that this has something to do with daylight savings/ summer vs winter time... Can anyone explain?

Barry
0
Comment
Question by:barrel
  • 7
  • 4
  • 2
  • +2
16 Comments
 
LVL 8

Expert Comment

by:colr__
ID: 17100790
You are adding time instead of setting it - you are adding the hour of the day to the hour of the day, and the same for the minutes and seconds.
0
 
LVL 2

Author Comment

by:barrel
ID: 17101153
Sure thing, but for the date 2005-10-30, the initial hour is zero. Adding 7 to zero should lead to seven, not six.... Or am I misinterpreting something? Additionally, this way of date handling works for 99% of the dates we process. There are just a few cases (one of them mentioned in the testcase) in which it doesn't work....
0
 
LVL 2

Expert Comment

by:RoyalNepal
ID: 17101174
  //The following does not work (resulting hour is 6 instead of 7)

           // 2005-10-30
          Date theDate = new Date (1130623200000L);
          // 07:21:35
          Time theTime = new Time (22895000L);
          GregorianCalendar calendar = new GregorianCalendar();
          calendar.setTime(theDate);
////////////////////////////////////////////////////////ERRORRRR///////////////////////////////
          calendar.add(Calendar.HOUR, theTime.getHours());  //use calendar.set
          calendar.add(Calendar.MINUTE, theTime.getMinutes());//use calender.set
          calendar.add(Calendar.SECOND, theTime.getSeconds());//use calender.set

          assertTrue (calendar.get (Calendar.HOUR) == 7);

Do u really have to use calender.add function???
0
 
LVL 2

Author Comment

by:barrel
ID: 17101263
No, I do not ;-)

I have become the maintainer of a project for which I did not write the code. The above illustrates one of the bugs I found and corrected. So... the problem is solved, but the big question WHY??? remains.

So I am not looking for a solution, I am looking for an explanation.

0
 
LVL 2

Expert Comment

by:RoyalNepal
ID: 17101345
here is why...................

calendar fields can be changed using three methods: set(), add(), and roll().

set(f, value) changes field f to value. In addition, it sets an internal member variable to indicate that field f has been changed. Although field f is changed immediately, the calendar's milliseconds is not recomputed until the next call to get(), getTime(), or getTimeInMillis() is made. Thus, multiple calls to set() do not trigger multiple, unnecessary computations. As a result of changing a field using set(), other fields may also change, depending on the field, the field value, and the calendar system. In addition, get(f) will not necessarily return value after the fields have been recomputed. The specifics are determined by the concrete calendar class.

Example: Consider a GregorianCalendar originally set to August 31, 1999. Calling set(Calendar.MONTH, Calendar.SEPTEMBER) sets the calendar 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 calendar to September 30, 1999, since no recomputation occurs after set() itself.


add(f, delta) adds delta  to field f. This is equivalent to calling set(f, get(f) + delta) with two adjustments:

    Add rule 1. The value of field f after the call minus the value of field f before the call is delta, modulo any overflow that has occurred in field f. Overflow occurs when a field value exceeds its range and, as a result, the next larger field is incremented or decremented and the field value is adjusted back into its range.

    Add rule 2. If a smaller field is expected to be invariant, but   it is impossible for it to be equal to its prior value because of changes in its minimum or maximum after field f is changed, then its value is adjusted to be as close as possible to its expected value. A smaller field represents a smaller unit of time. HOUR is a smaller field than DAY_OF_MONTH. No adjustment is made to smaller fields that are not expected to be invariant. The calendar system determines what fields are expected to be invariant.

In addition, unlike set(), add() forces an immediate recomputation of the calendar's milliseconds and all fields.

Example: Consider a GregorianCalendar originally set to August 31, 1999. Calling add(Calendar.MONTH, 13) sets the calendar to September 30, 2000. Add rule 1 sets the MONTH field to September, since adding 13 months to August gives September of the next year. Since DAY_OF_MONTH cannot be 31 in September in a GregorianCalendar, add rule 2 sets the DAY_OF_MONTH to 30, the closest possible value. Although it is a smaller field, DAY_OF_WEEK is not adjusted by rule 2, since it is expected to change when the month changes in a GregorianCalenda
0
 
LVL 2

Expert Comment

by:RoyalNepal
ID: 17101366
BTW:roll function--------------

roll(f, delta) adds delta to field f without changing larger fields. This is equivalent to calling add(f, delta) with the following adjustment:

    Roll rule. Larger fields are unchanged after the call. A larger field represents a larger unit of time. DAY_OF_MONTH is a larger field than HOUR.

Example: Consider a GregorianCalendar originally set to August 31, 1999. Calling roll(Calendar.MONTH, 8) sets the calendar to April 30, 1999. Add rule 1 sets the MONTH field to April. Using a GregorianCalendar, the DAY_OF_MONTH cannot be 31 in the month April. Add rule 2 sets it to the closest possible value, 30. Finally, the roll rule maintains the YEAR field value of 1999.

Example: Consider a GregorianCalendar originally set to Sunday June 6, 1999. Calling roll(Calendar.WEEK_OF_MONTH, -1) sets the calendar to Tuesday June 1, 1999, whereas calling add(Calendar.WEEK_OF_MONTH, -1) sets the calendar to Sunday May 30, 1999. This is because the roll rule imposes an additional constraint: The MONTH must not change when the WEEK_OF_MONTH is rolled. Taken together with add rule 1, the resultant date must be between Tuesday June 1 and Saturday June 5. According to add rule 2, the DAY_OF_WEEK, an invariant when changing the WEEK_OF_MONTH, is set to Tuesday, the closest possible value to Sunday (where Sunday is the first day of the week).
0
 
LVL 2

Author Comment

by:barrel
ID: 17102109
Yep, I have been reading the javadoc as well but this still leaves me puzzled.

Date 1: 2005-10-30 (and I checked, hh:ii:ss are set to 00:00:00)

set (Calendar.HOUR, 7) (which results in the expected value) should be the same as add (Calendar.HOUR, 7) on date with hours set to 0, which is the equivalent of set (calendar.HOUR, get (which is 0) + 7)


0
 
LVL 86

Expert Comment

by:CEHJ
ID: 17102246
>>My guess is that this has something to do with daylight savings/ summer vs winter time... Can anyone explain?

Very probably. In the case of my machine/timezone, since the clocks go back one hour at the end of October, the net effect is to add six hours, as opposed to seven
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 2

Author Comment

by:barrel
ID: 17102294
The funny thing is that not all date/time combinations on this day have the problem, only a few. I would expect a similar behaviour for all...
0
 
LVL 86

Expert Comment

by:CEHJ
ID: 17102341
>>not all date/time combinations on this day have the problem

Can you post an example that doesn't?
0
 
LVL 2

Author Comment

by:barrel
ID: 17102364
I'll post an example tomorrow, I am no longer at work ;-)

Anyway, colr__, CEHJ and RoyalNepal; thanks for your answers so far!
0
 
LVL 2

Expert Comment

by:RoyalNepal
ID: 17102503
Mr. barrel,
            as far as i understand...
 
SET METHOD:

lets say 07:00:00 is the current time..

and u do...  calendar.set(Calendar.HOUR, 9);

now the current time is... 09:00:00

ADD METHOD:        

lets say 07:00:00 is the current time.
and u do...  calendar.add(Calendar.HOUR, 9);

current time will  be 7 + 9 = 16 or 4...
whatever is the other difference....it might be because of time/regional difference...i am just guessing....:)

thanks
roy  

0
 
LVL 2

Author Comment

by:barrel
ID: 17102599
Actually, I receive two fields from the database which I combine into one GregorianCalendar (user requirements, I cannot help it ;-)

So; the first field is converted to a java.util.Date, no time is set (equal to 00:00:00). Additionally I have a java.sql.Time field which has no date.

So I know that I always start with hour => 0, minutes =>0 and seconds => 0 for the date. This date is used to initially set the time in the calendar, afterwhich I would like to add.

0
 
LVL 4

Expert Comment

by:astorer
ID: 17107495
Couple of things to check I think:

1. hour => 0 etc.  Do you mean hour == 0  (equals zero) ?  I think you do.

2. How do you know that is true? java.util.Date has no concept of timezone, daylight saving or anything.  The only time these matter is when you construct the Date from something else or convert a date into something else.

3. Assuming your Date is correct and the hour mins and secs *are* zero, we turn to the Calendar.  
After doing calendar.setTime(theDate); What is calendar.getTimeInMillis() ?
Does this equal your original 1130623200000L ?
If not, then the Calendar has decided to set a time?  (I suspect that will be OK)

4. After setting the hour, mins, secs, what is calendar.getTimeInMillis() in the two cases?
Again, trying to find if the Calendar is setting the time in the same way in both cases.
I suspect a difference may appear here.  If so, we need to find out why.

5. Just for completeness, what is calendar.getTimeZone() ?
 



0
 
LVL 4

Accepted Solution

by:
astorer earned 125 total points
ID: 17107613
In a Calendar, the underlying date is held as a time in milliseconds (long) and also held as separate int fields for year, month, day, hour, mins etc...
Calendar does its best to ensure that these are kept in sync.

But there is a difference between set() and add().
 - add() simply converts the field you pass into a number of milliseconds and then adds it to the underlying time (the long).  It will then go and update the other fields to match this
  - set() simply sets the separate field and does not update the time (the long).  The time is updated when you later do the get() - this performs a computerTime() set the the time ( the long).

Note that computerTime() *does* take account of daylight saving in its calculation.

So, using add() involves no daylight saving in the calculation.  using set() does involve daylight saving in the calculation.
This would lead to a one hour difference.

cheers
Andrew



0
 
LVL 2

Author Comment

by:barrel
ID: 17107697
Ok, thanks for the explanation. Things are much clearer. Lets say you're at midmight, close your eyes for 4 hours (and passed the daylightsaving moment), you will find that it is 3 in the morning and not four. Pretty simple actually ;-)

> So, using add() involves no daylight saving in the calculation.  using set() does involve daylight saving in the calculation.
> This would lead to a one hour difference.

You probably mean the other way around?



0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Java had always been an easily readable and understandable language.  Some relatively recent changes in the language seem to be changing this pretty fast, and anyone that had not seen any Java code for the last 5 years will possibly have issues unde…
Introduction This article is the first of three articles that explain why and how the Experts Exchange QA Team does test automation for our web site. This article explains our test automation goals. Then rationale is given for the tools we use to a…
Viewers learn about the “while” loop and how to utilize it correctly in Java. Additionally, viewers begin exploring how to include conditional statements within a while loop and avoid an endless loop. Define While Loop: Basic Example: Explanatio…
This tutorial will introduce the viewer to VisualVM for the Java platform application. This video explains an example program and covers the Overview, Monitor, and Heap Dump tabs.

760 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now