py-business-calendar icon indicating copy to clipboard operation
py-business-calendar copied to clipboard

bug has been identified.

Open lukshap opened this issue 8 years ago • 1 comments

Hi! Could you please check why the code below calculates with error: import business-calendar cal = Calendar() date1=datetime.datetime(2017, 1, 20, 14, 43, 45) date2=datetime.datetime(2017, 1, 28, 14, 40, 45) print cal.workdaycount(date1, date2) >>> 0

lukshap avatar Jan 28 '17 10:01 lukshap

Hi @lukshap ,

you are right, I've been able to reproduce this error and it affects the busdaycount method too:

import datetime
import business_calendar as bc
cal = bc.Calendar()
date1 = datetime.datetime(2017, 1, 20, 14, 43, 45)
date2 = datetime.datetime(2017, 1, 28, 14, 40, 45)
print(cal.busdaycount(date1, date2))
>>> 0

The issue results from a combination of the facts that

  • date2 is a non-workday (respectively businessday)
  • date2 is "ealier" in the day, meaning: date1.hour + 60 * date1.minute + 3600 * date1.second > date2.hour + 60 * date2.minute + 3600 * date2.second
  • The following day of date1 is a non-workday (respectively businessday)

If the following day of date1 would however be a workday (respectively businessday) we would still get an incorrect value as the result would be one less than the correct answer. With the current setup the code subtracts exactly 24 hours from date2 so both days are on a Friday. As we then go mod 7 we get a remainder of 6 for which we then count how often we need to add a workday until both days are the same weekday. However, as both already are fridays this leads to the issue you described.

Anyway, as date1 is observed COB we could simply add date1 = date1.replace(hour=0, minute=0, second=0) in the beginning of the _workdaycount method and would then get:

    def _workdaycount(self, date1, date2):`
        """
        (PRIVATE) Count work days between two dates, ignoring holidays.
        """
        assert date2 >= date1
        date1 = date1.replace(hour=0, minute=0, second=0)
        date1wd = date1.weekday()
        date2wd = date2.weekday()
        if not self.weekdaymap[date2wd].isworkday:
            date2 += datetime.timedelta(days=self.weekdaymap[date2wd].offsetprev)
            date2wd = self.weekdaymap[date2wd].prevworkday
        if date2 <= date1:
            return 0
        
        nw, nd = divmod((date2 - date1).days, 7)
        ndays = nw * len(self.workdays)
        if nd > 0:
            date1wd = date1.weekday()
            date2wd = date2.weekday()
            while date1wd != date2wd:
                ndays += 1
                date1wd = self.weekdaymap[date1wd].nextworkday
        return ndays`

This should fix the issue for the _workdaycount method but I haven't yet come to test this on the busdaycount method too.

Kind regards

sevendaybeard avatar Feb 13 '17 17:02 sevendaybeard