【问题标题】:Creating a range of dates in Python在 Python 中创建日期范围
【发布时间】:2010-11-02 20:42:14
【问题描述】:

我想创建一个日期列表,从今天开始,回溯任意天数,例如在我的示例中为 100 天。还有比这更好的方法吗?

import datetime

a = datetime.datetime.today()
numdays = 100
dateList = []
for x in range (0, numdays):
    dateList.append(a - datetime.timedelta(days = x))
print dateList

【问题讨论】:

    标签: python datetime date


    【解决方案1】:

    稍微好一点...

    base = datetime.datetime.today()
    date_list = [base - datetime.timedelta(days=x) for x in range(numdays)]
    

    【讨论】:

    • 如果我想获取前 7 个日期,不包括今天的日期怎么办? @S.Lott
    • @MohammadAmir date_list = [base - datetime.timedelta(days=x+1) for x in range(7)]
    • "稍微好一点..." 更正:一个 Lott 更好。
    【解决方案2】:

    Pandas 通常非常适合时间序列,并且直接支持日期范围。

    例如pd.date_range():

    import pandas as pd
    from datetime import datetime
    
    datelist = pd.date_range(datetime.today(), periods=100).tolist()
    

    它还有很多选项可以让生活更轻松。例如,如果您只想要工作日,则只需换入 bdate_range

    date range documentation

    此外,它完全支持 pytz 时区,并且可以顺利跨越春季/秋季 DST 班次。

    由 OP 编辑​​:

    如果您需要实际的 python 日期时间,而不是 Pandas 时间戳:

    import pandas as pd
    from datetime import datetime
    
    pd.date_range(end = datetime.today(), periods = 100).to_pydatetime().tolist()
    
    #OR
    
    pd.date_range(start="2018-09-09",end="2020-02-02")
    
    

    这使用“end”参数来匹配原始问题,但如果你想要降序日期:

    pd.date_range(datetime.today(), periods=100).to_pydatetime().tolist()
    

    【讨论】:

      【解决方案3】:

      获取指定开始日期和结束日期之间的日期范围(针对时间和空间复杂性进行了优化):

      import datetime
      
      start = datetime.datetime.strptime("21-06-2014", "%d-%m-%Y")
      end = datetime.datetime.strptime("07-07-2014", "%d-%m-%Y")
      date_generated = [start + datetime.timedelta(days=x) for x in range(0, (end-start).days)]
      
      for date in date_generated:
          print date.strftime("%d-%m-%Y")
      

      【讨论】:

      • 最初的建议是使用 () 而不是 [] 来获取 date_generator。从某种意义上说是高效的,无需存储整个日期数组并仅在需要时生成一个。
      • 注意 end_date 没有生成。
      • 使用 (end-start+1) 获取结束日期。
      • 不回答 OP 的问题,但这就是我所追求的 :)
      • (end-start+1) 不起作用,您无法添加 timedelta 和 int。不过你可以使用(end-start).days + 1
      【解决方案4】:

      您可以编写一个生成器函数,从今天开始返回日期对象:

      import datetime
      
      def date_generator():
        from_date = datetime.datetime.today()
        while True:
          yield from_date
          from_date = from_date - datetime.timedelta(days=1)
      

      此生成器返回从今天开始的日期,并一次返回一天。以下是如何获取前 3 个日期:

      >>> import itertools
      >>> dates = itertools.islice(date_generator(), 3)
      >>> list(dates)
      [datetime.datetime(2009, 6, 14, 19, 12, 21, 703890), datetime.datetime(2009, 6, 13, 19, 12, 21, 703890), datetime.datetime(2009, 6, 12, 19, 12, 21, 703890)]
      

      与循环或列表推导相比,这种方法的优势在于您可以根据需要多次返回。

      编辑

      使用生成器表达式而不是函数的更紧凑的版本:

      date_generator = (datetime.datetime.today() - datetime.timedelta(days=i) for i in itertools.count())
      

      用法:

      >>> dates = itertools.islice(date_generator, 3)
      >>> list(dates)
      [datetime.datetime(2009, 6, 15, 1, 32, 37, 286765), datetime.datetime(2009, 6, 14, 1, 32, 37, 286836), datetime.datetime(2009, 6, 13, 1, 32, 37, 286859)]
      

      【讨论】:

      • 如果天数是任意的,Generators 是执行此操作的理想方式。
      【解决方案5】:

      是的,重新发明轮子...... 只要搜索论坛,你会得到这样的:

      from dateutil import rrule
      from datetime import datetime
      
      list(rrule.rrule(rrule.DAILY,count=100,dtstart=datetime.now()))
      

      【讨论】:

      • 需要labix.org/python-dateutil。看起来不错,但不值得仅仅为了节省一行而使用外部依赖。
      • 无法相信任何其他答案都非常pythonic的东西。虽然 rrule 是一个糟糕的名字,但这个名字至少看起来很容易。
      • @BeniCherniavsky-Paskin:在实现周期(日历)算法时,很容易引入细微的错误。 dateutil.rrule 实现 iCalendar RFC - 使用具有明确定义的行为的函数比使用几乎相同功能的多个实现更容易,这些实现略有不同。 dateutil.rrule 允许将错误修复限制在一个地方。
      • @Mark0978: rrule 名称不是任意的;来自the corresponding rfc
      • @BeniCherniavsky-Paskin gist.github.com/Asday/be44c79fa5ead8461e8da8da2b93c30e 你的错误。该日期不存在。
      【解决方案6】:

      您还可以使用日期序号使其更简单:

      def date_range(start_date, end_date):
          for ordinal in range(start_date.toordinal(), end_date.toordinal()):
              yield datetime.date.fromordinal(ordinal)
      

      或者按照 cmets 中的建议,您可以创建如下列表:

      date_range = [
          datetime.date.fromordinal(ordinal) 
          for ordinal in range(
              start_date.toordinal(),
              end_date.toordinal(),
          )
      ]
      

      【讨论】:

        【解决方案7】:

        从这个问题的标题中,我期待找到类似range() 的内容,这可以让我指定两个日期并创建一个包含其间所有日期的列表。这样一来,如果事先不知道,就不需要计算这两个日期之间的天数。

        因此,有可能会稍微偏离主题,这个单行代码可以完成这项工作:

        import datetime
        start_date = datetime.date(2011, 01, 01)
        end_date   = datetime.date(2014, 01, 01)
        
        dates_2011_2013 = [ start_date + datetime.timedelta(n) for n in range(int ((end_date - start_date).days))]
        

        感谢this answer

        【讨论】:

        • 这与该问题的许多其他答案不同。
        • 我从来没有假装这比其他答案更单行,也不是更好的解决方案。我展示了一段代码,它的功能与您所要求的略有不同,但考虑到问题的标题,我会很高兴在这里找到。
        【解决方案8】:

        这是一个基于 S.Lott 的答案略有不同的答案,它提供了两个日期 startend 之间的日期列表。在下面的示例中,从 2017 年初到今天。

        start = datetime.datetime(2017,1,1)
        end = datetime.datetime.today()
        daterange = [start + datetime.timedelta(days=x) for x in range(0, (end-start).days)]
        

        【讨论】:

          【解决方案9】:

          如果有两个日期并且你需要范围试试

          from dateutil import rrule, parser
          date1 = '1995-01-01'
          date2 = '1995-02-28'
          datesx = list(rrule.rrule(rrule.DAILY, dtstart=parser.parse(date1), until=parser.parse(date2)))
          

          【讨论】:

            【解决方案10】:

            根据我为自己写的答案:

            import datetime;
            print [(datetime.date.today() - datetime.timedelta(days=x)).strftime('%Y-%m-%d') for x in range(-5, 0)]
            

            输出:

            ['2017-12-11', '2017-12-10', '2017-12-09', '2017-12-08', '2017-12-07']
            

            不同之处在于我得到的是“date”对象,而不是“datetime.datetime”对象。

            【讨论】:

              【解决方案11】:

              我知道答案有点晚,但我只是遇到了同样的问题,并认为 Python 的内部范围函数在这方面有点缺乏,所以我在我的 util 模块中覆盖了它。

              from __builtin__ import range as _range
              from datetime import datetime, timedelta
              
              def range(*args):
                  if len(args) != 3:
                      return _range(*args)
                  start, stop, step = args
                  if start < stop:
                      cmp = lambda a, b: a < b
                      inc = lambda a: a + step
                  else:
                      cmp = lambda a, b: a > b
                      inc = lambda a: a - step
                  output = [start]
                  while cmp(start, stop):
                      start = inc(start)
                      output.append(start)
              
                  return output
              
              print range(datetime(2011, 5, 1), datetime(2011, 10, 1), timedelta(days=30))
              

              【讨论】:

              • 我想你想要output = [] 并交换while cmp(...) 循环中的行。比较range(0,10,1)_range(0,10,1)
              • 我认为如果你将函数命名为date_range,这个答案会更好。
              【解决方案12】:

              这是我根据自己的代码创建的要点,这可能会有所帮助。 (我知道问题太老了,但其他人可以使用它)

              https://gist.github.com/2287345

              (下同)

              import datetime
              from time import mktime
              
              def convert_date_to_datetime(date_object):
                  date_tuple = date_object.timetuple()
                  date_timestamp = mktime(date_tuple)
                  return datetime.datetime.fromtimestamp(date_timestamp)
              
              def date_range(how_many=7):
                  for x in range(0, how_many):
                      some_date = datetime.datetime.today() - datetime.timedelta(days=x)
                      some_datetime = convert_date_to_datetime(some_date.date())
                      yield some_datetime
              
              def pick_two_dates(how_many=7):
                  a = b = convert_date_to_datetime(datetime.datetime.now().date())
                  for each_date in date_range(how_many):
                      b = a
                      a = each_date
                      if a == b:
                          continue
                      yield b, a
              

              【讨论】:

                【解决方案13】:

                这是 bash 脚本获取工作日列表的一个衬里,这是 python 3。很容易修改,最后的 int 是你想要的过去的天数。

                python -c "import sys,datetime; print('\n'.join([(datetime.datetime.today() - datetime.timedelta(days=x)).strftime(\"%Y/%m/%d\") for x in range(0,int(sys.argv[1])) if (datetime.datetime.today() - datetime.timedelta(days=x)).isoweekday()<6]))" 10
                

                这是一个提供开始(或更确切地说是结束)日期的变体

                python -c "import sys,datetime; print('\n'.join([(datetime.datetime.strptime(sys.argv[1],\"%Y/%m/%d\") - datetime.timedelta(days=x)).strftime(\"%Y/%m/%d \") for x in range(0,int(sys.argv[2])) if (datetime.datetime.today() - datetime.timedelta(days=x)).isoweekday()<6]))" 2015/12/30 10
                

                这是任意开始和结束日期的变体。并不是说这不是非常有效,而是在 bash 脚本中放入 for 循环很有用:

                python -c "import sys,datetime; print('\n'.join([(datetime.datetime.strptime(sys.argv[1],\"%Y/%m/%d\") + datetime.timedelta(days=x)).strftime(\"%Y/%m/%d\") for x in range(0,int((datetime.datetime.strptime(sys.argv[2], \"%Y/%m/%d\") - datetime.datetime.strptime(sys.argv[1], \"%Y/%m/%d\")).days)) if (datetime.datetime.strptime(sys.argv[1], \"%Y/%m/%d\") + datetime.timedelta(days=x)).isoweekday()<6]))" 2015/12/15 2015/12/30
                

                【讨论】:

                • 可能想用您评论中的代码更新您的回答帖子。 Python 在 cmets 中被破坏了,有点需要格式化。
                【解决方案14】:

                Matplotlib 相关

                from matplotlib.dates import drange
                import datetime
                
                base = datetime.date.today()
                end  = base + datetime.timedelta(days=100)
                delta = datetime.timedelta(days=1)
                l = drange(base, end, delta)
                

                【讨论】:

                  【解决方案15】:

                  我知道这已经被回答了,但出于历史目的,我会写下我的答案,因为我认为它是直截了当的。

                  import numpy as np
                  import datetime as dt
                  listOfDates=[date for date in np.arange(firstDate,lastDate,dt.timedelta(days=x))]
                  

                  当然它不会赢得像代码高尔夫这样的东西,但我认为它很优雅。

                  【讨论】:

                  • 带有步骤的arange 相当不错,但listOfDatesnumpy datetime64 组成,而不是python 原生日期时间。
                  • 但是您可以使用np.arange(…).astype(dt.datetime) 使arange 返回本机python datetime 而不是numpy datetime64。
                  【解决方案16】:

                  另一个向前或向后计数的例子,从 Sandeep 的回答开始。

                  from datetime import date, datetime, timedelta
                  from typing import Sequence
                  def range_of_dates(start_of_range: date, end_of_range: date) -> Sequence[date]:
                  
                      if start_of_range <= end_of_range:
                          return [
                              start_of_range + timedelta(days=x)
                              for x in range(0, (end_of_range - start_of_range).days + 1)
                          ]
                      return [
                          start_of_range - timedelta(days=x)
                          for x in range(0, (start_of_range - end_of_range).days + 1)
                      ]
                  
                  start_of_range = datetime.today().date()
                  end_of_range = start_of_range + timedelta(days=3)
                  date_range = range_of_dates(start_of_range, end_of_range)
                  print(date_range)
                  

                  给予

                  [datetime.date(2019, 12, 20), datetime.date(2019, 12, 21), datetime.date(2019, 12, 22), datetime.date(2019, 12, 23)]
                  

                  start_of_range = datetime.today().date()
                  end_of_range = start_of_range - timedelta(days=3)
                  date_range = range_of_dates(start_of_range, end_of_range)
                  print(date_range)
                  

                  给予

                  [datetime.date(2019, 12, 20), datetime.date(2019, 12, 19), datetime.date(2019, 12, 18), datetime.date(2019, 12, 17)]
                  

                  请注意,开始日期包含在返回中,因此如果您想要四个日期,请使用timedelta(days=3)

                  【讨论】:

                    【解决方案17】:

                    允许在参数化窗口大小(天、分钟、小时、秒)上创建日期范围的通用方法:

                    from datetime import datetime, timedelta
                    
                    def create_date_ranges(start, end, **interval):
                        start_ = start
                        while start_ < end:
                            end_ = start_ + timedelta(**interval)
                            yield (start_, min(end_, end))
                            start_ = end_
                    

                    测试:

                    def main():
                        tests = [
                            ('2021-11-15:00:00:00', '2021-11-17:13:00:00', {'days': 1}),
                            ('2021-11-15:00:00:00', '2021-11-16:13:00:00', {'hours': 12}),
                            ('2021-11-15:00:00:00', '2021-11-15:01:45:00', {'minutes': 30}),
                            ('2021-11-15:00:00:00', '2021-11-15:00:01:12', {'seconds': 30})
                        ]
                        for t in tests:
                            print("\nInterval: %s, range(%s to %s)" % (t[2], t[0], t[1]))
                            start = datetime.strptime(t[0], '%Y-%m-%d:%H:%M:%S')
                            end =  datetime.strptime(t[1], '%Y-%m-%d:%H:%M:%S')
                            ranges = list(create_date_ranges(start, end, **t[2]))        
                            x = list(map(
                                lambda x: (x[0].strftime('%Y-%m-%d:%H:%M:%S'), x[1].strftime('%Y-%m-%d:%H:%M:%S')),
                                ranges
                            ))
                            print(x)
                    main()
                    

                    测试输出:

                    Interval: {'days': 1}, range(2021-11-15:00:00:00 to 2021-11-17:13:00:00)
                    [('2021-11-15:00:00:00', '2021-11-16:00:00:00'), ('2021-11-16:00:00:00', '2021-11-17:00:00:00'), ('2021-11-17:00:00:00', '2021-11-17:13:00:00')]
                    
                    Interval: {'hours': 12}, range(2021-11-15:00:00:00 to 2021-11-16:13:00:00)
                    [('2021-11-15:00:00:00', '2021-11-15:12:00:00'), ('2021-11-15:12:00:00', '2021-11-16:00:00:00'), ('2021-11-16:00:00:00', '2021-11-16:12:00:00'), ('2021-11-16:12:00:00', '2021-11-16:13:00:00')]
                    
                    Interval: {'minutes': 30}, range(2021-11-15:00:00:00 to 2021-11-15:01:45:00)
                    [('2021-11-15:00:00:00', '2021-11-15:00:30:00'), ('2021-11-15:00:30:00', '2021-11-15:01:00:00'), ('2021-11-15:01:00:00', '2021-11-15:01:30:00'), ('2021-11-15:01:30:00', '2021-11-15:01:45:00')]
                    
                    Interval: {'seconds': 30}, range(2021-11-15:00:00:00 to 2021-11-15:00:01:12)
                    [('2021-11-15:00:00:00', '2021-11-15:00:00:30'), ('2021-11-15:00:00:30', '2021-11-15:00:01:00'), ('2021-11-15:00:01:00', '2021-11-15:00:01:12')]
                    

                    【讨论】:

                      【解决方案18】:

                      带有datetimedateutil 的每月日期范围生成器。简单易懂:

                      import datetime as dt
                      from dateutil.relativedelta import relativedelta
                      
                      def month_range(start_date, n_months):
                              for m in range(n_months):
                                  yield start_date + relativedelta(months=+m)
                      

                      【讨论】:

                        【解决方案19】:
                        import datetime    
                        def date_generator():
                            cur = base = datetime.date.today()
                            end  = base + datetime.timedelta(days=100)
                            delta = datetime.timedelta(days=1)
                            while(end>base):
                                base = base+delta
                                print base
                        
                        date_generator()
                        

                        【讨论】:

                        • 他想后退,而不是前进。所以end 应该是base - datetime.timedelta。此外...为什么这个解决方案比原来的解决方案更好?
                        【解决方案20】:
                        from datetime import datetime, timedelta
                        from dateutil import parser
                        def getDateRange(begin, end):
                            """  """
                            beginDate = parser.parse(begin)
                            endDate =  parser.parse(end)
                            delta = endDate-beginDate
                            numdays = delta.days + 1
                            dayList = [datetime.strftime(beginDate + timedelta(days=x), '%Y%m%d') for x in range(0, numdays)]
                            return dayList
                        

                        【讨论】:

                          【解决方案21】:

                          根据上面的答案,我为日期生成器创建了这个示例

                          import datetime
                          date = datetime.datetime.now()
                          time = date.time()
                          def date_generator(date, delta):
                            counter =0
                            date = date - datetime.timedelta(days=delta)
                            while counter <= delta:
                              yield date
                              date = date + datetime.timedelta(days=1)
                              counter +=1
                          
                          for date in date_generator(date, 30):
                             if date.date() != datetime.datetime.now().date():
                               start_date = datetime.datetime.combine(date, datetime.time())
                               end_date = datetime.datetime.combine(date, datetime.time.max)
                             else:
                               start_date = datetime.datetime.combine(date, datetime.time())
                               end_date = datetime.datetime.combine(date, time)
                             print('start_date---->',start_date,'end_date---->',end_date)
                          

                          【讨论】:

                            【解决方案22】:

                            我想我会用一个简单(但不完整)的日期范围实现来投入我的两分钱:

                            from datetime import date, timedelta, datetime
                            
                            class DateRange:
                                def __init__(self, start, end, step=timedelta(1)):
                                    self.start = start
                                    self.end = end
                                    self.step = step
                            
                                def __iter__(self):
                                    start = self.start
                                    step = self.step
                                    end = self.end
                            
                                    n = int((end - start) / step)
                                    d = start
                            
                                    for _ in range(n):
                                        yield d
                                        d += step
                            
                                def __contains__(self, value):
                                    return (
                                        (self.start <= value < self.end) and 
                                        ((value - self.start) % self.step == timedelta(0))
                                    )
                            

                            【讨论】:

                              猜你喜欢
                              • 1970-01-01
                              • 1970-01-01
                              • 2011-08-31
                              • 2020-03-28
                              • 1970-01-01
                              相关资源
                              最近更新 更多