aspose file tools*
The moose likes Java in General and the fly likes DateFormat.format unexpected side effect Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Java » Java in General
Bookmark "DateFormat.format unexpected side effect" Watch "DateFormat.format unexpected side effect" New topic
Author

DateFormat.format unexpected side effect

Jacob Anawalt
Ranch Hand

Joined: Oct 27, 2010
Posts: 64

While using a SimpleDateFormat and a couple of GregorianCalendar objects in what I thought was a simple piece of code, I came across something that was quite unexpected. When I would call DateFormat.format, one of my Calendar objects would get updated to reflect the Date passed into the function.

After a lot of unfruitful searching and testing I found that the affected Calendar object was the one I passed into DateFormat.setCalendar. If I didn't set the calendar or if I set it using a defensive copy then the program worked as I expected.

As far as I can tell this behavior is undocumented in the 1.6 Javadocs. DateFormat.setCalendar doesn't say if it makes a copy or not and DateFormat.format doesn't mention it will change the Calendar object it was given. I haven't tested but now I won't be surprised if DateFormat.parse also changes it's related Calendar object. I think DateFormat.setCalendar should have done the defensive copy.

Are there well-known patterns that use this side-effect on the Calendar object given to DateFormat.setCalendar? How about a list of gotchas that like Odi's Date and Time in Java that warns of this side-effect? I didn't find them and that makes me think I did something unusual.

Here's the test case:



And the output:
CalendarCloneTest wrote:
cal: 2010-10-27
cal : 2010-Oct-27 16:56 EDT
cal2: 2010-Oct-27 16:56 EDT (clone)
cal : 2010-Oct-27 16:56 EDT
cal2: 2010-Nov-03 00:56 EDT (post-add/set)
cal : 2010-Nov-03 00:56 EDT (post-cal2-add/set)
cal2: 2010-Nov-03 00:56 EDT


Jacob
Bobby Smallman
Ranch Hand

Joined: Sep 09, 2010
Posts: 107
To my knowledge there are a few different ways this, for lack of a better word "linked cloning" comes about. I had not encountered it in exactly the same way you have, so it sparked my interest. Check out this link for a similar yet different issues to potentially help shed some light on the situation, I look forward to hearing what others on here say.

http://techvivek.wordpress.com/2010/07/31/calendar-clone-problem/


Everyday in every way, we get a little better.
Campbell Ritchie
Sheriff

Joined: Oct 13, 2005
Posts: 40052
    
  28
I think this question is too difficult for "beginning Java" so I shall move it.
Rob Spoor
Sheriff

Joined: Oct 27, 2005
Posts: 19785
    
  20

Unless the documentation specifies otherwise you should always assume the worst.


SCJP 1.4 - SCJP 6 - SCWCD 5 - OCEEJBD 6
How To Ask Questions How To Answer Questions
Kurt Van Etten
Ranch Hand

Joined: Sep 07, 2010
Posts: 98
Bobby Smallman wrote:To my knowledge there are a few different ways this, for lack of a better word "linked cloning" comes about. I had not encountered it in exactly the same way you have, so it sparked my interest. Check out this link for a similar yet different issues to potentially help shed some light on the situation, I look forward to hearing what others on here say.

http://techvivek.wordpress.com/2010/07/31/calendar-clone-problem/


In that link, they refer to the weird behavior as a cloning problem, but the code is doing exactly the same thing that Jacob's example does. You can verify that it's not the cloning part that causes the behavior by just instantiating a separate, second Calendar object instead of using the clone function--you will get similar behavior. Although the API documentation doesn't spell it out, it seems pretty clear that the DateFormat.format() method is applying the date/time parameter value to its calendar as part of the formatting process.
Jacob Anawalt
Ranch Hand

Joined: Oct 27, 2010
Posts: 64

I too thought my problem was with clone being a shallow and not a deep copy when I first ran into this. My actual program used a loop similar to the Calendar clone problem example on Vivek's Tech Blog but as my test case shows instead of getting the Calendar from the DateFormat object, I was setting my "baseCalendar" (cal) into my DateFormat object. In the end the effect is the same, the DateFormat and my "baseCalendar" are a reference to the same object being modified with each call to DateFormat.format.

Since I just got bit it currently feels like a bug that DateFormat.setCalendar doesn't itself make the defensive copy and perhaps Vivek will feel it is a bug that DateFormat.getCalendar doesn't appear to return a defensive copy. I recognize that others may be taking advantage of this side effect as a feature so the most I hope for is better education/documentation.

I agree, assuming the worst is safest. Being new to Java I was wondering if this side-effect was commonly used as a feature or listed somewhere as a gotcha.
Jacob Anawalt
Ranch Hand

Joined: Oct 27, 2010
Posts: 64

I searched through the bug list and found a few that show similar mistakes or comments on how to use DateFormat related to get/setCalendar and parse/format. Here are a couple of notable ones with their problems, workarounds and the bug triage evaluation comments paraphrased. See the actual bug reports for exact details.

Bug 6609675: [Fmt-Da] DateFormat.parse() on a timezone changes its calendar's timezone

Problem:
DateFormat.parse() has an undocumented side-effect: calling it with a string that contains a timezone, different from one that was explicitly set, will overwrite the DateFormat's internal timezone with that of the string.

Further attempts to use the DateFormat for formatting will use the new timezone.

Workaround:
Use separate DateFormats for formatting and parsing.

Evaluation:
- The parse methods may overwrite the date-time fields and the TimeZone value of the embedded calendar (DateFormat.calendar).



Bug 6609452: [Fmt-Da] DateFormat.getCalendar().getTime() outputs wrong year

Problem:
Inputting DateFormat.calendar.getTime() into DateFormat.format gives the wrong results.

Workaround:
Use a different source for the Calendar object or make a new Date.

Evaluation:
The Calendar instance in a DateFormat is first used to calculate the base year for the 2-digit year handling. Please don't rely on the [ DateFormat ] Calendar fields.

 
Consider Paul's rocket mass heater.
 
subject: DateFormat.format unexpected side effect