Calvin Raveenthran
Calvin Raveenthran

Reputation: 321

How do I add one day to a Calendar object and also account for daylight savings time?

I am trying to create a time limit for objects in a list. This could mean that the objects shelf life could be 23, 24 or 25 hours. Are there any Java libraries that could be useful? This is what I have so far.

My problem is that when I create a record at 9:30 am for example, it must be removed at 9:30 am on the following day. I get discrepancies when it is during the days which DST takes effect. The record is is either deleted at 8:30 or 10:30 depending if I spring forward or backward.

//baseValue = object that I want to check

Date dt = new Date();
Calendar c = Calendar.getInstance(); 
c.setTime(dt); 
c.add(Calendar.DATE, -1);

if(baseValue.getTime() < c.getTime()){
    array.remove(baseValue);
}

Upvotes: 2

Views: 1517

Answers (1)

user7605325
user7605325

Reputation:

The old classes (Date, Calendar and SimpleDateFormat) have lots of problems and design issues, including difficulty to deal with DST changes, 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 <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's 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.


To take care of DST changes, the ideal class is ZonedDateTime, which represents a date and time in a specific timezone. I also use the ZoneId class, which represents the timezone itself.

I'm using my timezone (America/Sao_Paulo), because here we have DST as well, but you can replace with yours (more on that below):

// create a date 1 day before DST change in Sao Paulo, at 9 AM
ZoneId zone = ZoneId.of("America/Sao_Paulo");
ZonedDateTime z = ZonedDateTime.of(2017, 10, 14, 9, 0, 0, 0, zone);

// get the next day, at 9 AM
ZonedDateTime nextDay = z.plusDays(1);

System.out.println(z);
System.out.println(nextDay);

The output is:

2017-10-14T09:00-03:00[America/Sao_Paulo]
2017-10-15T09:00-02:00[America/Sao_Paulo]

Note that the offset changed from -03:00 to -02:00 - it's due to DST starting in São Paulo timezone (clocks move forward 1 hour). But also note that the time (9 AM) was preserved correctly.

If we take the difference in hours, we can see that it's correct:

System.out.println(ChronoUnit.HOURS.between(z, nextDay));

The output is:

23

Which correctly means that 23 hours has passed between those 2 dates (because of clocks shifting 1 hour forward, so 1 hour is "lost").


In your case, you need to know if 1 day has already passed, so you just call:

long days = ChronoUnit.DAYS.between(z, nextDay);

In this case, days will be 1 (even if the difference in hours calculated above is 23, because the API is smart enough to consider DST effects).

So in your case, you just need to check if the difference in days is 1 (or greater than 1, I don't know) and do all that it needs to be done.

If you need to get the current date/time, you can call ZonedDateTime.now(zone).


To use your timezone instead of mine, first note that the API uses IANA timezones names (always in the format Continent/City, like America/Sao_Paulo or Europe/Berlin). Avoid using the 3-letter abbreviations (like CST or PST) because they are ambiguous and not standard.

You can get a list of timezones names with ZoneId.getAvailableZoneIds() - then choose the one that fits best to your case.

You can also use ZoneId.systemDefault() - it returns the system's default timezone. But this can be changed without notice - even at runtime - so it's recommended to use an explicit timezone.

Upvotes: 5

Related Questions