3

I am facing a weird issue with Java (version 6, 7, 8). In this code, I am trying to calculate the date diff between 2 dates and this code gives me date difference between 03/12/2018 and 01/04/2018 as 66.958 days and not exactly 67 days which is little surprising. I have not seen this behavior ever.

To prove this theory, you can try any date before 03/11/2018 which is simple math. Please advise what do you guys think. I have tried ChronoUnit class which is bundled with Java and it calculated the days correctly but just wondering what is going on here and if you have any opinion.

static  void dateLogic1() throws ParseException{
       String matYear1 = "03/11/2018";
       String matYear2 = "03/12/2018";

       String stlDate = "01/04/2018";


       java.util.Date mtYear1 = new SimpleDateFormat("MM/dd/yyyy").parse(matYear1);
       java.util.Date mtYear2 = new SimpleDateFormat("MM/dd/yyyy").parse(matYear2);


       java.util.Date stYear = new SimpleDateFormat("MM/dd/yyyy").parse(stlDate);

       long time1 = mtYear1.getTime();
       long time2 = mtYear2.getTime();
       long time4 = stYear.getTime();


       double diff1 =  (double) (mtYear1.getTime()-stYear.getTime()) /( 24 * 60 * 60 * 1000);
       double diff2 =  (double) (mtYear2.getTime()-stYear.getTime()) /( 24 * 60 * 60 * 1000);

       System.out.println("Date Diff between "+ mtYear1 +" & "+ stYear +" is " + diff1 );
       System.out.println("Date Diff between "+ mtYear2  +" & "+ stYear +" is " + diff2 );

   }
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 3
    In a nutshell — I expect it is due to the switch from winter (standard) to summer (daylight saving) time in the USA. – Jonathan Leffler Jan 03 '18 at 21:28
  • 4
    Don't do math with times. Use proper libraries. – Andy Turner Jan 03 '18 at 21:29
  • 2
    It can be if the application can use a result that is just "in the ballpark." Very accurate historical date arithmetic needs to account for things like state legislative actions on local time zones (and when they happened ... Indiana is nightmare in this regard), when the Gregorian Calendar was adopted (and where), whether the local zone for each date recognizes daylight savings, and (possibly) even the occurence of leap seconds. TLDR: date arithmetic is tricky. Don't do it yourself. – scottb Jan 03 '18 at 21:41
  • Any particular reason why you are still using the long outdated classes `Date` and `SimpleDateFormat`? The latter in particular is notorious for being troublesome. [`java.time`, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/), is so much nicer to work with. And offers a `LocalDate` class, a date without time-of-day with which you could never have this kind of problem. – Ole V.V. Jan 04 '18 at 07:26

2 Answers2

8

It has to do with daylight savings time. At 2 AM on March 11, 2018, one hour is lost in many places. If you convert that hour into a fraction of a day (1/24), the result is the difference between 67 and 66.958.

Edit: Date.getTime() returns the number of milliseconds since January 1, 1970, 00:00:00 GMT represented by the Date object. Because you lose one hour of milleseconds, that's how the fractional hour gets lost.

Sam M
  • 4,136
  • 4
  • 29
  • 42
3

tl;dr

Duration.between( ZonedDateTime , ZonedDateTime )

Details

The Answer by Sam M and comment by scottb are both correct.

You are using troublesome old date-time classes that are now legacy, supplanted by the java.time classes.

LocalDate start = LocalDate.of( 2018 , Month.JANUARY , 4 ) ;
LocalDate stop = LocalDate.of( 2018 , Month.MARCH , 12 ) ;
long days = ChronoUnit.DAYS.between( start , stop ) ;

If you want moments rather than dates, you must specify time zone. For any given moment, the date varies around the globe by zone.

ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;

Let java.time determine first moment of the day as it is not always at 00:00.

We add one day to account for the entire length of the last day.

ZonedDateTime zdtStart = start.atZone( z ) ; 
ZonedDateTime zdtStop = stop.plusDays( 1 ).atZone( z ) ; 

Calculate elapsed time to nanosecond resolution with Duration object.

Duration d = Duration.between( zdtStart , zdtStop ) ;

Represent the span of time as a string in standard ISO 8601 format.

String output = d.toString() ;  // Generate string in standard ISO 8601 format. 

Lessons learned:

  • Do not ignore/forget time zone.
  • Avoid the legacy date-time classes like the Plague. Always use the industry-leading java.time classes instead. Never roll your own date-time handling.
  • Days are not always 24-hours long.
  • Days do not always start at 00:00.
  • Use Half-Open approach to define a span of time where the beginning is inclusive while the ending is exclusive.

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?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • Agree with you. Because of compatibility issue in the platform I was not able to move to Java 8,So I imported Jodatime library with Java version 7 & it worked perfectly fine. – Amit Sharma Jan 04 '18 at 13:43
  • @AmitSharma FYI, the Joda-Time project is now in maintenance mode, with its team advising migration to its official successor, the java.time classes. For Java 7 and Java 6, see the *ThreeTen-Backport* project linked in my Answer for a library to add to your project providing much of the java.time functionality. The back-port is built by the same folks who built java.time and built Joda-Time; all three projects are led by the same man, Stephen Colebourne. – Basil Bourque Jan 04 '18 at 17:03