在两个日期之间计算星期几和小时对的数量

7
考虑以下以24H格式表示的星期几-小时对列表:
{
 'Mon': [9,23],
 'Thu': [12, 13, 14],
 'Tue': [11, 12, 14],
 'Wed': [11, 12, 13, 14]
 'Fri': [13],
 'Sat': [],
 'Sun': [],
}

以及两个时间点,例如:

  • Start:

    datetime.datetime(2015, 7, 22, 17, 58, 54, 746784)
    
  • End:

    datetime.datetime(2015, 8, 30, 10, 22, 36, 363912)
    
假设我们需要知道在上述每个“星期几-小时”对之间有多少小时(向上或向下取整),该如何用Python解决这个问题?我在一般层面上研究了timedelta和relativedelta,但没有找到类似于此的东西。为简单起见,我们可以假设所有内容均参考相同的时区。
也许一个更简单的问题是聚焦在单个日期时间对上,例如:在两个任意日期时间之间有多少个“周三14点”?

这里时区有关系吗? - Simeon Visser
谢谢@SimeonVisser。我们可以假设所有时间都指的是同一个时区。我会在原帖中澄清。 - Amelio Vazquez-Reina
基本上,2015年07月22日17:58:54至2015年08月30日10:22:36之间有多少个星期三下午2点? - TigerhawkT3
@TigerhawkT3 正确。 - Amelio Vazquez-Reina
1
我认为你应该从计算两个时间点之间的周数开始。请参考这个答案 - running.t
5个回答

4
也许可以像这样:

也许可以尝试以下方法:

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}}

2

下面是使用循环和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()%来解决问题。


2

这里有另一种使用算术的解决方案:

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”示例)谢谢! - Amelio Vazquez-Reina
@AmelioVazquez-Reina - 我不确定,我没有测试过。我只是怀疑一点算术运算可能比循环遍历每个可能的小时要快。如果你想尝试一些这些解决方案并让我们知道你的结果,我会很感兴趣看到它们。 - TigerhawkT3
澄清一下,我没有测试每个算法的相对性能。 - TigerhawkT3
@AmelioVazquez-Reina - 我刚刚重构了这个,删除了很多不必要的东西。现在应该更清晰、更快了。 - TigerhawkT3

0

所以,如果我正确理解了你的问题,我会从时间范围内找到“小时”的第一个出现,然后逐周寻找下一个出现。就像这样:

#!/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()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接