28

I'm trying to make a deep copy of an object, including a GregorianCalendar instance. I'm always wary of using clone() and it doesn't seem to have been overridden here, so I'm just doing the copy field by field. Ideally, there'd be a copy constructor, which I could use like so:

GregorianCalendar newCalendar = new GregorianCalendar(oldCalendar);

Unfortunately I can't find any such functionality in the API and am stuck trying to figure out which fields I need to get an exact copy. So, to make a copy of one of these calendars, how would you do it? Am I missing some simple shortcut here?

wds
  • 31,873
  • 11
  • 59
  • 84

4 Answers4

41

java.util.Calendar has overridden clone() which is working, so use it. Furthermore, Calendar doesn't have a deep data hierarchy — its data are mainly ints.

To extend the answer, you can call SerializationUtils.clone(…) (from Apache commons-lang) on any object which makes a deep copy, if the whole data hierarchy implements Serializable.

Daniel Werner
  • 1,350
  • 16
  • 26
Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
  • I wasn't quite clear on why I'm avoiding `clone()`. In this case I'm just following advice from Joshua Bloch on making defensive copies. Since clone() could conceivably be calling a subclass clone() method I could end up with a non-clean copy. A bit academic in my case, but that's why I was avoiding it. – wds Mar 23 '10 at 10:46
  • as I said, the data in the Calendar isn't affected by this problem - all its data is primitive. And the clone method in `Calendar` works fine. – Bozho Mar 23 '10 at 10:50
  • His point is that you could conceivably make a subclass that keeps a reference to the original calendar instance and keeps that reference around. If, say, all setters would set values in the original instance instead of the new one, your "deep" copy will now be compromised. – wds Mar 23 '10 at 10:58
  • 1
    You could also just do something like this: Calendar cal_copy = Calendar.getInstance(); cal_copy.setTimeInMillis(cal.getTimeInMillis()); – Christopher Masser May 07 '14 at 08:47
2

tl;dr

Use the modern java.time classes that supplanted GregorianCalendar.

GregorianCalendar.from(                     // Convert from modern object to legacy class object.
    myGregorianCalendar.toZonedDateTime()   // Convert from legacy to modern.
)                                           // Returns a new `GregorianCalendar` object.

But better to stop using GregorianCalendar altogether, and just use ZonedDateTime.

ZonedDateTime

The terrible GregorianCalendar class was years ago supplanted by the java.time classes defined in JSR 310. Specifically replaced by the ZonedDateTime class.

You can convert back and forth using new to…/from… methods added to the old classes.

ZonedDateTime zdt = myGregorianCalendar.toZonedDateTime() ;

Going the other direction.

GregorianCalendar myGregorianCalendar = GregorianCalendar.from( zdt ) ;

To address your Question specifically, combine these two conversions to get another GregorianCalendar object.

GregorianCalendar gc = GregorianCalendar.from( myGregorianCalendar.toZonedDateTime() ) ;

About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
1

Specifically, the quickest line of code to copy a Calendar is:

GregorianCalendar newCalendar = (Calendar)(oldCalendar.clone());
Everyone_Else
  • 3,206
  • 4
  • 32
  • 55
  • Better to case to GregorianCalendar `GregorianCalendar newCalendar = (GregorianCalendar ) oldCalendar.clone();` – Yetti99 Sep 24 '21 at 18:53
-8

Uh, clone() sucks.

Is it that hard? You only have to set 3 things, I believe, the time, the time zone, and the locale. All those fields have getters and setters. Make a quite utility method to return a copy?

piyo
  • 461
  • 1
  • 5
  • 18