该解决方案可让您根据需要定义任意多个周期及其各自的权重。
\n\n首先,一个辅助函数对日期时间之间的间隔进行切片:
\n\nfrom datetime import date, time, datetime, timedelta\n\ndef slice_datetimes_interval(start, end):\n """\n Slices the interval between the datetimes start and end.\n\n If start and end are on different days:\n start time -> midnight | number of full days | midnight -> end time\n ---------------------- ------------------- --------------------\n ^ ^ ^\n day_part_1 full_days day_part_2\n\n If start and end are on the same day:\n start time -> end time\n ----------------------\n ^\n day_part_1 full_days = 0\n\n Returns full_days and the list of day_parts (as tuples of time objects).\n """\n\n if start > end:\n raise ValueError("Start time must be before end time")\n\n # Number of full days between the end of start day and the beginning of end day\n # If start and end are on the same day, it will be -1\n full_days = (datetime.combine(end, time.min) - \n datetime.combine(start, time.max)).days\n if full_days >= 0:\n day_parts = [(start.time(), time.max),\n (time.min, end.time())]\n else:\n full_days = 0\n day_parts = [(start.time(), end.time())]\n\n return full_days, day_parts\nRun Code Online (Sandbox Code Playgroud)\n\n该类计算给定的周期和权重列表的加权持续时间:
\n\nclass WeightedDuration:\n def __init__(self, periods):\n """\n periods is a list of tuples (start_time, end_time, weight)\n where start_time and end_time are datetime.time objects.\n\n For a period including midnight, like 22:00 -> 6:30,\n we create two periods:\n - midnight (start of day) -> 6:30,\n - 22:00 -> midnight(end of day)\n\n so periods will be:\n [(time.min, time(6, 30), 0.5),\n (time(22, 0), time.max, 0.5)]\n\n """\n self.periods = periods\n # We store the weighted duration of a whole day for later reuse\n self.day_duration = self.time_interval_duration(time.min, time.max)\n\n def time_interval_duration(self, start_time, end_time):\n """ \n Returns the weighted duration, in seconds, between the datetime.time objects\n start_time and end_time - so, two times on the *same* day.\n """\n dummy_date = date(2000, 1, 1)\n\n # First, we calculate the total duration, *without weight*.\n # time objects can\'t be substracted, so\n # we turn them into datetimes on dummy_date\n duration = (datetime.combine(dummy_date, end_time) -\n datetime.combine(dummy_date, start_time)).total_seconds()\n\n # Then, we calculate the reductions during all periods\n # intersecting our interval\n reductions = 0\n for period in self.periods:\n period_start, period_end, weight = period\n if period_end < start_time or period_start > end_time:\n # the period and our interval don\'t intersect\n continue\n\n # Intersection of the period and our interval\n start = max(start_time, period_start)\n end = min (end_time, period_end)\n\n reductions += ((datetime.combine(dummy_date, end) -\n datetime.combine(dummy_date, start)).total_seconds()\n * (1 - weight))\n # as time.max is midnight minus a \xc2\xb5s, we round the result\n return round(duration - reductions)\n\n def duration(self, start, end):\n """\n Returns the weighted duration, in seconds, between the datetime.datetime\n objects start and end.\n """\n full_days, day_parts = slice_datetimes_interval(start, end)\n dur = full_days * self.day_duration\n for day_part in day_parts:\n dur += self.time_interval_duration(*day_part)\n return dur\nRun Code Online (Sandbox Code Playgroud)\n\n我们创建一个 WeightedDuration 实例,定义周期及其权重。\n我们可以拥有任意多个周期,权重小于或大于 1。
\n\nwd = WeightedDuration([(time.min, time(7, 0), 0.5), # from midnight to 7, 50%\n (time(12, 0), time(13, 0), 0.75), # from 12 to 13, 75%\n (time(21, 0), time.max, 0.5)]) # from 21 to midnight, 50%\nRun Code Online (Sandbox Code Playgroud)\n\n让我们计算日期时间之间的加权持续时间:
\n\n# 1 hour at 50%, 1 at 100%: that should be 3600 + 1800 = 5400 s\nprint(wd.duration(datetime(2017, 1, 3, 6, 0), datetime(2017, 1, 3, 8)))\n# 5400\n\n# a few tests\nintervals = [\n (datetime(2017, 1, 3, 9, 0), datetime(2017, 1, 3, 10)), # 1 hour with weight 1\n (datetime(2017, 1, 3, 23, 0), datetime(2017, 1, 4, 1)), # 2 hours, weight 0.5\n (datetime(2017, 1, 3, 5, 0), datetime(2017, 1, 4, 5)), # 1 full day\n (datetime(2017, 1, 3, 5, 0), datetime(2017, 1, 3, 23)), # same day\n (datetime(2017, 1, 3, 5, 0), datetime(2017, 1, 4, 23)), # next day\n (datetime(2017, 1, 3, 5, 0), datetime(2017, 1, 5, 23)), # 1 full day in between\n ]\nfor interval in intervals:\n print(interval)\n print(wd.duration(*interval)) \n\n# (datetime.datetime(2017, 1, 3, 9, 0), datetime.datetime(2017, 1, 3, 10, 0))\n# 3600\n# (datetime.datetime(2017, 1, 3, 23, 0), datetime.datetime(2017, 1, 4, 1, 0))\n# 3600\n# (datetime.datetime(2017, 1, 3, 5, 0), datetime.datetime(2017, 1, 4, 5, 0))\n# 67500\n# (datetime.datetime(2017, 1, 3, 5, 0), datetime.datetime(2017, 1, 3, 23, 0))\n# 56700\n# (datetime.datetime(2017, 1, 3, 5, 0), datetime.datetime(2017, 1, 4, 23, 0))\n# 124200\n# (datetime.datetime(2017, 1, 3, 5, 0), datetime.datetime(2017, 1, 5, 23, 0))\n# 191700\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
934 次 |
| 最近记录: |