Given a range of dates, how do you calculate the number of days off partially or completely within that range?

Given a range of dates, how do you calculate the number of days off partially or completely within that range?

(Multiple queries as requested: take "weekend" to mean Saturday and Sunday. Date range is included, meaning the end date is part of the "wholly or partially" range means that any portion of the weekend falling within the date range means the entire weekend calculated.)

To keep it simple, I am assuming that you actually need to know the duration and what day of the week is the starting day ...

I'm pretty darn good, it will now involve doing integer division by 7 and some logic to add 1 depending on the remainder, but I can't quite figure out what ...

additional points for answers in Python ;-)

Edit

Here's my final code.

Weekends are Friday and Saturday (as we count the nights remaining) and days 0 through 0 are indexed. I used the onebyone algorithm and Tom's code layout. Many thanks to people.

def calc_weekends(start_day, duration):
    days_until_weekend = [5, 4, 3, 2, 1, 1, 6]
    adjusted_duration = duration - days_until_weekend[start_day]
    if adjusted_duration < 0:
        weekends = 0
    else:
        weekends = (adjusted_duration/7)+1
    if start_day == 5 and duration % 7 == 0: #Saturday to Saturday is an exception
        weekends += 1
    return weekends

if __name__ == "__main__":
    days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
    for start_day in range(0,7):
        for duration in range(1,16):
            print "%s to %s (%s days): %s weekends" % (days[start_day], days[(start_day+duration) % 7], duration, calc_weekends(start_day, duration))
        print

      

+1


a source to share


4 answers


A general approach for this kind of thing:

For each day of the week, find out how many days it takes before the start of the "contains holidays" period. For example, if "contains weekends" means "contains both Saturday and Sunday", then we have the following table:

Sunday: 8 Monday: 7 Tuesday: 6 Wednesday: 5 Thursday: 4 Friday: 3 Saturday: 2

For "part or all" we have:

Sunday: 1 Monday: 6 Tuesday: 5 Wednesday: 4 Thursday: 3 Friday: 2 Saturday: 1

Obviously this doesn't need to be coded as a table, now that it's obvious what it looks like.



Then, given the day of the week at the start of your period, subtract the [*] magic value from the length of the period in days (perhaps start-end + 1 to include both anchors). If the result is less than 0, it contains 0 days off. If it is equal to or greater than 0, it contains (at least) 1 weekend.

Then you have to deal with the remaining days. In the first case, it is easy, one extra weekend in 7 days. This is also true in the second case for every starting day except Sunday, which takes another 6 days to include another weekend. So in the second case, for periods starting on Sunday, you can count 1 weekend at the beginning of the period, then subtract 1 from the length and recalculate from Monday.

More generally, what happens here for "whole or partial" weekends, we check to see if we're starting halfway through an interesting bit ("weekend"). If so, we can either:

  • 1) Count one, move the start date to the end of the interesting bit, and count.
  • 2) Move the start date to the beginning of the interesting bit and recalculate.

In the case of weekends, there is only one special case that starts halfway through, so (1) looks good. But if you were getting the date as date + time in seconds rather than day, or if you were interested in 5-day work weeks rather than 2-day weekends, then (2) may be easier to understand.

[*] If you don't use unsigned types, of course.

+5


a source


My general approach to things like this is: don't start messing around with trying to override your own date logic - it's tricky i.e. you screw it to the edges and look bad. Tip: if you have mod 7 arithmetic anywhere in your program, or treat dates as integers anywhere in your program: you are failing . If I saw the "accepted solution" anywhere (or even close) to my codebase, someone would need to start over. It sparks the imagination that anyone who considers themselves a programmer will vote for this answer.

Instead, use the built-in date / time logic that comes with Python:

First, get a list of all the days that interest you:

from datetime import date, timedelta    
FRI = 5; SAT = 6

# a couple of random test dates
now = date.today()
start_date = now - timedelta(57)
end_date = now - timedelta(13)
print start_date, '...', end_date    # debug

days = [date.fromordinal(d) for d in  
            range( start_date.toordinal(),
                   end_date.toordinal()+1 )]

      

Then filter to only days that are holidays. In your case, you are interested in Friday and Saturday nights, which are 5 and 6. (Note that I am not trying to flip this part into the previous comprehension of the list, as this will be difficult to verify as correct).



weekend_days = [d for d in days if d.weekday() in (FRI,SAT)]

for day in weekend_days:      # debug
    print day, day.weekday()  # debug

      

Finally, you want to figure out how many weekends are on your list. This is the tricky part, but there are really only four cases, one for each end on Friday or Saturday. Concrete examples help make it clearer, and this is really what you want to document in your code:

num_weekends = len(weekend_days) // 2

# if we start on Friday and end on Saturday we're ok,
# otherwise add one weekend
#  
# F,S|F,S|F,S   ==3 and 3we, +0
# F,S|F,S|F     ==2 but 3we, +1
# S|F,S|F,S     ==2 but 3we, +1
# S|F,S|F       ==2 but 3we, +1

ends = (weekend_days[0].weekday(), weekend_days[-1].weekday())
if ends != (FRI, SAT):
    num_weekends += 1

print num_weekends    # your answer

      

Shorter, more comprehensible and straightforward means you can trust your code more and can tackle more interesting problems.

+2


a source


To count the entire weekend, simply adjust the number of days to start on Monday and then divide by seven. (Note that if the start day is a weekday, add days to go to the previous Monday, and if it is on the weekend, subtract days to go to the next Monday, since you already missed that weekend.)

days = {"Saturday":-2, "Sunday":-1, "Monday":0, "Tuesday":1, "Wednesday":2, "Thursday":3, "Friday":4}

def n_full_weekends(n_days, start_day):
    n_days += days[start_day]
    if n_days <= 0:
        n_weekends = 0
    else:
        n_weekends = n_days//7
    return n_weekends

if __name__ == "__main__":
    tests = [("Tuesday", 10, 1), ("Monday", 7, 1), ("Wednesday", 21, 3), ("Saturday", 1, 0), ("Friday", 1, 0),
    ("Friday", 3, 1), ("Wednesday", 3, 0), ("Sunday", 8, 1), ("Sunday", 21, 2)]
    for start_day, n_days, expected in tests:
        print start_day, n_days, expected, n_full_weekends(n_days, start_day)

      

If you want to know the fractional weekend (or week), just look at the fractional part of seven.

+1


a source


You will need external logic next to the original math. You need to have a calendar library (or if you have enough time to implement it yourself) to determine what is on the weekend, what day of the week you start, end, etc.

Have a look at the Python calendar class .

Without a logical definition of days in your code, pure math methods will fail for an angle, for example within 1 day, or anything lower than a full week, I suppose (or below 6 days if you allow partial).

0


a source







All Articles