Mailing List Archive: 49091 messages

# Exercise: Calculate # work days between two dates

### [1/3] from: edoconnor::gmail::com at: 31-Jan-2007 16:22

I found Sununda's algorithm challenge to be very interesting. I'm often amazed by the different approaches to a given problem. In this spirit, I thought I'd offer up a basic exercise based on a post about Ruby I came across yesterday. See: *Calculate the number of working days between two dates* http://www.railsonwave.com/railsonwave/2007/1/30/calculate-the-number-of-working-days-between-two-dates Here's an approach I took, which embellishes with a refinement: calc-workdays: func [ "Return number of workdays between two dates, excluding holidays" date1 [date!] "Start date" date2 [date!] "End date" holidays [block!] "Block of dates to exclude (holidays, etc.)" /non "Return number of non-work days (weekend + holidays) between 2 dates" /local days day1 day2 diff param ][ days: copy [] set [day1 day2] sort reduce [date1 date2] diff: day2 - day1 param: pick [[> 5 union][< 6 exclude]] either found? non [1][2] loop diff [ day1: day1 + 1 if do param/1 day1/weekday param/2 [append days day1] ] return length? do param/3 days holidays ] Results:
>> calc-workdays now/date 3-feb-2007 [1-feb-2007]
== 1
>> print calc-workdays/non now/date 3-feb-2007 [1-feb-2007]
== 2
>> calc-workdays now/date 31-dec-2007 [25-dec-2007 1-Jan-2008]
== 237
>> print calc-workdays/non now/date 31-dec-2007 [25-dec-2007 1-Jan-2008]
== 98 Anyone care to share a different or more natural approach? One of the things I'd like to see in REBOL's future is a guide or "phrase-book" for common expressions found in problems big and small. Ed

### [2/3] from: SunandaDH::aol::com at: 1-Feb-2007 21:15

Ed:
> I found Sununda's algorithm challenge to be very interesting. I'm often > amazed by the different approaches to a given problem.
Thanks. I was amazed too at the range of responses. Here's my offering for your exercise. My tweaks are: 1. Avoid the sort as we have only two items (I haven't timed that doing it by hand is faster than letting REBOL set up a sort for just two items -- it may be worth someone running a benchmark on that one day, 2. Avoid the loop around *all* the days in the period. I reckoned if I could round the first date down to the nearest Monday while rounding the second date up to the next nearest Monday, I was half way there. The tricky part was tracking how many countable days I'd added in by the rounding -- hence the blocks of obscure negative numbers. This theoretically makes it faster -- especially for very wide date ranges. 3. That left me needing to run a loop against the extra holiday days, checking each if was in range and not a weekend. And I had to do that before Step 2 so it was on the original dates, not the rounded ones. This may counteract the theoretical speed improvements in Step 2, especially if there are many holiday dates for a narrow date range. calc-workdays: func [ date1 [date!] date2 [date!] holidays [block!] /non /local dates offset hol-count working-days ][ ;; Put dates in order ;; ------------------ dates: copy reduce [date1] either date1 < date2 [append dates date2] [insert dates date2] ;; count holidays in range ;; ----------------------- hol-count: 0 foreach hd holidays [ if all [ hd/weekday <= 5 ;; got to be a weekday to count hd >= dates/1 hd <= dates/2][ hol-count: hol-count + 1 ] ] ;; Round dates to Mondays ;; ---------------------- offset: 0 offset: offset + pick [0 -1 -2 -3 -4 -4 -4] dates/1/weekday dates/1: dates/1 + 1 - dates/1/weekday offset: offset + pick [-5 -4 -3 -2 -1 -1 -1] dates/2/weekday dates/2: dates/2 + 8 - dates/2/weekday ;; Calculate the working / non-working days ;; -------------------------------- working-days: (dates/2 - dates/1) / 7 * 5 + offset - hol-count if non [return (abs (date1 - date2)) - working-days] return working-days ] That was fun -- thanks! Sunanda.