【问题标题】:Generate a list of datetimes between an interval生成间隔之间的日期时间列表
【发布时间】:2012-05-28 02:47:18
【问题描述】:

给定两个日期时间(start_dateend_date),我想生成这两个日期之间的其他日期时间列表,新的日期时间由可变间隔分隔。例如在 2011 年 10 月 10 日和 2011 年 12 月 12 日之间每 4 天或从现在到明天晚上 19​​ 点之间每 8 小时一次。

可能大致相当于Dateperiod PHP 类。

在 Python 中完成此任务的最有效方法是什么?

【问题讨论】:

    标签: python list datetime date period


    【解决方案1】:

    使用datetime.timedelta:

    from datetime import date, datetime, timedelta
    
    def perdelta(start, end, delta):
        curr = start
        while curr < end:
            yield curr
            curr += delta
    
    >>> for result in perdelta(date(2011, 10, 10), date(2011, 12, 12), timedelta(days=4)):
    ...     print result
    ...
    2011-10-10
    2011-10-14
    2011-10-18
    2011-10-22
    2011-10-26
    2011-10-30
    2011-11-03
    2011-11-07
    2011-11-11
    2011-11-15
    2011-11-19
    2011-11-23
    2011-11-27
    2011-12-01
    2011-12-05
    2011-12-09
    

    适用于日期和日期时间对象。你的第二个例子:

    >>> for result in perdelta(datetime.now(),
    ...         datetime.now().replace(hour=19) + timedelta(days=1),
    ...         timedelta(hours=8)):
    ...     print result
    ... 
    2012-05-21 17:25:47.668022
    2012-05-22 01:25:47.668022
    2012-05-22 09:25:47.668022
    2012-05-22 17:25:47.668022
    

    【讨论】:

    • Python 应该允许range 中的日期。这才有意义。弱类型是有原因的......
    • @TylerCrompton:“显式优于隐式。”范围内会增加什么:天、秒、微秒、毫秒、分钟、小时或周?
    • @NoctisSkytower 步长值将是timedelta 对象。
    • 使用日期对象时需要。
    • 那个生成器是我一整天见过的最漂亮的一段 Python 代码。谢谢!
    【解决方案2】:

    试试这个:

    from datetime import datetime
    from dateutil.relativedelta import relativedelta
    
    def date_range(start_date, end_date, increment, period):
        result = []
        nxt = start_date
        delta = relativedelta(**{period:increment})
        while nxt <= end_date:
            result.append(nxt)
            nxt += delta
        return result
    

    问题中的示例“从现在到明天 19:00 之间每 8 小时”将这样写:

    start_date = datetime.now()
    end_date = start_date + relativedelta(days=1)
    end_date = end_date.replace(hour=19, minute=0, second=0, microsecond=0)
    date_range(start_date, end_date, 8, 'hours')    
    

    注意period 的有效值是为relativedelta 相关信息定义的值,即:'years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'microseconds'

    我的解决方案根据问题的要求返回一个列表。如果您一次不需要所有元素,则可以使用生成器,如@MartijnPieters 的回答。

    【讨论】:

      【解决方案3】:

      我真的很喜欢@Martijn Pieters 和@Óscar López 的两个答案。 让我建议我在这两个答案之间的组合解决方案。

      from datetime import date, datetime, timedelta
      
      def datetime_range(start, end, delta):
          current = start
          if not isinstance(delta, timedelta):
              delta = timedelta(**delta)
          while current < end:
              yield current
              current += delta
      
      
      start = datetime(2015,1,1)
      end = datetime(2015,1,31)
      
      #this unlocks the following interface:
      for dt in datetime_range(start, end, {'days': 2, 'hours':12}):
          print dt
          print dt
      
      2015-01-01 00:00:00
      2015-01-03 12:00:00
      2015-01-06 00:00:00
      2015-01-08 12:00:00
      2015-01-11 00:00:00
      2015-01-13 12:00:00
      2015-01-16 00:00:00
      2015-01-18 12:00:00
      2015-01-21 00:00:00
      2015-01-23 12:00:00
      2015-01-26 00:00:00
      2015-01-28 12:00:00
      

      【讨论】:

        【解决方案4】:

        此处建议的解决方案适用于几天、几小时等间隔。使用 timedeltadateutil.relativedelta 支持的任何内容(如果您想依赖第三方库)。但我想以 yyyymm 格式分享我针对每月间隔的特定情况的解决方案,询问here(但标记为此问题的重复)。

        def iterate_months(start_ym, end_ym):
            for ym in range(int(start_ym), int(end_ym) + 1):
                if ym % 100 > 12 or ym % 100 == 0:
                    continue
                yield str(ym)
        
        list(iterate_months('201710', '201803'))
        

        输出:

        ['201710', '201711', '201712', '201801', '201802', '201803']
        

        这个解决方案非常适合这种对 yyyymm 格式的特殊需求(尽管它至少在我的世界中经常出现)并且对于大量 continues 可能不是最有效的答案,但具有优势简洁,易于理解,并且不涉及许多库或日期转换代码。

        【讨论】:

          【解决方案5】:

          到目前为止,此处给出的所有解决方案都特定于 start

          import datetime
          import operator
          
          def time_range(start: datetime.datetime, stop, step: datetime.timedelta):
              "Imitate range function for datetimes instead of ints."
              sec = step.total_seconds()
              if sec == 0:
                  raise ValueError("step must not be 0 seconds")
              if sec < 0:
                  compare = operator.gt
              else:
                  compare = operator.lt
              x = start
              while compare(x, stop):
                  yield x
                  x += step  # immutable
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2021-09-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-01-22
            • 1970-01-01
            • 2022-01-14
            相关资源
            最近更新 更多