计算Python中两个日期之间的明暗周期数

时间:2020-07-05 02:03:27

标签: python datetime

这是我最近在我的程序中遇到的一个计算:

给定2个日期和一个明暗周期,计算在这些日期之间出现的明暗周期的(分数)数。

def foo(start_date, end_date, lights_on, lights_off):
    #stuff...
    return (daytimes, nighttimes)
  • start_dateend_date是具有(至少)分钟精度的日期时间对象
  • lights_onlights_offint,分别代表每天打开和关闭灯光的时间。这些是定义“点亮”和“熄灭”时间段的持续时间。
  • lights_onlights_off 可以不均等地划分日期(即不必是12H光照:12H黑暗),而光照周期不能不必在白天和晚上都匹配“真正的” (例如,可以在早上7点关闭灯光,在晚上7点打开灯光)。但是,假定照明周期将是恒定的(即,想象房间或建筑物中的照明,而不是日出/日落),并且照明始终在打开时打开或关闭。小时(例如,晚上8:30灯光不能改变)。
  • 结果应为float表示两个日期之间已完成的点亮和熄灭期间的数量
  • 您可以假设在DST不变的时期内,日期相隔不到一个月。

示例:

import pandas as pd

date1 = pd.Timestamp(year=2020, month=1, day=1, hour=9, minute=0)
date2 = pd.Timestamp(year=2020, month=1, day=3, hour=13, minute=0)
date3 = pd.Timestamp(year=2020, month=1, day=5, hour=20, minute=47)

foo(date1, date2, 9, 17)
#returns (2.5, 2)

foo(date1, date3, 9, 17)
#returns (5, 4.236...)

foo(date1, date2, 19, 7)
#returns (2, 2.333...)

说明:

  • 在9:00至17:00的光照周期中,有 2.5个完整的光照周期(1月1日为9:00至17:00,1月9:00至17:00 1月2日的2号,以及9:00至13:00)和 2个完成的暗周期(1月1日的整个晚上和1月2日的整个晚上)在date1date2
  • 光周期为9:00至17:00,共有 5个完整的光周期(1月1日,2日,3日,4日和5日的9:00至17:00)和 4.236 ..完成了黑暗时段(1月1日,2日,3日和4月一整夜,然后从17:00到20:47,又有3小时47分钟的黑暗,是date1date3之间一个暗周期长度的0.236 ...倍。
  • 光周期 19:00至7:00 ,共有 2个完整的光周期(19:00至7: 1月1日和2日晚上的00)和 2.333 ..完成了黑暗时段(1月1日晚上9:00至17:00的黑暗周期的10/12 +黑暗的12/12在date1和{之间,从1月2日的7:00到17:00的周期+ 1月3日的7:00至13:00的黑暗周期的6/12 = 28/12 = 2.333 ....) {1}}。

对我来说,这种计算很容易概念化但很难编码。我有一个有效的答案(在下面发布),但我为此苦苦挣扎了很长时间,希望看到其他更好的方法。如果有可用的现有库很好,并且我不介意只要输出正确,数据的表示方式是否不同。

2 个答案:

答案 0 :(得分:1)

这是另一种解决方案,它尝试通过将开始时间和结束时间偏移lights_onlights_off的最小值来简化数学,以便每天的周期实际上由{{ 1}}。我已经使用abs(lights_on - lights_off)对象完成了所有数学运算。

datetime.timedelta

输出:

from datetime import datetime, time, timedelta

def cycle_times(start_date, end_date, lights_on):
    start_time = timedelta(hours=start_date.hour, minutes=start_date.minute)
    lights_on_time = timedelta(hours=lights_on)
    # generate the cycles for the first day
    off_time = max(lights_on_time - start_time, timedelta(seconds = 0))
    on_time = timedelta(hours=24) - max(lights_on_time, start_time)
    cycle_start = start_date.replace(hour=0, minute=0) + timedelta(days=1)
    # add a complete cycle for each day before end_date
    while cycle_start.date() < end_date.date():
        cycle_start += timedelta(days=1)
        off_time += lights_on_time
        on_time += timedelta(hours=24-lights_on)
    # generate the partial cycles for the last day
    end_time = timedelta(hours=end_date.hour, minutes=end_date.minute)
    off_time += min(end_time, lights_on_time)
    on_time += max(end_time - lights_on_time, timedelta(seconds=0))
    return (off_time.total_seconds() / 3600, on_time.total_seconds() / 3600)
    
def get_daynight_count(start_time, end_time, lights_on, lights_off):
    if lights_on > lights_off:
        off_period = lights_on - lights_off
        on_period = 24 - off_period
        offset = timedelta(hours=-lights_off)
        off_time, on_time = cycle_times(start_time + offset, end_time + offset, lights_on - lights_off)
    else:
        on_period = lights_off - lights_on
        off_period = 24 - on_period
        offset=timedelta(hours=-lights_on)
        on_time, off_time = cycle_times(start_time + offset, end_time + offset, lights_off - lights_on)
    return (on_time / on_period, off_time / off_period)
    
date1 = datetime(year=2020, month=1, day=1, hour=9, minute=0)
date2 = datetime(year=2020, month=1, day=3, hour=13, minute=0)
date3 = datetime(year=2020, month=1, day=5, hour=20, minute=47)

print(get_daynight_count(date1, date2, 9, 17))
print(get_daynight_count(date1, date3, 9, 17))
print(get_daynight_count(date1, date2, 19, 7))

更新

只需使用小时/分钟数学((2.5, 2.0) (5.0, 4.236458333333333) (2.0, 2.3333333333333335) )即可简化cycle_times代码:

1 hour = 60 minutes

答案 1 :(得分:0)

以下是以下代码中使用的步骤:

  1. 定义一个确定时间是在“白天”还是“晚上”的函数
  2. 创建一个数组date2,该数组以cuts开头,以start_time结尾,并且在每次灯光变化的时候都位于两者之间
  3. 计算每个明暗时段的小时数
  4. 对于end_time中的每个时间(cuts除外),检查它是在“白天”还是“晚上”,并计算到下一次的时间差。
  5. 将每个时间间隔都划分为适当的光照或夜间持续时间。
  6. 求和并返回明暗时段的归一化时间增量。
end_date

对于以上示例:

import pandas as pd
import datetime as dt

#step 1
def is_day_or_night(time, period, lights_on=7, lights_off=19):
    lights_on = dt.time(hour=lights_on)
    lights_off = dt.time(hour=lights_off)
    val = False
    #defaults to checking if at night
    if lights_off > lights_on:
        val = time.time() >= lights_off or time.time() < lights_on
    elif lights_off < lights_on:
        val = time.time() >= lights_off and time.time() < lights_on
    #reverses if period='day'
    return val if period=='night' else not val

def get_daynight_count(start_time, end_time, lights_on, lights_off):
    #step 2
    cuts = []
    cuts.append(start_time)
    loop_time = start_time.replace(minute=0,second=0)
    while loop_time < end_time:
        loop_time += pd.Timedelta(hours=1)
        if loop_time.hour == lights_on:
            cuts.append(loop_time)
        elif loop_time.hour == lights_off:
            cuts.append(loop_time)
    cuts.append(end_time)
    days = []
    nights = []
    #step 3
    if lights_off > lights_on:
        day_hours = lights_off - lights_on
        night_hours = 24 - day_hours
    else:
        night_hours = lights_on - lights_off
        day_hours = 24 - night_hours    
    day_hours = pd.Timedelta(hours = day_hours)
    night_hours = pd.Timedelta(hours = night_hours)
    #step 4 and 5
    for i, t in enumerate(cuts[:-1]):
        if is_day_or_night(t, 'day', lights_on, lights_off):
            days.append((cuts[i+1] - t)/day_hours)
        else:
            nights.append((cuts[i+1] - t)/night_hours)
    #step 6
    return (sum(days),sum(nights))