填补Python中的时间空白

时间:2018-10-30 19:55:42

标签: python

在给定端点和端点之间的某些点的情况下,是否存在更优雅的方法来确定互斥/穷举间隔?

以下测试以一个月的计费期以及该月内的几个点或区间划分来描述该方案。我想得到一个成对的列表,详细列出由给定的边界导致的各个间隔。

def test_fill_time_gaps(self):
    bill_period = (localtz_parse('2018-03-01'), localtz_parse('2018-03-31'))

    # test fill first gap
    periods = fill_time_gaps(bill_period, [(localtz_parse('2018-03-04'), localtz_parse('2018-03-31'))])
    self.assertEqual(periods, [(localtz_parse('2018-03-01'), localtz_parse('2018-03-04')),
                               (localtz_parse('2018-03-04'), localtz_parse('2018-03-31'))])

    periods = fill_time_gaps(bill_period, [(localtz_parse('2018-03-04'), localtz_parse('2018-03-05')),
                                                           (localtz_parse('2018-03-05'), localtz_parse('2018-03-31'))])
    self.assertEqual(periods, [(localtz_parse('2018-03-01'), localtz_parse('2018-03-04')),
                               (localtz_parse('2018-03-04'), localtz_parse('2018-03-05')),
                               (localtz_parse('2018-03-05'), localtz_parse('2018-03-31'))])

    # test fill first and last gap
    periods = fill_time_gaps(bill_period, [(localtz_parse('2018-03-04'), localtz_parse('2018-03-15'))])
    self.assertEqual(periods, [(localtz_parse('2018-03-01'), localtz_parse('2018-03-04')),
                               (localtz_parse('2018-03-04'), localtz_parse('2018-03-15')),
                               (localtz_parse('2018-03-15'), localtz_parse('2018-03-31'))])

    # test fill first gap and gap in between
    periods = fill_time_gaps(bill_period, [(localtz_parse('2018-03-04'), localtz_parse('2018-03-05')),
                                                           (localtz_parse('2018-03-08'), localtz_parse('2018-03-31'))])
    self.assertEqual(periods, [(localtz_parse('2018-03-01'), localtz_parse('2018-03-04')),
                               (localtz_parse('2018-03-04'), localtz_parse('2018-03-05')),
                               (localtz_parse('2018-03-05'), localtz_parse('2018-03-08')),
                               (localtz_parse('2018-03-08'), localtz_parse('2018-03-31'))])

    # test fill first gap and gap in between and last gap
    periods = fill_time_gaps(bill_period, [(localtz_parse('2018-03-04'), localtz_parse('2018-03-05')),
                                                           (localtz_parse('2018-03-08'), localtz_parse('2018-03-15'))])
    self.assertEqual(periods, [(localtz_parse('2018-03-01'), localtz_parse('2018-03-04')),
                               (localtz_parse('2018-03-04'), localtz_parse('2018-03-05')),
                               (localtz_parse('2018-03-05'), localtz_parse('2018-03-08')),
                               (localtz_parse('2018-03-08'), localtz_parse('2018-03-15')),
                               (localtz_parse('2018-03-15'), localtz_parse('2018-03-31'))])

这是我最初的尝试:

def fill_time_gaps(boundary, periods):
    """
    Given a period boundary, fill in gaps within given periods
    Assuming periods are in seqential order
    :param boundary: period boundry
    :param periods: sequence of periods, should contain at least one period
    :return: sequence of periods with filled gaps
    """
    if not len(periods):
        raise Exception('periods should contain at least one period')

    # works by stepping through the periods and compare the against the way-point
    # to determine if there is a gap
    result = []
    bound_start, bound_end = boundary
    way_point = bound_start

    for period in periods:
        period_start, period_end = period

        if period_start > way_point:
            gap = (way_point, period_start)
            result.append(gap)

        result.append(period)
        way_point = period_end

    # fill the last gap
    if way_point < bound_end:
        result.append((way_point, bound_end))

    return result

但是这似乎有点“愚蠢”(有时愚蠢的代码是好的代码,但在这种情况下不确定),我不确定这是否也是防弹的,但它可以通过我现有的测试。

我渴望知道是否有更好的解决方法?

1 个答案:

答案 0 :(得分:1)

由于填充逻辑实际上并不依赖于日期时间表示形式,因此为了便于理解,我将其缩减为日期数字。

  • 采用结算端点并包括提供的内部期间 分隔线。
  • 进行一组删除重复项(计费端点) 并对生成的“围栏帖子”进行排序。
  • 逐步执行此顺序,并从相邻的日期开始建立周期。
  • 将该列表返回给呼叫者。

代码:

def fill_time_gaps(boundary, periods):
    bound = sorted(list(set(boundary + periods)))
    return tuple([(post, bound[i+1]) for i, post in enumerate(bound[:-1])])

billing = (1, 31)
test = [(2, 3),
        (1, 8),
        (10, 31),
        (4, 5, 12)]

for case in test:
    print(fill_time_gaps(billing, case))

输出:

((1, 2), (2, 3), (3, 31))
((1, 8), (8, 31))
((1, 10), (10, 31))
((1, 4), (4, 5), (5, 12), (12, 31))