0

Have an issue where, when clocks are moved due to a Daylight savings time (twice a year), dates are not correct in Java (I am based in Central Europe: GMT+2 in summer, GMT+1 in winter)

If time is moved 1 hour ahead, new Date() still returns old time (1 hour behind of current time).

In Java 7, can this be solved, without restarting the Jboss application servers?

If I change the time manually in Windows, reproduce the problem: Date is not updated to the system date unless jboss is restarted.

Calendar c = Calendar.getInstance();
c.setTime(new Date());
Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
kandan
  • 715
  • 2
  • 12
  • 39
  • 1
    This behavior means that you are probably not using correct time zones, so you might want to overhaul how your app works with timestamps. And you might want to migrate away from `Date` altogether, since that class does not support time zones or any sort of DST transitions, to my knowledge. Use classes from `java.time` package, or if you don't have Java8 yet, use at least the `java.util.Calendar`. – M. Prokhorov Jun 08 '17 at 11:18
  • 4
    A Date object has no time zone information, it's just a number of millis since the epoch. It happens to show the default timezone when printed but it doesn't affect its state. – assylias Jun 08 '17 at 11:26
  • was using Calendar and setTime(new Date()). Just want to get current system date without restarting. If system date is manually updated, current calendar date is not updated without restarting. – kandan Jun 08 '17 at 11:27
  • 1
    What do you mean current calendar date is not updated? Expected behaviour is the `Calendar` object you created before updating the system time should keep its value. A new `Calendar` object created after updating system time ought to give you the new time (I am unsure whether a caching issue might prevent it, but it sounds unlikely to me). – Ole V.V. Jun 08 '17 at 11:41
  • Did some test in Windows, using jboss, and after updating local time, a new Calendar object returned old hour. Only after restarting jboss, it returned the current windows time. – kandan Jun 08 '17 at 11:43
  • As assylias commented, **do *not* trust the `toString`method**. That method applies your JVM’s current default time zone while generating the string. The `Date` itself is always is UTC. One of many reasons to avoid these terrible old legacy date-time classes. Use java.time instead. – Basil Bourque Jun 08 '17 at 18:59

2 Answers2

2

Use ZonedDateTime class from JDK 8 java.time. It accommodates the Daylight Saving Time changes. Refer the details at : https://docs.oracle.com/javase/8/docs/api/java/time/ZonedDateTime.html

Pallavi Sonal
  • 3,661
  • 1
  • 15
  • 19
2

In Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes.

With this, you can handle DST changes easily.

First, you can use the org.threeten.bp.DateTimeUtils to convert from and to Calendar.

The following code converts the Calendar to org.threeten.bp.Instant, which is a class that represents an "UTC instant" (a timestamp independent of timezone: right now, at this moment, everybody in the world are in the same instant, although their local date and time might be different, depending on where they are).

Then, the Instant is converted to a org.threeten.bp.ZonedDateTime (which means: at this instant, what is the date and time at this timezone?). I also used the org.threeten.bp.ZoneId to get the timezone:

Calendar c = Calendar.getInstance();
c.setTime(new Date());

// get the current instant in UTC timestamp
Instant now = DateTimeUtils.toInstant(c);

// convert to some timezone
ZonedDateTime z = now.atZone(ZoneId.of("Europe/Berlin"));

// today is 08/06/2017, so Berlin is in DST (GMT+2)
System.out.println(z); // 2017-06-08T14:11:58.608+02:00[Europe/Berlin]

// testing with a date in January (not in DST, GMT+1)
System.out.println(z.withMonth(1)); // 2017-01-08T14:11:58.608+01:00[Europe/Berlin]

I've just picked some timezone that uses Central Europe timezone (Europe/Berlin): you can't use those 3-letter abbreviations, because they are ambiguous and not standard. You can change the code to the timezone that suits best for your system (you can get a list of all available timezones with ZoneId.getAvailableZoneIds()).

I prefer this solution because it's explicit what timezone we're using to display to the user (Date and Calendar's toString() methods use the default timezone behind the scenes and you never know what they're doing).

And internally, we can keep using the Instant, which is in UTC, so it's not affected by timezones (and you can always convert to and from timezones whenever you need) - if you want to convert the ZonedDateTime back to an Instant, just use the toInstant() method.


Actually, if you want to get the current date/time, just forget the old classes (Date and Calendar) and use just the Instant:

// get the current instant in UTC timestamp
Instant now = Instant.now();

But if you still need to use the old classes, just use DateTimeUtils to do the conversions.


The output of the examples above are the result of the ZonedDateTime.toString() method. If you want to change the format, use the org.threeten.bp.format.DateTimeFormatter class (take a look at the javadoc for more details about all the possible formats):

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss z X");
// DST (GMT+02)
System.out.println(formatter.format(z)); // 08/06/2017 14:11:58 CEST +02
// not DST (GMT+01)
System.out.println(formatter.format(z.withMonth(1))); // 08/01/2017 14:11:58 CET +01