1

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.

adama
  • 537
  • 2
  • 10
  • 29
  • 1
    You are using old date-time classes that are now legacy, supplanted by the java.time classes. Instead of `GregorianCalendar`, use `ZonedDateTime`. For a date-only value without time of day, use `LocalDate`. – Basil Bourque Oct 24 '17 at 14:40

3 Answers3

4

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.

4

Start using java.time.LocalDate. It provide a LocalDate.plusDays(long) that return a copy of the instance.

Returns a copy of this LocalDate with the specified number of days added.

Like this :

LocalDate tomorrow = LocalDate.now().plusDays(1);

And you can get an instance using LocalDate.of(int, int, int) like :

LocalDate date = LocalDate.of(year, month, day);

NOTE: This is a shorter version of Hugo's answer, just realise that there were to part in his answer...

AxelH
  • 14,325
  • 2
  • 25
  • 55
2

You can use DateUtils.addDays like this:

DateUtils.addDays(gc.getDate(), -2).getTime()

It needs a Date object (you can use gc.getDate() for this) and int number of days to add as arguments and also return a Date object without modifying your original gc.

System.out.println("Karfreitag;"+dateFormat.format(DateUtils.addDays(gc.getDate(), -2).getTime()));
System.out.println("Ostermontag;"+dateFormat.format(DateUtils.addDays(gc.getDate(), 3).getTime()));
System.out.println("something else;"+dateFormat.format(DateUtils.addDays(gc.getDate(), 38).getTime()));

In Android its available from API level 3,in Java you'll have to use Apache Commons

Ashish Ranjan
  • 5,523
  • 2
  • 18
  • 39
  • 2
    You should probably add where this DateUtils comes from since Apache Commons isn't part of the standard java library. – OH GOD SPIDERS Oct 24 '17 at 12:19
  • 1
    Is it still intersting to use apache commons for date since Java 8 ? I don't know it enough but my current usage are always done in `java.time.*` API – AxelH Oct 24 '17 at 12:46