Reputation: 6033
Date d = new Date(today.getTimeInMillis());
Date d1 = new Date(dueDate.getTimeInMillis());
int daysUntil = (int) ((d1.getTime() - d.getTime())/ (1000 * 60 * 60 * 24));
Using the above code, where today
is a calendar set to 00:00 on the current day, and dueDate
is set to 00:00 on the date I am comparing today to, my results from this differ.
There is something in this which varies, making my output either x or x+1 where x is the correct answer.
What is the issue here, and what can I do to make it more stable?
Upvotes: 0
Views: 311
Reputation: 44071
There are mainly two reasons why your code is broken:
I demonstrate and explain the second reason.
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date d1 = sdf.parse("2016-03-20");
Date d2 = sdf.parse("2016-03-28");
int daysUntil = (int) ((d2.getTime() - d1.getTime()) / (1000 * 60 * 60 * 24));
System.out.println(daysUntil); // 7 (should be 8)
The code was run in timezone "Europe/Berlin". Due to the change from winter time to summer time causing a jump of clocks by one hour forward on 2016-03-27 at 2 am, there is one hour missing. One day has only 23 hours so the division by 24 yields zero resulting in counting one day less.
What can you do else?
Your workaround adding 1000 milliseconds to dueDate
sounds as if you have overlooked possible millisecond deltas in your input. This might solve a special case but will usually not be sufficient to solve the daylight saving problem, too. Whatever you choose on base of java.util.Date
it is a more or less an evil hack.
The best I have in mind (within the scope of Android-built-in stuff) is to construct an instance of java.util.GregorianCalendar
and to add successively one day after one until you have passed the due-date, and then count how many days you have added. Not elegant and errorprone because varying millisecond parts can easily be overlooked here, too.
Otherwise you can try various external libraries for this task. There are four available on Android which can calculate elapsed days in an easy way.
Upvotes: 1
Reputation: 340118
You do not provide actual values, so we cannot determine precisely the problem. We do not know what the today
and dueDate
variables are.
The question is now outmoded, as the troublesome old date-time classes including java.util.Date/.Calendar have been supplanted by the new java.time framework. See Tutorial. Defined by JSR 310, inspired by Joda-Time, and extended by the ThreeTen-Extra project.
In java.time:
Instant
is a moment on the timeline in UTC.ZoneId
represents a time zone. Use proper time zone names, never the 3-4 letter codes like "EST" or "IST" as they are neither standardized nor unique.ZonedDateTime
= Instant + ZoneId.Unfortunately, java.time does not include a facility for calculating days elapsed between date-time values. We can use the ThreeTen-Extra project and its Days
class with between
method to provide that calculation. The ThreeTen-Extra project is a collection of features deemed non-essential for java.time during the JSR process.
ZoneId zoneId = ZoneId.of ( "America/Montreal" );
ZonedDateTime now = ZonedDateTime.now ( zoneId );
ZonedDateTime then = now.minusDays ( 4 );
ZonedDateTime due = now.plusDays ( 3 );
Integer days = org.threeten.extra.Days.between ( then , due ).getAmount ();
Dump to console.
System.out.println ( "From then: " + then + " to due: " + due + " = days: " + days );
From then: 2015-10-31T16:01:13.082-04:00[America/Montreal] to due: 2015-11-07T16:01:13.082-05:00[America/Montreal] = days: 7
For Android or older versions of Java, use the excellent Joda-Time library.
The Days
class is smart and handles anomalies such as Daylight Saving Time (DST).
Note that unlike java.util.Date, a Joda-Time DateTime
object knows its own time zone.
// Specify a time zone rather than rely on default.
DateTimeZone timeZone = DateTimeZone.forID( "America/Regina" ); // Or "Europe/London".
DateTime now = new DateTime( timeZone );
DateTime startOfToday = now.withTimeAtStartOfDay();
DateTime fewDaysFromNow = now.plusDays( 3 );
DateTime startOfAnotherDay = fewDaysFromNow.withTimeAtStartOfDay();
Days days = Days.daysBetween( startOfToday, startOfAnotherDay );
Dump to console…
System.out.println( days.getDays() + " days between " + startOfToday + " and " + startOfAnotherDay + "." );
When run…
3 days between 2014-01-21T00:00:00.000-06:00 and 2014-01-24T00:00:00.000-06:00.
Upvotes: 1