Calendar clone problem

There are two ways to get Calendar instance, one to use GregorianCalendar.getInstance() and another DateFormat.getCalandar(). There is one basic problem if you use DateFormat.getCalendar() approcah. Let sess this by an example.
Cloning of Caneldar object from DateFormat.getInstance():-


import java.text.DateFormat;
import java.util.Calendar;

public class Test {
public static void main(String[] args)
{
DateFormat dateFormat = DateFormat.getDateInstance();
Calendar baseCalendar = dateFormat.getCalendar();
Calendar workCalendar = null;
for(int i = 1; i < 6; i++)
{
workCalendar = (Calendar) baseCalendar.clone();
workCalendar.add(Calendar.MONTH, 1);
System.out.println("base:" +dateFormat.format(baseCalendar.getTime()));
System.out.println("work:" +dateFormat.format(workCalendar.getTime()));
workCalendar = null;
}
}
}

Here we are getting Canendar object using DateFormat.getCalendar() method and cloning it and modifying the object that we received from colning. We expect this to be a deep copy but it is not. Even if we modify clonned object(workCalendar) our original object will also change.


Output:-

base:Jul 31, 1930
work:Aug 31, 1930
base:Aug 31, 1930
work:Sep 30, 1930
base:Sep 30, 1930
work:Oct 30, 1930
base:Oct 30, 1930
work:Nov 30, 1930
base:Nov 30, 1930
work:Dec 30, 1930

If you see after each ineration or base object is also changing even if we changed the clonned object.

Now let us see this by a calendar instance using GregorianCalendar.

Here is the program:-

public class Test {
public static void main(String[] args)
{
DateFormat dateFormat = DateFormat.getDateInstance();
Calendar baseCalendar = GregorianCalendar.getInstance();
Calendar workCalendar = null;
for(int i = 1; i < 6; i++)
{
workCalendar = (Calendar) baseCalendar.clone();
workCalendar.add(Calendar.MONTH, 1);
System.out.println("base:" +dateFormat.format(baseCalendar.getTime()));
System.out.println("work:" +dateFormat.format(workCalendar.getTime()));
workCalendar = null;
}
}
}

Output:-
base:Jul 31, 2010
work:Aug 31, 2010
base:Jul 31, 2010
work:Aug 31, 2010
base:Jul 31, 2010
work:Aug 31, 2010
base:Jul 31, 2010
work:Aug 31, 2010
base:Jul 31, 2010
work:Aug 31, 2010

If you see the output then you realize that even if we modify the clonned object base does not change.

Calendar oject created using DateFormat can create possible bug in the program. It is always bette to use GregorianCalendar approcah.

Advertisements

4 responses to “Calendar clone problem

  1. I think the issue isn’t the depth of cloning, but that DateFormat.format modifies it’s Calendar object, the same object you have a reference to as “baseCalendar”.

    Changing line eight of Test.java gives the expected output:


    8: Calendar baseCalendar = (Calendar) dateFormat.getCalendar().clone();

    base:Oct 28, 1930
    work:Nov 28, 1930
    base:Oct 28, 1930
    work:Nov 28, 1930
    base:Oct 28, 1930
    work:Nov 28, 1930
    base:Oct 28, 1930
    work:Nov 28, 1930
    base:Oct 28, 1930
    work:Nov 28, 1930
    

    I ran into this in a slightly different way and posted about it at the JavaRanch forums.

    • Thanks for you valuable comments!
      You are right this is not the problem if cloning. What I think is that the main problem is “dateFormat.format(workCalendar.getTime())” this line will modify the calendar object associated with dateFormat (Ie baseCalendar). If you check the code of java.text.DateFormat.format() and check line 876 you will see that “calendar.setTime(date);”. what I think is that this will modify the baseCalendar.

      Point is that there is a Calendar associated with DateFormat and if we call DaTeFormat.format(Date ) this will update the calendar with the input Date. What I think is that this format() method should not modify the calendar instance associated with DateFormat.

      In second case after calling “dateFormat.format(workCalendar.getTime())” calendar instance associated with dateFormat is modified, but since we are not reading baseCalendar for DateFormat so we are safe.

  2. After being bitten by this and sorting out that it wasn’t due to depth of clone, my next target was also that “format was wrong”, but after more thinking my opinion is now that it could be better documented. I don’t believe that what it is doing “seems pretty clear”. Sure, it can be deduced, but it is not clearly stated.

    I am waffling on the idea that DateFormat’s setCalendar should do defensive copying. That could be an unnecessary copy in the case of df.setCalendar(new GregorianCalendar()). I’m willing to cede that it’s getCalendar also shouldn’t do a defensive copy if there is a use to see the side effect of other DateFormat methods or to be able to indirectly mutate a DateFormat object (ugh). If the jvm can optimize the double copy away and no useful reason for having a hand on DateFormat’s guts exists (the Calendar object is protected and handing it out is breaking that encapsulation), then I would prefer those methods make the defensive copies.

  3. nice article ….

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s