2

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?

JAVA_CAT
  • 737
  • 3
  • 13
  • 33
  • 1
    How do you build your input dates ? – Stéphane Ammar Oct 16 '17 at 14:06
  • 1
    It already has been done for you. See the [Java Time Trail](https://docs.oracle.com/javase/tutorial/datetime/iso/period.html) – M. le Rutte Oct 16 '17 at 14:11
  • 1
    "During day light saving periods duration gives 13.0 which is wrong." No it's not. Not if there was a DST transition between that 2pm and the 2am - there could easily have been 13 elapsed hours (or 11). – Jon Skeet Oct 16 '17 at 14:13
  • 3
    For the love of all things good, do not try to calculate date differences like this. There is just so much insanity surrounding dates, times and timezones that the fabric of spacetime could not cope with another "there are always 24 hours in a day" assumption. See https://github.com/kdeldycke/awesome-falsehood#dates-and-time – ptomli Oct 16 '17 at 14:16
  • @M.leRutte I am using java 7. The tutorial which you shared is Java8 – JAVA_CAT Oct 16 '17 at 14:16
  • @StéphaneAmmar This is there in the screen already fetched from the database table. – JAVA_CAT Oct 16 '17 at 14:18
  • 2
    If you are on JSE7 then http://www.joda.org/joda-time/ – ptomli Oct 16 '17 at 14:18
  • Why would you use Java 7? Java 7 is from 2011, Java 8 from 2014. You should be on Java 9 by now. – M. le Rutte Oct 16 '17 at 14:18
  • @JonSkeet I didn't get your calculation. Could you please explain ? – JAVA_CAT Oct 16 '17 at 14:19
  • 1
    @M.leRutte Certain things clients decides not us :-) – JAVA_CAT Oct 16 '17 at 14:20
  • @ptomli I cannot use any 3rd party packages other than spring and hibernate. – JAVA_CAT Oct 16 '17 at 14:21
  • What's the values of `endDate.getTime()` and `startDate.getTime()`? And what's the JVM default timezone (`TimeZone.getDefault().getID()`)? –  Oct 16 '17 at 14:35
  • 2
    Suppose there's a "fall back" from 2017-10-29T01:30 to 2017-10-29T00:30 as a DST transition. That means that the hour between 00:30 and 01:30 happens twice. That means the *elapsed* time between 2017-10-28T14:00 and 2017-10-29T02:00 is 13 hours. If you convert each local time into UTC, you should see that clearly. The reverse happens if 00:30 to 01:30 is skipped due to a "spring forward". – Jon Skeet Oct 16 '17 at 14:40
  • FYI, you are using one of the troublesome old date-time classes that is now legacy, supplanted by the java.time classes. In particular, `Date` is replaced by `Instant`. – Basil Bourque Oct 16 '17 at 15:06
  • @Hugo Default Time zone is "Europe/Berlin" – JAVA_CAT Oct 16 '17 at 15:12
  • And the values of endDate.getTime() and startDate.getTime()? –  Oct 16 '17 at 15:12
  • @JAVA_CAT Check the locale in effect for your application and test. For example, en_US week starts Sunday, en_GB week starts Monday. This suggests inconsistent environments between application and test. See https://stackoverflow.com/a/28021267/134894 – ptomli Oct 16 '17 at 15:26
  • @Hugo end date = 1509238800000 , start date = 1509192000000 – JAVA_CAT Oct 16 '17 at 15:40
  • @ptomli Locale is en_US in application , what you meant by test? – JAVA_CAT Oct 16 '17 at 16:01
  • @JAVA_CAT So the difference is really 13 hours (I explain in details in my answer below) - and the day of week is irrelevant to this result, btw (it's a different issue, probably caused by the locale, as already said - maybe you could post a different question specifically to address this issue, adding the code you're using to get the day of week and so on - but remember to make a search before asking, I'm sure this was already covered before). –  Oct 16 '17 at 16:04
  • @Hugo Thanks. But to solve this I think I need to work on Locale Issue. I will add another post. Thanks. – JAVA_CAT Oct 16 '17 at 16:19
  • @JAVA_CAT The 13-hour difference is due to timezone (actually, it depends on what's in database and/or how you're retrieving the date from it - maybe this code could be relevant). The locale controls another aspects (like the beginning of the week and other stuff), but it has no relation to the timezones (thus, it's not related to the difference between the dates) –  Oct 16 '17 at 23:19

4 Answers4

3

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?

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • Somewhere in the comments on the question there's a note that it's JSE7, so no `java.time` – ptomli Oct 16 '17 at 15:20
  • 1
    @ptomli (A) It is the responsibility of that author to edit important facts into the Question rather than bury in comments. (B) Much of java.time is back-ported to Java 6 & 7 in the ThreeTen-Backport project. – Basil Bourque Oct 16 '17 at 15:23
2

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.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • @BasilBourque I double checked. In Europe/Berlin timezone, at October 29th 2017, the wall-clock [repeats the 02:00-03:00 hour](https://www.timeanddate.com/time/zone/germany/berlin?year=2017). Check this: https://ideone.com/ATOofe –  Oct 16 '17 at 23:16
  • 1
    I was wrong: 2 AM vs 3 AM cut-over. Deleted my comment. I assumed a 2 AM cut-over as used in US/Canada. Then I learned [`Europe/Berlin`](https://time.is/Berlin) uses a 3 AM cut-over. I'll leave this comment for any other too-quick-to-assume Americans/Canadians. – Basil Bourque Oct 16 '17 at 23:20
1

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.

ptomli
  • 11,730
  • 4
  • 40
  • 68
  • I cannot suggest a new package as this is not allowed. – JAVA_CAT Oct 16 '17 at 14:25
  • 1
    Although a great API (I used a lot and really love it), Joda-Time is now in maintainance mode and being replaced by the new APIs, so I don't recommend start a new project with it. Even in [joda's website](http://www.joda.org/joda-time) it says: **"Note that Joda-Time is considered to be a largely “finished” project. No major enhancements are planned. If using Java SE 8, please migrate to java.time (JSR-310)."**. In Java 7, there's the [ThreeTen Backport](http://www.threeten.org/threetenbp), a great backport for Java 8's new date/time classes. –  Oct 16 '17 at 14:31
  • @Hugo could you please tell me what is wrong with my code shown above? and how dayofweek affects duration result? – JAVA_CAT Oct 16 '17 at 14:40
  • 1
    @JAVA_CAT To me it's not clear how the day of week should affect the results. Maybe if you edit your question and add more code and provide more context to it... My guess is that the day of week is not relevant, though –  Oct 16 '17 at 14:42
  • @Hugo "finished" != "obsolete". I would argue that using a well tested library like Joda-Time would be preferential to a newer library like the 310 backport, if for no other reason than proven quality. The same page also says **Joda-Time is the de facto standard date and time library for Java prior to Java SE 8.** – ptomli Oct 16 '17 at 15:18
  • Well, the backport was made by the same authors of Java 8 API (btw, the same author of Joda-Time) who made [some improvements in the new API](http://blog.joda.org/2009/11/why-jsr-310-isn-joda-time_4941.html). I just think that using the backport make a future migration to Java 8 easier, as it'll require just a change in the imports and a few adjustments, while migrating from Joda would be more difficult as the API's are similar [but not exactly the same](http://blog.joda.org/2014/11/converting-from-joda-time-to-javatime.html). –  Oct 16 '17 at 15:23
  • And IMO, when the authors of both API's recommend migrating to the newer one, to me it's enough (but I can understand your point). But I'm not saying that you're wrong, Joda-Time is still a great API. Anyway, I didn't say it's obsolete (although I can see how you inferred that), nor did I say your answer is wrong. Sorry for the misunderstanding. –  Oct 16 '17 at 15:27
  • 1
    @Hugo NP. Date/Time/Timezones are complex, even in the meta ;-) – ptomli Oct 16 '17 at 15:28
1

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>
jpganz18
  • 5,508
  • 17
  • 66
  • 115