调整夜间时间的python日期时间差异

mar*_*oxe 8 python datetime numpy pandas

我在python d1和d2中有两个日期时间对象.我想要他们之间的时间差.我想要一些比(d1-d2)稍微复杂的东西:我希望夜间的时间比白天的时间少一个恒定的分数c,例如白天的一小时在白天只算半小时.

在python(pandas和/或numpy)中有一个简单的方法吗?

谢谢!

编辑:夜间时间是从晚上9点到早上7点.但理想情况下,我正在寻找一种解决方案,您可以在白天为任意时段选择任意权重

Thi*_*lle 1

该解决方案可让您根据需要定义任意多个周期及其各自的权重。

\n\n

首先,一个辅助函数对日期时间之间的间隔进行切片:

\n\n
from 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\n
Run Code Online (Sandbox Code Playgroud)\n\n

该类计算给定的周期和权重列表的加权持续时间:

\n\n
class 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\n
Run Code Online (Sandbox Code Playgroud)\n\n

我们创建一个 WeightedDuration 实例,定义周期及其权重。\n我们可以拥有任意多个周期,权重小于或大于 1。

\n\n
wd = 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%\n
Run 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\n
Run Code Online (Sandbox Code Playgroud)\n