Calendar documentation clarification
Hello. The java.util.Calendar class probably behaves correctly, but I can't figure out how it is supposed to behave from reading the documentation.
Whatshould the behavior be if youset a negative value on the month field? Currently I see no effect.
Thanks,
Laird
[304 byte] By [
ljnelsona] at [2007-10-1 12:05:47]

Correction: the behavior seems to be indeterminate. A simple test of setting a calendar to one month, and then setting the month field to -1 works properly. Anything more complicated appears to have no effect. I suspect this all falls under the horrifically underdocumented semantics of the set() method?
L
you mean you call Calendadar.set(Calendar.MONTH, x) with x outside the range 0 - 11? I think that behavior is undefined - if you want to change the month field relative to the current month use the add or roll methods.Good LuckLee
tsitha at 2007-7-10 14:01:04 >

Works like I'd expect: The month of -5 in the year 2005 is August of 2004. (Remember, Jan=0, Feb=1, ...)>>> cal.getTime()Mon Apr 25 12:34:19 PDT 2005>>> cal.set(Calendar.MONTH, -5)>>> cal.getTime()Wed Aug 25 12:34:19 PDT 2004
jverda at 2007-7-10 14:01:04 >

And if I call cal.setLenient(false) then I get an IllegalArgumentException, not on the set() call, but on getTime().
jverda at 2007-7-10 14:01:04 >

Not to mention that with that particular field using a raw int is bad practice anyway (the paramater shouldn't even be an int but we don't have any choice there). Use Calendar.JANUARY, etc. Then it's impossible to mess it up. As for the other fields, I would guess that the behavior depends on the leniency flag. Just a guess, though.
> And if I call cal.setLenient(false) then I get> an IllegalArgumentException, not on the set() call,> but on getTime().There's a WTF.
> > And if I call cal.setLenient(false) then I
> get
> > an IllegalArgumentException, not on the set()
> call,
> > but on getTime().
>
> There's a WTF.
If you look @ Cal's/GC's guts, I think that it's lazy. set() just sets the value and a dirty flag. Anything that actually has to interpret the values checks the dirty flag, and interprets them if it's set.
Approximately.
jverda at 2007-7-10 14:01:04 >

from the docs:
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.
tsitha at 2007-7-10 14:01:04 >

> Works like I'd expect: The month of -5 in the year
> 2005 is August of 2004. (Remember, Jan=0, Feb=1,
> ...)
>
> > >>> cal.getTime()
> Mon Apr 25 12:34:19 PDT 2005
>
> >>> cal.set(Calendar.MONTH, -5)
> >>> cal.getTime()
> Wed Aug 25 12:34:19 PDT 2004
>
Thanks; yes, in isolated cases it does indeed work like this. Here, for example, is a quick JUnit test case I wrote that shows how it behaves:
public void testSetMonth() throws Exception {
final Calendar calendar = Calendar.getInstance();
assertNotNull(calendar);
calendar.set(2006, Calendar.JANUARY, 1);
calendar.set(Calendar.MONTH, -1);
assertEquals(2005, calendar.get(Calendar.YEAR));
assertEquals(1, calendar.get(Calendar.DAY_OF_MONTH));
assertEquals(Calendar.DECEMBER, calendar.get(Calendar.MONTH));
calendar.set(2005, Calendar.MARCH, 12);
calendar.set(Calendar.MONTH, -1);
assertEquals(2004, calendar.get(Calendar.YEAR));
assertEquals(Calendar.DECEMBER, calendar.get(Calendar.MONTH));
assertEquals(12, calendar.get(Calendar.DAY_OF_MONTH));
}
Note the slightly weird behavior of setting the month to -1 when the original date is March 12, 2005 (above); the date returned is December 12, 2004. This seems to indicate that Calendar.MONTH is really interpreted as "month of the current year", so -1 would indeed be December 2004.
I'm trying to isolate the other behavior I'm seeing; I'll post another case here when I do. Briefly it looks like after setting certain combinations of fields, setting the Calendar.MONTH field to a negative value simply has no effect at all.
L
> Note the slightly weird behavior of setting the month
> to -1 when the original date is March 12, 2005
> (above); the date returned is December 12, 2004.
> This seems to indicate that Calendar.MONTH is really
> y interpreted as "month of the current year", so -1
> would indeed be December 2004.
Careful. "Current" year could mean what we humans consider to be the "real" year right "now" or it could mean the year as set in the cal.
If you mean the latter, then, yes, of course, that's how I'd expect it to behave.
If the year is set to N, and the month to M, then you count M months (pos or neg) from Jan of that year.
So the year has been set to 2005. Then you set the month to 2004, so since month 0 of 2005 is Jan '05, you'd expect month -1 of the same year to be Dec '04.
>
> I'm trying to isolate the other behavior I'm seeing;
> I'll post another case here when I do. Briefly it
> looks like after setting certain combinations of
> fields, setting the Calendar.MONTH field to a
> negative value simply has no effect at all.
>
> L
jverda at 2007-7-10 14:01:05 >

> I'm trying to isolate the other behavior I'm seeing;
> I'll post another case here when I do. Briefly it
> looks like after setting certain combinations of
> fields, setting the Calendar.MONTH field to a
> negative value simply has no effect at all.
Oh, okay.
Yeah, if you could post an example, that would be cool.
jverda at 2007-7-10 14:01:05 >

Good; as I suspected this was a ridiculously ******* pilot error. As they usually are.Thanks, folks.L
> Good; as I suspected this was a ridiculously *******> pilot error. As they usually are.The frustration at being so blinkin' stupid is offset by the releif that there is order in the universe, eh? Been there, done that. Weekly, at least. :-)
jverda at 2007-7-10 14:01:05 >

In case anyone cares, here is the test case that was designed to isolate this bug, but that did not, thus leading me straight to where I had screwed up in my code:
public void testCalendarBug() throws Exception {
final Calendar calendar = Calendar.getInstance();
assertNotNull(calendar);
calendar.set(2005, Calendar.JANUARY, 11); // January 11, 2005
calendar.add(Calendar.YEAR, 1); // January 11, 2006
calendar.set(Calendar.MONTH, -1); // Expecting December 11, 2005
assertEquals(345, calendar.get(Calendar.DAY_OF_YEAR));
}
I had thought (incorrectly) that perhaps the fact that I had most recently set the MONTH field to a negative value but was going after the DAY_OF_YEAR field that some sort of arcane "last field set" rule was in effect. Turned out the problem was much simpler (and utterly unrelated to this topic).
Thanks,
L
> If you look @ Cal's/GC's guts, I think that it's
> lazy. set() just sets the value and a dirty flag.
> Anything that actually has to interpret the values
> checks the dirty flag, and interprets them if it's
> set.
I know, but IMO that's no excuse for passively accepting invalid input, and then failing an an unspecified later time with an IllegalArgumentException on a method that accepts no arguments.And maybe (I didn't research) the exception doesn't contain the information needed to diagnose the problem. If have to pick between correct and fast, I'll take correct.
> > If you look @ Cal's/GC's guts, I think that it's
> > lazy. set() just sets the value and a dirty flag.
> > Anything that actually has to interpret the values
> > checks the dirty flag, and interprets them if it's
> > set.
>
> I know, but IMO that's no excuse for passively
> accepting invalid input, and then failing an an
> unspecified later time with an
> IllegalArgumentException on a method that accepts no
> arguments.And maybe (I didn't research) the
> exception doesn't contain the information needed to
> diagnose the problem. If have to pick between
> correct and fast, I'll take correct.
I wouldn't call this incorrect. Maybe IllegalStateException or whatever it's called would have been better, but since you might be setting a number of fields in order to get your desired date, it's certainly possible that you'd pass through an invalid date on the way. I don't see any reason to force the user to set the fields in a particular order just to avoid a temporary invalid state.
Good point, but at best it's the wrong exception to throw. In fact, that exception that you mentioned is conspicuously absent from the JDK, to the extent that I made one myself.
> Good point, but at best it's the wrong exception to
> throw.
I can see the logic behind it--an illegal arg is ultimately what led to the problem.
However, the arg was accepted by the method that took it, and it resulted in an invalid state.
So, yeah.
> In fact, that exception that you mentioned is
> conspicuously absent from the JDK,
Yeah, I hate that this doesn't exist. :-)
http://java.sun.com/j2se/1.4.2/docs/api/java/lang/IllegalStateException.html
Yes, I know about that one, but it "Signals that a method has been invoked at an illegal or inappropriate time. In other words, the Java environment or Java application is not in an appropriate state for the requested operation."
Maybe it's just me, but that doesn't read to me like "the object or class is not in an appropriate state..." Perhaps I'm being too nitpicky? In looking at its subclasses it looks like this exception is pretty much what I'm after. I had always thought of it as a "VM is in an invalid state" sort of thing but I think I was mistaken.
> Yes, I know about that one, but it "Signals that a
> method has been invoked at an illegal or
> inappropriate time. In other words, the Java
> environment or Java application is not in an
> appropriate state for the requested operation."
>
Yeah, that second bit: In other words, the Java environment or Java application is not in an appropriate state for the requested operation.
In other words, you're passing through an invalid state, have set some of the fields needed to get to a valid state, but not all, and then called, say, getTime(). This was an inappropriate time to call it, and the application (well, okay, the object) is in an inappropriate state.
Of course, you might have just plain set bogus values because you're a doofus, but I'll take this one in that case too.
> Maybe it's just me, but that doesn't read to me like
> "the object or class is not in an appropriate
> state..." Perhaps I'm being too nitpicky?
I think so. I'd argue for better docs on that exception more than anything. The name of it seems pretty self-explanatory to how I'd use it and expect it to be used, and the docs, while a little weird, aren't totally incompatible with that.
Subclasses: Yeah. I hadn't even looked at them, but I agree. They kind of bolster it being what you think it migh be the name, docs notwithstanding.