As the add
method changes the current calendar instance, one solution is to create another one, using the clone()
method:
// clone it before changing it
GregorianCalendar other = (GregorianCalendar) gc.clone();
gc.add(Calendar.DAY_OF_MONTH, -2);
System.out.println("Karfreitag;" + dateFormat.format(gc.getTime()));
other.add(Calendar.DAY_OF_MONTH, 1);
System.out.println("Ostermontag;" + dateFormat.format(other.getTime()));
Java new Date/Time API
The old classes (Date
, Calendar
and SimpleDateFormat
) have lots of problems and design issues, and they're being replaced by the new APIs.
If you're using Java 8, consider using the new java.time
API. It's easier, less bugged and less error-prone than the old APIs.
If you're using Java 6 or 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, you'll also need the ThreeTenABP (more on how to use it here).
The code below works for both.
The only difference is the package names (in Java 8 is java.time
and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp
), but the classes and methods names are the same.
As you're dealing with dates (day/month/year), you can use the LocalDate
class. In this new API, classes are immutable, so the methods that add a number of days always create another object:
LocalDate easter = LocalDate.parse("2017-04-16");
// minusDays and plusDays return a new LocalDate, keeping the original unchanged
System.out.println("Karfreitag;" + easter.minusDays(2));
System.out.println("Ostermontag;" + easter.plusDays(1));
The output will be:
Karfreitag;2017-04-14
Ostermontag;2017-04-17
If you have the day, month and year as int
values, you can create a LocalDate
using the of
method:
int tag = 16;
int monat = 4;
int jahr = 2017;
// Easter
LocalDate easter = LocalDate.of(jahr, monat, tag);
Convert from/to GregorianCalendar
If you still need to work with GregorianCalendar
, you can easily convert it from/to the new API. In Java 8 there are new methods to do the conversion, while in Java 6/7 ThreeTen Backport there's the org.threeten.bp.DateTimeUtils
class:
// Java 8: convert calendar to local date
LocalDate dt = gc.toZonedDateTime().toLocalDate();
// Java 7: convert calendar to local date
LocalDate dt = DateTimeUtils.toZonedDateTime(gc).toLocalDate();
To convert the LocalDate
back to GregorianCalendar
is a little bit tricky. A LocalDate
has only the date fields (day, month and year), while a GregorianCalendar
represents a "full date": a date and time in a specific timezone.
So, when converting a LocalDate
to a GregorianCalendar
, you must make some assumptions about the time (hour, minutes, etc) and the timezone. One example is to set the time to midnight, and use the JVM default timezone:
// Java 8: convert local date to calendar (midnight in JVM default timezone)
GregorianCalendar cal = GregorianCalendar.from(dt.atStartOfDay(ZoneId.systemDefault()));
// Java 7: convert local date to calendar (midnight in JVM default timezone)
GregorianCalendar cal = DateTimeUtils.toGregorianCalendar(dt.atStartOfDay(ZoneId.systemDefault()));
You can also convert to any other time of the day and change the timezone to whatever you want:
// set to 10:30 AM in Berlin timezone
dt.atTime(10, 30).atZone(ZoneId.of("Europe/Berlin"));
Or you can use the ZonedDateTime
returned by toZonedDateTime()
directly, and extract the LocalDate
part when printing:
// convert calendar to ZonedDateTime
ZonedDateTime z = gc.toZonedDateTime();
// print just the LocalDate part
System.out.println("Karfreitag;" + z.minusDays(2).toLocalDate());
System.out.println("Ostermontag;" + z.plusDays(1).toLocalDate());
// get the original calendar back
GregorianCalendar cal = GregorianCalendar.from(z);
This new API has lots of new types and allows you to choose the best for each case.