Reputation: 859
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
Reputation: 339342
For Java 6 and Java 7, use the ThreeTen-Backport project.
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
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.
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.
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
Reputation:
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
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
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