JAVA_CAT
JAVA_CAT

Reputation: 859

Hours duration calculation issue using two Date objects

I have a method in my class to calculate the hourly duration of two dates.

public static double getDuration(Date startDate, Date endDate){
    return (endDate.getTime()-startDate.getTime()) / (1000.0 * 60 * 60);
}

My input dates are 28-10-2017 14:00 and 29-10-2017 02:00. During Daylight Saving periods duration gives 13.0 which is wrong.

If I write a simple program with this input I am getting correct result. So just checked the Date object. In my application input it comes as dayofWeek as 7 for start date and 1 for end date. But my simple program gives it as 1 for start date and 2 for end date. Any idea how this is happening?

Upvotes: 2

Views: 414

Answers (4)

Basil Bourque
Basil Bourque

Reputation: 339342

tl;dr

For Java 6 and Java 7, use the ThreeTen-Backport project.

12 hours

Between 2 PM and 2 AM in Europe/Berlin time zone on those dates is twelve hours.

org.threeten.bp.temporal.ChronoUnit.HOURS.between( 

    org.threeten.bp.LocalDateTime
        .parse( "2017-10-28T14:00:00" )
        .atZone( org.threeten.bp.ZoneId.of( "Europe/Berlin" ) ) ,  // Producing a `ZonedDateTime` instance. 

    org.threeten.bp.LocalDateTime
        .parse( "2017-10-29T02:00:00" )
        .atZone( org.threeten.bp.ZoneId.of( "Europe/Berlin" ) )  // Producing another `ZonedDateTime` instance. 

)

12

13 hours

To see the effect of a Daylight Saving Time (DST) cutover scheduled for 3 AM in Berlin time, change your start-stop to 3 PM and 3 AM, to get 13 hours rather than 12 hours.

The hour of 02:00-03:00 repeats, as discussed in the Answer by Hugo.

org.threeten.bp.temporal.ChronoUnit.HOURS.between( 

    org.threeten.bp.LocalDateTime
        .parse( "2017-10-28T15:00:00" )
        .atZone( org.threeten.bp.ZoneId.of( "Europe/Berlin" ) ) ,  // Producing a `ZonedDateTime` instance. 

    org.threeten.bp.LocalDateTime
        .parse( "2017-10-29T03:00:00" )
        .atZone( org.threeten.bp.ZoneId.of( "Europe/Berlin" ) )  // Producing another `ZonedDateTime` instance. 

)

13

See this code run live at IdeOne.com, using the java.time classes built into Java 8.

java.time

This has been covered many many times already on Stack Overflow, so search for more info.

Briefly…

Avoid the Date class. Use only the java.time classes. Likewise, avoid the Joda-Time project for new work as the team advises migration to java.time classes.

Parse your input strings as LocalDateTime values because they lack an indicator of time zone or offset-from-UTC. A LocalDateTime does not represent an actual moment on the timeline, but we'll get there; read on.

Tip: write your date-time strings in standard ISO 8601 format, used by default in java.time classes when parsing/generating strings.

Define a time zone (ZoneId) intended for these values. Specify a zone by proper name in continent/region format such as Africa/Casablanca or Europe/Berlin. Never use 3-4 pseudo zone names such as PST or CST.

Apply the zone to produce a ZonedDateTime object for each moment.

Calculate elapsed time instantiating a Duration object, or by calling ChronoUnit.HOURS.between.

Much of java.time functionality is back-ported to Java 6 & Java 7 in the ThreeTen-Backport project. The java.time classes are built into Java 8 and Java 9.


Table of all date-time types in Java, both modern and legacy


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

Where to obtain the java.time classes?

Upvotes: 3

user7605325
user7605325

Reputation:

DST cut-over at 03:00

You said in the comments your JVM default timezone is Europe/Berlin. In October 28th 2017, Berlin is in Daylight Saving Time (DST), so the offset is +02:00 (two hours ahead of UTC). When the local time gets to October 29th 2017 at 3 AM, the clocks shift back 1 hour, to 2 AM (and offset changes from +02:00 to +01:00).

This means that, in this timezone, all local times between 2 AM and 2:59 AM exist twice (in offsets +02:00 and +01:00). Just to better illustrate it, that's how the timeline would be in this timezone, hour by hour:

  • 2017-10-29T01:00+02:00 (1 AM in DST - offset +02:00)
  • 2017-10-29T02:00+02:00 (1 hour later, 2 AM in DST - offset +02:00)
  • 2017-10-29T02:00+01:00 (1 hour later, 2 AM not in DST - offset +01:00)
  • 2017-10-29T03:00+01:00 (1 hour later, 3 AM not in DST - offset +01:00)

Your Date object with its getTime() value equal to 1509238800000 corresponds to the second 2 AM (the not-in-DST date), then the difference will be 13 hours.


This can become more clear if we convert the timestamps to UTC (using the values provided in the comments):

Timestamp (getTime()) | Europe/Berlin timezone              | UTC
----------------------|-------------------------------------|------------------
1509192000000         | 2017-10-28T14:00+02:00 (in DST)     | 2017-10-28T12:00Z
1509238800000         | 2017-10-29T02:00+01:00 (not in DST) | 2017-10-29T01:00Z

Note that, when converting to UTC, it becomes clear that the difference is really 13 hours.

Upvotes: 2

jpganz18
jpganz18

Reputation: 5858

First of all I would suggest to use a library that handle different time zones and changes on times (like summer +1 hours and so on). For this I suggest JodaTime.

 Hours hoursDiff =  Hours.hoursBetween(start.toLocalDate(), end.toLocalDate()).getDays())

and then you can do

hoursDiff.getHours(); //returns and int

You can convert date to datetime (required in the params) like this:

Date date = new Date();
DateTime dateTime = new DateTime(date);

<!-- https://mvnrepository.com/artifact/joda-time/joda-time -->
<dependency>
    <groupId>joda-time</groupId>
    <artifactId>joda-time</artifactId>
    <version>2.9.7</version>
</dependency>

Upvotes: 1

ptomli
ptomli

Reputation: 11818

First off, read this, cry a little, and then be glad you're not required to write date/time libraries, because others have done it for you

Then add Joda-Time to your project.

If you know the timezone of all the dates in your database then you should be able to use Duration, Period or Interval, depending on your needs.

Upvotes: 1

Related Questions