Link to home
Start Free TrialLog in
Avatar of barrel
barrel

asked on

Gregorian Calendar one hour off

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
Avatar of colr__
colr__

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.
Avatar of barrel

ASKER

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....
  //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???
Avatar of barrel

ASKER

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.

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
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).
Avatar of barrel

ASKER

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)


Avatar of CEHJ
>>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
Avatar of barrel

ASKER

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...
>>not all date/time combinations on this day have the problem

Can you post an example that doesn't?
Avatar of barrel

ASKER

I'll post an example tomorrow, I am no longer at work ;-)

Anyway, colr__, CEHJ and RoyalNepal; thanks for your answers so far!
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  

Avatar of barrel

ASKER

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.

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() ?
 



ASKER CERTIFIED SOLUTION
Avatar of astorer
astorer

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of barrel

ASKER

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?