Here is a part from my code:
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
GregorianCalendar gc = new GregorianCalendar();
gc.setTime(dateFormat.parse(jahr+"-"+monat+"-"+tag));
And now I want to print out some moving easter holidays in Germany.
gc.add(Calendar.DAY_OF_MONTH, -2);
System.out.println("Karfreitag;"+dateFormat.format(gc.getTime()));
gc.add(Calendar.DAY_OF_MONTH, +3);
System.out.println("Ostermontag;"+dateFormat.format(gc.getTime()));
gc.add(Calendar.DAY_OF_MONTH, +38);
To make it a bit clearer. Eastersunday was on 16.04.2017 this year.
So the first print out is working fine, 'Karfreitag' is two days before eastersunday. So it was the 14.04.2017.
Moving on to eastermonday throws a problem. Eastermonday is the day after eastersunday. Unfortuanately I have to add +3 days because I overwrote the eastersunday date with the 'Karfreitag' date.
So I want to know if it is possible to make the date from eastersunday fix so that I have to change my 3th line into:
gc.add(Calendar.DAY_OF_MONTH, +1);
It think this could be very easy but I have no clue how to change this in a proper way.
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()));
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);
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.