【问题标题】:Counting day-of-week-hour pairs between two dates计算两个日期之间的星期几对
【发布时间】:2015-10-13 00:39:20
【问题描述】:

考虑以下24H 格式的day-of-week-hour 对列表:

{
 'Mon': [9,23],
 'Thu': [12, 13, 14],
 'Tue': [11, 12, 14],
 'Wed': [11, 12, 13, 14]
 'Fri': [13],
 'Sat': [],
 'Sun': [],
}

和两个时间点,例如:

  • 开始:

    datetime.datetime(2015, 7, 22, 17, 58, 54, 746784)
    
  • 结束

    datetime.datetime(2015, 8, 30, 10, 22, 36, 363912)
    

假设我们需要知道对于上面指定的每个 day-of-week-hour 对,这两个日期时间之间有多少小时(向上或向下舍入)。

如何在 Python 中解决这个问题?我对timedeltarelativedelta 进行了大致的详细研究,但没有找到任何可以提供与此类似的东西。

为简单起见,我们可以假设所有内容都指向同一个时区。


也许一个更简单的问题是专注于单个日-小时对,例如两个任意日期时间之间有多少个Wednesdays: 14

【问题讨论】:

  • 时区在这里重要吗?
  • 谢谢@SimeonVisser 我们可以假设一切都指的是同一个时区。我将在 OP 中澄清。
  • 那么基本上,在 2015-07-22 17:58:54 和 2015-08-30 10:22:36 之间有多少个星期三下午 2 点?
  • @TigerhawkT3 正确。
  • 我相信你应该从计算两个时间点之间的周数开始。见this answer

标签: python datetime python-3.x timedelta


【解决方案1】:

大概是这样的:

from calendar import day_abbr
from datetime import datetime, timedelta


def solve(start, end, data):
    days = list(day_abbr)
    output = dict.fromkeys(days, 0)

    while start <= end:
        day = days[start.weekday()]
        if start.hour in data[day]:
            output[day] += 1
        start = start + timedelta(minutes=60)

    return output


data = {
 'Mon': [9, 23],
 'Thu': [12, 13, 14],
 'Tue': [11, 12, 14],
 'Wed': [11, 12, 13, 14],
 'Fri': [13],
 'Sat': [],
 'Sun': [],
}

start = datetime(2015, 7, 22, 17, 58, 54, 746784)
end = datetime(2015, 8, 30, 10, 22, 36, 363912)

print solve(start, end, data)
# {'Wed': 20, 'Sun': 0, 'Fri': 6, 'Tue': 15, 'Mon': 10, 'Thu': 18, 'Sat': 0} 

按小时计算每天的计数:

from calendar import day_abbr
from collections import defaultdict
from datetime import datetime, timedelta
from pprint import pprint


def solve(start, end, data):
    days = list(day_abbr)
    output = defaultdict(lambda: defaultdict(int))

    while start <= end:
        day = days[start.weekday()]
        if start.hour in data[day]:
            output[day][start.hour] += 1
        start = start + timedelta(minutes=60)
    return {k: dict(v) for k, v in output.items()}


data = {
 'Mon': [9, 23],
 'Thu': [12, 13, 14],
 'Tue': [11, 12, 14],
 'Wed': [11, 12, 13, 14],
 'Fri': [13],
 'Sat': [],
 'Sun': [],
}

start = datetime(2015, 7, 22, 17, 58, 54, 746784)
end = datetime(2015, 8, 30, 10, 22, 36, 363912)

pprint(solve(start, end, data))
# output 
{'Fri': {13: 6},
 'Mon': {9: 5, 23: 5},
 'Thu': {12: 6, 13: 6, 14: 6},
 'Tue': {11: 5, 12: 5, 14: 5},
 'Wed': {11: 5, 12: 5, 13: 5, 14: 5}}

【讨论】:

    【解决方案2】:

    也许我没有完全理解你的问题,但你可以得到两个日期之间的所有时间,并总结两个日期之间每小时和每天出现的次数:

    from datetime import datetime
    from dateutil import  rrule,parser
    
    
    d={
     'Mon': [9, 23],
     'Thu': [12, 13, 14],
     'Tue': [11, 12, 14],
     'Wed': [11, 12, 13, 14],
     'Fri': [13],
     'Sat': [],
     'Sun': [],
    }
    
    st = datetime(2015, 7, 22, 17, 58, 54, 746784)
    
    ed = datetime(2015, 8, 30, 10, 22, 36, 363912)
    dates = list(rrule.rrule(rrule.HOURLY,
                             dtstart=parser.parse(st.strftime("%Y-%m-%d %H:%M:%S")),
                             until=parser.parse(ed.strftime("%Y-%m-%d %H:%M:%S"))))
    
    
    days = {"Mon":0,"Tue": 1,"Wed":2,"Thu": 3,"Fri":4,"Sat":5,"Sun":6}
    
    for k, val in d.items():
        for v in val:
            print("day: {} hour: {}".format(k,v))
            day = days[k]
            print(sum((v == dt.hour and dt.weekday() == day) for dt in dates))
    

    输出:

    day: Wed hour: 11
    5
    day: Wed hour: 12
    5
    day: Wed hour: 13
    5
    day: Wed hour: 14
    5
    day: Fri hour: 13
    6
    day: Tue hour: 11
    5
    day: Tue hour: 12
    5
    day: Tue hour: 14
    5
    day: Mon hour: 9
    6
    day: Mon hour: 23
    5
    day: Thu hour: 12
    5
    day: Thu hour: 13
    5
    day: Thu hour: 14
    5
    

    不确定您是想要每个列表中所有小时的总和还是每个小时的总和,但无论哪种方式,您都可以将输出存储在字典中。

    counts = {'Thu':{}, 'Sun':{}, 'Fri':{}, 'Mon':{}, 'Tue':{}, 'Sat':{}, 'Wed':{}}
    for k, val in d.items():
        for v in val:
            day = days[k]
            sm = sum((v == dt.hour and dt.weekday() == day) for dt in dates)
            counts[k][v] = sm
    
    from pprint import pprint as pp
    pp(counts)
    

    输出:

    {'Fri': {13: 6},
     'Mon': {9: 5, 23: 5},
     'Sat': {},
     'Sun': {},
     'Thu': {12: 6, 13: 6, 14: 6},
     'Tue': {11: 5, 12: 5, 14: 5},
     'Wed': {11: 5, 12: 5, 13: 5, 14: 5}}
    

    【讨论】:

      【解决方案3】:

      这是一个带有循环和datetime的解决方案:

      import datetime
      
      pairs = {1: [9,23],
      2: [11, 12, 14],
      3: [11, 12, 13, 14],
      4: [12, 13, 14],
      5: [13],
      6: [],
      7: []
      }
      
      start = datetime.datetime(2015, 7, 22, 17, 58, 54, 746784)
      end = datetime.datetime(2015, 8, 30, 10, 22, 36, 363912)
      result={}
      for d,hl in pairs.items():
          for h in hl:
              result[(d,h)] = 0
              for diff in range((end-start).days*24):
                  comp = start + datetime.timedelta(hours=diff)
                  if comp.isoweekday() == d and comp.hour == h:
                      result[(d,h)] += 1
      

       

      >>> result
      {(3, 12): 5, (5, 13): 6, (3, 13): 5, (1, 23): 5, (2, 11): 5, (3, 11): 5, (4, 14): 6, (4, 13): 6, (4, 12): 6, (2, 12): 5, (2, 14): 5, (3, 14): 5, (1, 9): 5}
      

      我也会尝试使用timestamp()% 的解决方案。

      【讨论】:

        【解决方案4】:

        这是另一种算术解决方案:

        import datetime
        
        pairs = {1: [9,23],
        2: [11, 12, 14],
        3: [11, 12, 13, 14],
        4: [12, 13, 14],
        5: [13],
        6: [],
        7: []
        }
        
        start = datetime.datetime(2015, 7, 22, 17, 58, 54, 746784)
        end = datetime.datetime(2015, 8, 30, 10, 22, 36, 363912)
        result={}
        weeks = (end-start).days//7
        
        for d,hl in pairs.items():
            for h in hl:
                initial = weeks
                if d > start.isoweekday() or (
                   d == start.isoweekday() and h >= start.hour):
                    initial += 1
                result[(d,h)] = initial
        

         

        >>> for k in sorted(result):
        ...     print(k, result[k])
        ...
        (1, 9) 5
        (1, 23) 5
        (2, 11) 5
        (2, 12) 5
        (2, 14) 5
        (3, 11) 5
        (3, 12) 5
        (3, 13) 5
        (3, 14) 5
        (4, 12) 6
        (4, 13) 6
        (4, 14) 6
        (5, 13) 6
        

        【讨论】:

        • 谢谢! -- 有什么理由相信其中一种解决方案比另一种更有效? (例如,循环通过数十万个 pairs 示例)
        • @AmelioVazquez-Reina - 我不确定;我没有测试它。我只是怀疑一点算术会比每个可能的小时循环更快。如果您想尝试其中一些解决方案并让我们知道您的结果,我很想看到它们。
        • 澄清一下,我没有测试每种算法的相对性能
        • @AmelioVazquez-Reina - 我刚刚重构了这个,删除了很多不必要的东西。现在应该更清晰更快了。
        【解决方案5】:

        因此,如果我正确理解了您的问题,我将首先在时间范围内查找“小时”的第一次出现,然后逐周寻找下一次出现。像这样:

        #!/usr/bin/python
        from __future__ import print_function
        import datetime
        import dateutil.relativedelta
        
        
        def hours_between(start, end, weekday, hour):
            first = start + dateutil.relativedelta.relativedelta(
                weekday=weekday, hour=hour,
                minute=0, second=0, microsecond=0)
            week = dateutil.relativedelta.relativedelta(weeks=1)
        
            all_dates = []
            d = first
            while d < end:
                all_dates.append(d)
                d += week
        
            return all_dates
        
        
        def main():
            start = datetime.datetime(2015, 7, 22, 17, 58, 54, 746784)
            end = datetime.datetime(2015, 8, 30, 10, 22, 36, 363912)
            all_dates = hours_between(start, end, dateutil.relativedelta.WE, 14)
            print(all_dates)
            print(len(all_dates))
        
        main()
        

        【讨论】:

          猜你喜欢
          • 2018-03-18
          • 2013-03-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-11-25
          • 1970-01-01
          • 2012-01-28
          • 1970-01-01
          相关资源
          最近更新 更多