【问题标题】:Given a date range how to calculate the number of weekends partially or wholly within that range?给定一个日期范围,如何计算部分或全部在该范围内的周末数?
【发布时间】:2010-10-27 09:14:04
【问题描述】:

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

(根据要求提供一些定义: 取“周末”来表示周六和周日。 日期范围包括在内,即结束日期是范围的一部分 “全部或部分”表示周末在日期范围内的任何部分都意味着整个周末都被计算在内。)

为简化起见,我想您实际上只需要知道持续时间以及初始日期是一周中的哪一天...

我现在很好,这将涉及到对整数除以 7 和一些逻辑来根据余数加 1,但我不太清楚是什么...

Python 答案加分 ;-)

编辑

这是我的最终代码。

周末是周五和周六(因为我们计算了住宿的夜数),从周一开始,天数为 0。我使用了 onebyone 的算法和 Tom 的代码布局。非常感谢各位。

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

【问题讨论】:

  • 首先定义“星期”。严重地。它从哪一天开始?你是四舍五入到下一个整周还是截断到前一个整周?您是从匹配日(周三到周三)开始,还是忽略小数周并从周日到周日(或周一到周一)?
  • 周末数是否意味着日期范围内的周六和/或周日数?
  • S.Lott - 数周不涉及。你有一系列的日子。如果周末(如果您愿意,定义为周六和周日)部分或全部在该期间,那么它很重要。除了定义周末之外,如果您仔细阅读,原始问题中的所有内容都会清楚地说明。
  • 抱歉,不清楚。如果范围从星期日开始到下一个星期六结束,是 0、1 还是 2 个周末?
  • @andybak:这是我的观点。这个问题有一些小的逻辑漏洞。 “通常暗示”和“大多数人解释”不会堵住漏洞。在日历计算的情况下,细节非常重要。请使用详细信息更新问题,以便有类似问题的其他人可以从您的问题中受益。

标签: python date date-arithmetic


【解决方案1】:

我对这类事情的一般方法:不要开始尝试重新实现自己的日期逻辑 - 这很难,即。你会把它搞砸的边缘情况,看起来很糟糕。 提示:如果您在程序中的任何地方都有 mod 7 算术,或者在程序中的任何地方将日期视为整数:你失败了。如果我在我的代码库中的任何地方(甚至附近)看到“公认的解决方案”,就需要有人重新开始。任何认为自己是程序员的人都会投票赞成这个答案,这令人难以置信。

改为使用 Python 自带的内置日期/时间逻辑:

首先,获取您感兴趣的所有日期的列表:

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

接下来,过滤到仅属于周末的日子。在您的情况下,您对周五和周六晚上感兴趣,分别是 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

最后,您想弄清楚您的列表中有多少个周末。这是棘手的部分,但实际上只有四种情况需要考虑,周五或周六各结束一个。具体示例有助于使其更清晰,而且这确实是您希望在代码中记录的内容:

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

更短、更清晰、更容易理解意味着您可以对自己的代码更有信心,并且可以继续解决更多有趣的问题。

【讨论】:

    【解决方案2】:

    要计算整个周末,只需将天数调整为从星期一开始,然后除以 7。 (请注意,如果开始日是工作日,则添加天数以移至上周一,如果是周末,则减去天数以移至下周一,因为您已经错过了本周末。)

    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)
    

    如果您想知道部分周末(或周),只需查看除以七的小数部分。

    【讨论】:

      【解决方案3】:

      除了原始数学之外,您还需要外部逻辑。您需要有一个日历库(或者如果您有大量时间自己实现它)来定义周末、一周中的哪一天开始、结束等。

      看看Python's calendar class

      如果您的代码中没有对天数的逻辑定义,纯数学方法会在极端情况下失败,例如 1 天的间隔,或者我相信任何低于整周的时间(或者如果允许部分则低于 6 天) )。

      【讨论】:

      • 我不认为你这样做。我们可以用整数重铸这个问题。给定一个整数范围 - 有多少间隔为 7 的 2 个整数的“条带”完全或部分在该范围内...
      • 日历库不是必需的,但它可能会很有帮助。如果目标是快速解决这个问题,可能会使用“日历”或“日期时间”类。如果这只是一个智力练习,那么一定要用整数算术把自己搞垮。 ;-)
      • @andybak 作为一个反驳点,考虑一下周六和周日有 2 天的情况,而不是周一、周二。或者更进一步,假设你有一天,星期六。我认为任何原始整数方法都会在那里失败。您的代码需要对一周中的哪几天以及周末构成一些清晰的逻辑概念。
      • @andybak,反对票应该是可撤销的。除非他们减少了您必须撤消它的时间。让我编辑我的问题,然后你应该可以撤消投票,如果你愿意的话。
      • 所选答案确实定义了天数。随着您使用内置库或滚动您自己的库,您仍然不能只将一个数字除以 7 的间隔来得到您的答案。您的代码必须对它所代表的日期有一些概念。
      【解决方案4】:

      这种事情的一般方法:

      对于一周中的每一天,计算从那一天开始的时间段“包含一个周末”之前需要多少天。例如,如果“包含一个周末”的意思是“包含周六和周日”,那么我们有下表:

      星期日:8 星期一:7 周二:6 星期三:5 星期四:4 周五:3 周六:2

      对于“部分或全部”,我们有:

      星期日:1 星期一:6 周二:5 星期三:4 星期四:3 周五:2 周六:1

      显然,这不必编码为表格,现在它的样子很明显。

      然后,给定周期开始的星期几,从周期长度中减去 [*] 魔法值(以天为单位)(可能 start-end+1,包括两个栅栏)。如果结果小于 0,则它包含 0 个周末。如果它等于或大于 0,则它包含(至少)1 个周末。

      然后你必须处理剩余的日子。在第一种情况下,这很容易,每 7 天多一个周末。在第二种情况下,除周日外的每个开始日也是如此,周日只需要再多 6 天就可以包括另一个周末。因此,在第二种情况下,从星期日开始的时段,您可以在时段开始时计算 1 个周末,然后从长度中减去 1 并从星期一重新计算。

      更一般地说,“整个或部分”周末在这里发生的事情是,我们正在检查我们是否从有趣的部分(“周末”)中途开始。如果是这样,我们可以:

      • 1) 数一,将开始日期移到感兴趣位的末尾,然后重新计算。
      • 2) 将开始日期移回感兴趣位的开头,然后重新计算。

      在周末的情况下,只有一种特殊情况在中途开始,所以 (1) 看起来不错。但是,如果您将日期作为日期+时间以秒而不是天为单位,或者如果您对 5 天工作周而不是 2 天周末感兴趣,那么 (2) 可能更容易理解。

      [*] 当然,除非您使用的是无符号类型。

      【讨论】:

      • 太棒了。感谢您解决具体案例并解释一般案例。最佳答案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-09-19
      • 1970-01-01
      • 2012-02-05
      • 1970-01-01
      • 2020-09-04
      相关资源
      最近更新 更多