361

How do I find the difference in Days between two Joda-Time DateTime instances? With ‘difference in days’ I mean if start is on Monday and end is on Tuesday I expect a return value of 1 regardless of the hour/minute/seconds of the start and end dates.

Days.daysBetween(start, end).getDays() gives me 0 if start is in the evening and end in the morning.

I'm also having the same issue with other date fields so I was hoping there would be a generic way to 'ignore' the fields of lesser significance.

In other words, the months between Feb and 4 March would also be 1, as would the hours between 14:45 and 15:12 be. However the hour difference between 14:01 and 14:55 would be 0.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
pvgoddijn
  • 12,638
  • 15
  • 47
  • 56

9 Answers9

425

Annoyingly, the withTimeAtStartOfDay answer is wrong, but only occasionally. You want:

Days.daysBetween(start.toLocalDate(), end.toLocalDate()).getDays()

It turns out that "midnight/start of day" sometimes means 1am (daylight savings happen this way in some places), which Days.daysBetween doesn't handle properly.

// 5am on the 20th to 1pm on the 21st, October 2013, Brazil
DateTimeZone BRAZIL = DateTimeZone.forID("America/Sao_Paulo");
DateTime start = new DateTime(2013, 10, 20, 5, 0, 0, BRAZIL);
DateTime end = new DateTime(2013, 10, 21, 13, 0, 0, BRAZIL);
System.out.println(daysBetween(start.withTimeAtStartOfDay(),
                               end.withTimeAtStartOfDay()).getDays());
// prints 0
System.out.println(daysBetween(start.toLocalDate(),
                               end.toLocalDate()).getDays());
// prints 1

Going via a LocalDate sidesteps the whole issue.

Alice Purcell
  • 12,622
  • 6
  • 51
  • 57
  • Can you provide an example case with specific data where you think [`Days.daysBetween`](http://www.joda.org/joda-time/apidocs/org/joda/time/Days.html#daysBetween-org.joda.time.ReadableInstant-org.joda.time.ReadableInstant-) is incorrect? – Basil Bourque Dec 25 '14 at 09:33
  • When you say to use `Instant`, you're not just talking about `start.toInstant()`, are you? – Patrick M Feb 10 '15 at 21:44
  • @PatrickM Yes, I was. On reflection, it's not clear exactly what constraints this is intended to impose, so I'll remove that last sentence. Thanks! – Alice Purcell Feb 11 '15 at 23:28
  • @chrispy daysBetween doc says it returns number of **WHOLE** days. In my case 2 days and 1 hour should return me 3 days. In your example it returns 2 days. Is there a way to achieve this? – Boss Man Jun 18 '16 at 20:51
  • @SujitJoshi I'm afraid I don't understand your question. I suggest posting a full Stack Overflow question and pasting a link here for me :) – Alice Purcell Jun 19 '16 at 21:55
  • Beware of dates overlapping with a new year! Days between 31 dec 2016 and 5 jan 2017 gives -361 days. – Sylphe Jan 04 '17 at 17:37
  • @Sylphe No, it doesn't, I just checked. I suspect you typo'd 2017. – Alice Purcell Jan 05 '17 at 12:47
195

Days Class

Using the Days class with the withTimeAtStartOfDay method should work:

Days.daysBetween(start.withTimeAtStartOfDay() , end.withTimeAtStartOfDay() ).getDays() 
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
  • 1
    thanks! I was trying to achieve the behavior of android.text.format.DateUtils.getRelativeTimeSpanString() with joda and this was really useful. – gop Jun 27 '13 at 10:28
  • 1
    Sir, with reference to this [post](http://stackoverflow.com/q/17665921/1031945) the method `toDateMidnight()` from the type DateTime is deprecated. – Aniket Kulkarni Oct 08 '13 at 05:33
  • 2
    You should now use .withTimeAtStartOfDay() instead of .toDateMidnight() – bgolson Oct 29 '13 at 16:54
  • 2
    what if the `end` is before the `start`, does it returns negative days? – akhy Feb 04 '15 at 12:50
  • 7
    @akhyar: why don't you try it? – Michael Borgwardt Feb 04 '15 at 12:53
  • How do I change the internal behavior of this method to be inclusive of all the dates? For instance, if the dates are the same, the number of days should be 1. Ignoring the time fields – TheRealChx101 Jul 11 '20 at 08:14
91

you can use LocalDate:

Days.daysBetween(new LocalDate(start), new LocalDate(end)).getDays() 
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
12

tl;dr

java.time.temporal.ChronoUnit.DAYS.between( 
    earlier.toLocalDate(), 
    later.toLocalDate() 
)

…or…

java.time.temporal.ChronoUnit.HOURS.between( 
    earlier.truncatedTo( ChronoUnit.HOURS )  , 
    later.truncatedTo( ChronoUnit.HOURS ) 
)

java.time

FYI, the Joda-Time project is now in maintenance mode, with the team advising migration to the java.time classes.

The equivalent of Joda-Time DateTime is ZonedDateTime.

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

Apparently you want to count the days by dates, meaning you want to ignore the time of day. For example, starting a minute before midnight and ending a minute after midnight should result in a single day. For this behavior, extract a LocalDate from your ZonedDateTime. The LocalDate class represents a date-only value without time-of-day and without time zone.

LocalDate localDateStart = zdtStart.toLocalDate() ;
LocalDate localDateStop = zdtStop.toLocalDate() ;

Use the ChronoUnit enum to calculate elapsed days or other units.

long days = ChronoUnit.DAYS.between( localDateStart , localDateStop ) ;

Truncate

As for you asking about a more general way to do this counting where you are interested the delta of hours as hour-of-the-clock rather than complete hours as spans-of-time of sixty minutes, use the truncatedTo method.

Here is your example of 14:45 to 15:12 on same day.

ZoneId z = ZoneId.of( "America/Montreal" ); 
ZonedDateTime start = ZonedDateTime.of( 2017 , 1 , 17 , 14 , 45 , 0 , 0 , z );
ZonedDateTime stop = ZonedDateTime.of( 2017 , 1 , 17 , 15 , 12 , 0 , 0 , z );

long hours = ChronoUnit.HOURS.between( start.truncatedTo( ChronoUnit.HOURS ) , stop.truncatedTo( ChronoUnit.HOURS ) );

1

This does not work for days. Use toLocalDate() in this case.


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.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

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.

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.

Alice Purcell
  • 12,622
  • 6
  • 51
  • 57
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • Your TL;DR for days using truncate falls prey to the bug detailed in the accepted answer, namely it is off-by-one when start-of-day is at 1am. I have updated it to use the correct answer you gave further down, using toLocalDate. – Alice Purcell Sep 27 '19 at 10:28
2

The accepted answer builds two LocalDate objects, which are quite expensive if you are reading lot of data. I use this:

  public static int getDaysBetween(DateTime earlier, DateTime later)
  {
    return (int) TimeUnit.MILLISECONDS.toDays(later.getMillis()- earlier.getMillis());
  }

By calling getMillis() you use already existing variables.
MILLISECONDS.toDays() then, uses a simple arithmetic calculation, does not create any object.

JBoy
  • 5,398
  • 13
  • 61
  • 101
  • 1
    This Answer works only if your definition of ‘days’ is exactly 24-hours long without regard for time zones and dates. If so, use this approach. If not, look to the other answers that address time zones. – Basil Bourque Jun 21 '16 at 17:16
  • @BasilBourque, you are right, still, i would go for a solution based on arithmetic calculation rather then building expensive objects for each method call, there are many flavours out there for such calculations, if you are reading an input from a web page, might be ok, but if you are processing a log file with hundreds of thousands of lines this just makes it very slow – JBoy Jun 21 '16 at 21:37
  • 1
    Java will optimize away the object allocation if your loop is hot. See http://docs.oracle.com/javase/7/docs/technotes/guides/vm/performance-enhancements-7.html#escapeAnalysis – Alice Purcell Jun 24 '16 at 08:20
1

java.time.Period

Use the java.time.Period class to count days.

Since Java 8 calculating the difference is more intuitive using LocalDate, LocalDateTime to represent the two dates

    LocalDate now = LocalDate.now();
    LocalDate inputDate = LocalDate.of(2018, 11, 28);

    Period period = Period.between( inputDate, now);
    int diff = period.getDays();
    System.out.println("diff = " + diff);
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
AzizSM
  • 6,199
  • 4
  • 42
  • 53
0

(KOTLIN) For Difference between a constant date and current date (Joda)

You can use Days.daysBetween(jodaDate1,jodaDate2)

Here is an example:

        val dateTime: DateTime = DateTime.parse("14/09/2020",
            DateTimeFormat.forPattern("dd/MM/yyyy"))
        val currentDate = DateTime.now()
        //To calculate the days in between
        val dayCount = Days.daysBetween(dateTime,currentDate).days
        //Set Value to TextView 
        binding.daysCount.text = dayCount.toString()
-1
DateTime  dt  = new DateTime(laterDate);        

DateTime newDate = dt.minus( new  DateTime ( previousDate ).getMillis());

System.out.println("No of days : " + newDate.getDayOfYear() - 1 );    
-12
public static int getDifferenceIndays(long timestamp1, long timestamp2) {
    final int SECONDS = 60;
    final int MINUTES = 60;
    final int HOURS = 24;
    final int MILLIES = 1000;
    long temp;
    if (timestamp1 < timestamp2) {
        temp = timestamp1;
        timestamp1 = timestamp2;
        timestamp2 = temp;
    }
    Calendar startDate = Calendar.getInstance(TimeZone.getDefault());
    Calendar endDate = Calendar.getInstance(TimeZone.getDefault());
    endDate.setTimeInMillis(timestamp1);
    startDate.setTimeInMillis(timestamp2);
    if ((timestamp1 - timestamp2) < 1 * HOURS * MINUTES * SECONDS * MILLIES) {
        int day1 = endDate.get(Calendar.DAY_OF_MONTH);
        int day2 = startDate.get(Calendar.DAY_OF_MONTH);
        if (day1 == day2) {
            return 0;
        } else {
            return 1;
        }
    }
    int diffDays = 0;
    startDate.add(Calendar.DAY_OF_MONTH, diffDays);
    while (startDate.before(endDate)) {
        startDate.add(Calendar.DAY_OF_MONTH, 1);
        diffDays++;
    }
    return diffDays;
}
user1091978
  • 145
  • 7