0

I have the following method that I took from the accepted answer this question Calculate number of weekdays between two dates in Java

public static int getWorkingDaysBetweenTwoDates(Date startDate, Date endDate) {
    Calendar startCal = Calendar.getInstance();
    startCal.setTime(startDate);

    Calendar endCal = Calendar.getInstance();
    endCal.setTime(endDate);

    int workDays = 0;

    //Return 0 if start and end are the same
    if (startCal.getTimeInMillis() == endCal.getTimeInMillis()) {
        return 0;
    }

    if (startCal.getTimeInMillis() > endCal.getTimeInMillis()) {
        startCal.setTime(endDate);
        endCal.setTime(startDate);
    }

    do {
        //excluding start date
        startCal.add(Calendar.DAY_OF_MONTH, 1);
        if (startCal.get(Calendar.DAY_OF_WEEK) != Calendar.SATURDAY && startCal.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY) {
            ++workDays;
        }
    } while (startCal.getTimeInMillis() < endCal.getTimeInMillis()); //excluding end date

    return workDays;
}

I pass to that function the first day and the last day of the current month I get the days like this:

Calendar firstDayOfMonth = Calendar.getInstance();
    firstDayOfMonth .set(Calendar.DAY_OF_MONTH,
            Calendar.getInstance().getActualMinimum(Calendar.DAY_OF_MONTH));

    Calendar lastDayOfMonth = Calendar.getInstance();
    lastDayOfMonth .set(Calendar.DAY_OF_MONTH,
            Calendar.getInstance().getActualMaximum(Calendar.DAY_OF_MONTH));

and I pass the parameters to the function like this:

getWorkingDaysBetweenTwoDates(firstDayOfMonth.getTime(),
            lastDayOfMonth.getTime());

I try the method and is returning 21 and we are in November of 2016 and this month have 22 working days not 21

I printed in console the parameters and these are the paramaters that I'm passing to the method

firstDayOfMonth.getTime() //equals to this  Tue Nov 01 09:09:47 VET 2016
lastDayOfMonth.getTime() //equals to this   Wed Nov 30 09:09:47 VET 2016
Community
  • 1
  • 1
Bill_Data23
  • 659
  • 2
  • 14
  • 30
  • 1
    Well, you're excluding the first date explicitly. It's even commented. So that's not surprising. – JB Nizet Nov 01 '16 at 13:59
  • I believe in your logic you are excluding the 1st Nov which is a week day, hence your result shows 21 instead of 22 – mhasan Nov 01 '16 at 13:59
  • sry I just copy and pasted the method from that question now I'm reading it more carefully, but I don't understand the logic, if I call that function during the first date and last date they will not add up?? – Bill_Data23 Nov 01 '16 at 14:05
  • 1
    What is the expected result if `startDate` is November 6 and `endDate` is November 7? Try debugging to see why the `while` loop ends. – Andrew S Nov 01 '16 at 14:07

2 Answers2

0

Replace startCal.getTimeInMillis() < endCal.getTimeInMillis()) with startCal.getTimeInMillis() <= endCal.getTimeInMillis()).

You current code act like end date not included in range.

talex
  • 17,973
  • 3
  • 29
  • 66
  • thanks friend it was that it works now , but I don't know if I should delete the question since it was down voted, I don't know if accepting answers in a down voted question is against the rules. – Bill_Data23 Nov 01 '16 at 14:41
  • You can either fix your question (I don't know what is wrong with it) or delete it. You decide. – talex Nov 01 '16 at 14:50
0

Indeed, to have the correct number :

do {
    // excluding start date
    startCal.add(Calendar.DAY_OF_MONTH, 1);
    if (startCal.get(Calendar.DAY_OF_WEEK) != Calendar.SATURDAY && startCal.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY) {
    ++workDays;
    }
}

should be replaced by :

do {
        // excluding start date
        if (startCal.get(Calendar.DAY_OF_WEEK) != Calendar.SATURDAY && startCal.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY) {
        ++workDays;
        }
        startCal.add(Calendar.DAY_OF_MONTH, 1);
    }

But I see another problem.
To give a correct result, the function supposes that there is a little time difference between the two date parameters :

For example, if you provide two dates (01-11-2016 and 30-11-2016) with last part of the datetime to 00:00:00:00, the days number returned will be 21. If you create one date, after the second date, you will get 22.

The problem happens here :

while (startCal.getTimeInMillis() < endCal.getTimeInMillis());

Because even if it has only some milliseconds between startCal and endCal in the last iteration, it adds one undesirable day in the result.

To have a deterministic result, you should consider only day (and not time) in the transmitted dates :

Calendar firstDayOfMonth = Calendar.getInstance();
firstDayOfMonth.set(Calendar.MILLISECOND, 0);
firstDayOfMonth.set(Calendar.SECOND, 0);
firstDayOfMonth.set(Calendar.MINUTE, 0);
firstDayOfMonth.set(Calendar.HOUR, 0);
firstDayOfMonth.set(Calendar.DAY_OF_MONTH,
    Calendar.getInstance().getActualMinimum(Calendar.DAY_OF_MONTH));

Calendar lastDayOfMonth = Calendar.getInstance();
lastDayOfMonth.set(Calendar.MILLISECOND, 0);
lastDayOfMonth.set(Calendar.SECOND, 0);
lastDayOfMonth.set(Calendar.MINUTE, 0);
lastDayOfMonth.set(Calendar.HOUR, 0);
lastDayOfMonth.set(Calendar.DAY_OF_MONTH,
    Calendar.getInstance().getActualMaximum(Calendar.DAY_OF_MONTH));

int nbDays = getWorkingDaysBetweenTwoDates(firstDayOfMonth.getTime(),
    lastDayOfMonth.getTime());

and use this condition in the loop :

 while (startCal.getTimeInMillis() <= endCal.getTimeInMillis());

This condition seems more natural since if the actual date is not after the last date (so before or equals), it should increment the counter for one additional day. Why exclude the last day ?

With Java 8 or JodaTime, it would be more simple and clean to set date values.
To avoid this kind of problem, I think that the getWorkingDaysBetweenTwoDates() method should reset to zero the time part of date parameters or use more specificDate objects (LocalDate for example) as parameters.

davidxxx
  • 125,838
  • 23
  • 214
  • 215