简体   繁体   English

给定日期范围,如何计算部分或全部在该范围内的周末数量?

[英]Given a date range how to calculate the number of weekends partially or wholly within that range?

Given a date range how to calculate the number of weekends partially or wholly within that range? 给定日期范围,如何计算部分或全部在该范围内的周末数量?

(A few definitions as requested: take 'weekend' to mean Saturday and Sunday. The date range is inclusive ie the end date is part of the range 'wholly or partially' means that any part of the weekend falling within the date range means the whole weekend is counted.) (要求提供一些定义:将“周末”表示为星期六和星期日。日期范围包括在内,即结束日期是“全部或部分”范围的一部分,表示周末的任何部分都落在该日期范围内,表示整个周末都算在内。)

To simplify I imagine you only actually need to know the duration and what day of the week the initial day is... 为简化起见,我想您实际上只需要知道持续时间以及第一天是星期几...

I darn well now it's going to involve doing integer division by 7 and some logic to add 1 depending on the remainder but I can't quite work out what... 我现在好多了,它涉及将整数除以7,并根据剩余的逻辑进行一些加1的运算,但我无法完全解决...

extra points for answers in Python ;-) Python答案的加分;-)

Edit 编辑

Here's my final code. 这是我的最终代码。

Weekends are Friday and Saturday (as we are counting nights stayed) and days are 0-indexed starting from Monday. 周末是星期五和星期六(因为我们在计算住宿天数),而天数从星期一开始是0索引。 I used onebyone's algorithm and Tom's code layout. 我使用了bybyone的算法和Tom的代码布局。 Thanks a lot folks. 非常感谢大家。

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

General approach for this kind of thing: 这类事情的一般方法:

For each day of the week, figure out how many days are required before a period starting on that day "contains a weekend". 对于一周中的每一天,计算出从该天开始的一段“包含一个周末”之前需要多少天。 For instance, if "contains a weekend" means "contains both the Saturday and the Sunday", then we have the following table: 例如,如果“包含一个周末”的意思是“包含星期六和星期日”,那么我们有下表:

Sunday: 8 Monday: 7 Tuesday: 6 Wednesday: 5 Thursday: 4 Friday: 3 Saturday: 2 星期日:8星期一:7星期二:6星期三:5星期四:4星期五:3星期六:2

For "partially or wholly", we have: 对于“部分或全部”,我们有:

Sunday: 1 Monday: 6 Tuesday: 5 Wednesday: 4 Thursday: 3 Friday: 2 Saturday: 1 星期日:1星期一:6星期二:5星期三:4星期四:3星期五:2星期六:1

Obviously this doesn't have to be coded as a table, now that it's obvious what it looks like. 显然,不必将其编码为表格,因为它看起来像什么。

Then, given the day-of-week of the start of your period, subtract[*] the magic value from the length of the period in days (probably start-end+1, to include both fenceposts). 然后,给定周期开始的星期几,从周期的长度(以天为单位)中减去[*]魔术值(可能是start-end + 1,包括两个栅栏)。 If the result is less than 0, it contains 0 weekends. 如果结果小于0,则包含0个周末。 If it is equal to or greater than 0, then it contains (at least) 1 weekend. 如果等于或大于0,则它包含(至少)1个周末。

Then you have to deal with the remaining days. 然后,您必须处理剩余的日子。 In the first case this is easy, one extra weekend per full 7 days. 在第一种情况下,这很容易,每7天额外增加一个周末。 This is also true in the second case for every starting day except Sunday, which only requires 6 more days to include another weekend. 在第二种情况下,对于每个开始日(星期日除外)也是如此,星期日仅需要6天才能包含另一个周末。 So in the second case for periods starting on Sunday you could count 1 weekend at the start of the period, then subtract 1 from the length and recalculate from Monday. 因此,在第二种情况下,对于从星期日开始的时间段,您可以在时间段开始时算一个周末,然后从长度中减去1,然后从星期一重新计算。

More generally, what's happening here for "whole or part" weekends is that we're checking to see whether we start midway through the interesting bit (the "weekend"). 更一般而言,“整个或部分”周末在这里发生的事情是,我们正在检查是否从有趣的部分(“周末”)开始。 If so, we can either: 如果是这样,我们可以:

  • 1) Count one, move the start date to the end of the interesting bit, and recalculate. 1)数一,将开始日期移动到感兴趣的位的末尾,然后重新计算。
  • 2) Move the start date back to the beginning of the interesting bit, and recalculate. 2)将开始日期移回感兴趣的位的开头,然后重新计算。

In the case of weekends, there's only one special case which starts midway, so (1) looks good. 在周末的情况下,只有一种特殊情况从中途开始,因此(1)看起来不错。 But if you were getting the date as a date+time in seconds rather than day, or if you were interested in 5-day working weeks rather than 2-day weekends, then (2) might be simpler to understand. 但是,如果您以秒为单位而不是日期来获取日期和日期,或者如果您对工作日为5天而不是周末为2天感兴趣,那么(2)可能更容易理解。

[*] Unless you're using unsigned types, of course. [*]当然,除非您使用无符号类型。

My general approach for this sort of thing: don't start messing around trying to reimplement your own date logic - it's hard, ie. 对于这种事情,我的一般做法是:不要开始弄乱尝试重新实现自己的日期逻辑-这很困难。 you'll screw it up for the edge cases and look bad. 您会把它固定在边盒上,看起来很糟。 Hint: if you have mod 7 arithmetic anywhere in your program, or are treating dates as integers anywhere in your program: you fail . 提示:如果您在程序中的任何地方都使用了mod 7算术,或者在程序中的任何地方都将日期视为整数: 则会失败 If I saw the "accepted solution" anywhere in (or even near) my codebase, someone would need to start over. 如果我在代码库中的任何地方(甚至附近)看到“可接受的解决方案”,那么有人将需要重新开始。 It beggars the imagination that anyone who considers themselves a programmer would vote that answer up. 让人难以想象的是,任何认为自己是程序员的人都会投票赞成这个答案。

Instead, use the built in date/time logic that comes with Python: 相反,请使用Python随附的内置日期/时间逻辑:

First, get a list of all of the days that you're interested in: 首先,获取您感兴趣的所有日期的列表:

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 )]

Next, filter down to just the days which are weekends. 接下来,只过滤到周末的日子。 In your case you're interested in Friday and Saturday nights, which are 5 and 6. (Notice how I'm not trying to roll this part into the previous list comprehension, since that'd be hard to verify as correct). 在您的情况下,您对周五和周六晚上分别为5和6感兴趣。(请注意,我不打算将此部分推入先前的列表理解中,因为这很难证明是正确的)。

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 in your list. 最后,您想弄清楚列表中有几个周末。 This is the tricky part, but there are really only four cases to consider, one for each end for either Friday or Saturday. 这是棘手的部分,但实际上只需要考虑四种情况,星期五或星期六的每一端都需要考虑。 Concrete examples help make it clearer, plus this is really the sort of thing you want documented 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, clearer and easier to understand means that you can have more confidence in your code, and can get on with more interesting problems. 更短,更清晰,更容易理解意味着您可以对代码更有信心,并可以解决更多有趣的问题。

To count whole weekends, just adjust the number of days so that you start on a Monday, then divide by seven. 要计算整个周末,只需调整天数,以便从星期一开始,然后除以7。 (Note that if the start day is a weekday, add days to move to the previous Monday, and if it is on a weekend, subtract days to move to the next Monday since you already missed this 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 partial weekends (or weeks), just look at the fractional part of the division by seven. 如果您想知道部分周末(或几周),只需看除以7的小数部分即可。

You would need external logic beside raw math. 除了原始数学之外,您还需要外部逻辑。 You need to have a calendar library (or if you have a decent amount of time implement it yourself) to define what a weekend, what day of the week you start on, end on, etc. 您需要有一个日历库(或者如果您有足够的时间自己实施),以定义一个周末,一周中的哪一天开始,结束等等。

Take a look at Python's calendar class . 看一下Python的日历类

Without a logical definition of days in your code, a pure mathematical methods would fail on corner case, like a interval of 1 day or, I believe, anything lower then a full week (or lower then 6 days if you allowed partials). 如果没有在代码中对天数进行逻辑定义,则纯数学方法将在极端情况下失败,例如间隔1天,或者,我相信,任何低于整整一周的时间(或者如果允许分批,则不到6天)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM