【问题标题】:Loop through and create a list of dates taking into account leap years循环并创建考虑闰年的日期列表
【发布时间】:2020-06-11 02:51:50
【问题描述】:

我想创建一个日期列表,在某个年份范围内每年选择相同的日期。

start_date = dt.date(2009, 2, 10)
end_date = dt.date(2019, 5, 1)
new_date = []

current_date = start_date
while current_date < end_date:
    if (current_date.year % 4) == 0:
        new_date.append(current_date + dt.timedelta(days=366))
    else: 
        new_date.append(current_date + dt.timedelta(days=365))
    if (current_date.year % 4) == 0:
        current_date += dt.timedelta(days=366)     
    else:
        current_date += dt.timedelta(days=365) 
new_date

这个输出:

[datetime.date(2010, 2, 10),
 datetime.date(2011, 2, 10),
 datetime.date(2012, 2, 10),
 datetime.date(2013, 2, 10),
 datetime.date(2014, 2, 10),
 datetime.date(2015, 2, 10),
 datetime.date(2016, 2, 10),
 datetime.date(2017, 2, 10),
 datetime.date(2018, 2, 10),
 datetime.date(2019, 2, 10),
 datetime.date(2020, 2, 10)]

但当我将日期更改为 2 月 29 日之后,闰年的日期会偏移一天。

start_date = dt.date(2009, 3, 10)
end_date = dt.date(2019, 5, 1)
new_date = []

current_date = start_date
while current_date < end_date:
    if (current_date.year % 4) == 0:
        new_date.append(current_date + dt.timedelta(days=366))
    else: 
        new_date.append(current_date + dt.timedelta(days=365))
    if (current_date.year % 4) == 0:
        current_date += dt.timedelta(days=366)     
    else:
        current_date += dt.timedelta(days=365) 
new_date

[datetime.date(2010, 3, 10),
 datetime.date(2011, 3, 10),
 datetime.date(2012, 3, 9),
 datetime.date(2013, 3, 10),
 datetime.date(2014, 3, 10),
 datetime.date(2015, 3, 10),
 datetime.date(2016, 3, 9),
 datetime.date(2017, 3, 10),
 datetime.date(2018, 3, 10),
 datetime.date(2019, 3, 10),
 datetime.date(2020, 3, 9)]

这是什么原因,我该如何解决?

【问题讨论】:

  • 为什么是时间增量?如果您只想要每年的特定日期,为什么不专门创建呢?
  • 不知道如何实现,这就是我尝试使用 timedelta 的原因。
  • 除了之前的 cmets,我建议使用 date.replace(year = date.year + 1) 之类的东西,而不是手动执行此操作(例如,请参阅 this question
  • @EliTurasky dt.date(current_date.year + 1, start_date.month, start_date.day)
  • 哦,我明白了。感谢这些 cmets。

标签: python datetime


【解决方案1】:

如果我理解正确,您可以像这样实现您所需要的:

from datetime import date

start_date, end_date = date(2009, 3, 10), date(2019, 5, 1)

new_date = []
for y in range(start_date.year+1, end_date.year+2): # +1 and +2 is arbitrary, adjust as needed
    new_date.append(date(y, start_date.month, start_date.day))

new_date
# [datetime.date(2010, 3, 10),
#  datetime.date(2011, 3, 10),
#  datetime.date(2012, 3, 10),
#  datetime.date(2013, 3, 10),
#  datetime.date(2014, 3, 10),
#  datetime.date(2015, 3, 10),
#  datetime.date(2016, 3, 10),
#  datetime.date(2017, 3, 10),
#  datetime.date(2018, 3, 10),
#  datetime.date(2019, 3, 10),
#  datetime.date(2020, 3, 10)]

或作为列表理解

d0, d1 = date(2009, 3, 10), date(2019, 5, 1)
new_date = [date(y, d0.month, d0.day) for y in range(d0.year+1, d1.year+2)]

【讨论】:

    【解决方案2】:

    如果您选择闰日,这将没有意义,所以我假设您没有。

    import datetime
    
    def date_range(start, end):
        ret = []
        while start < end:
            ret.append(start)
            start = start.replace(year=start.year + 1)
        return ret
    

    那么你的例子给出了

    >>> start_date = datetime.date(2009, 2, 10)
    >>> end_date = datetime.date(2019, 5, 1)
    >>> date_range(start_date, end_date)
    [datetime.date(2009, 2, 10),
     datetime.date(2010, 2, 10),
     datetime.date(2011, 2, 10),
     datetime.date(2012, 2, 10),
     datetime.date(2013, 2, 10),
     datetime.date(2014, 2, 10),
     datetime.date(2015, 2, 10),
     datetime.date(2016, 2, 10),
     datetime.date(2017, 2, 10),
     datetime.date(2018, 2, 10),
     datetime.date(2019, 2, 10)]
    

    与仅基于年份的生成器不同,此答案也可以正确处理月份。

    【讨论】:

      【解决方案3】:

      保留你的代码(虽然它可以更有效地完成),你只需要检查下一个年是否是闰年:

      import calendar
      import pprint
      import datetime as dt
      
      def increaseYear(start_date):
          end_date = dt.date(2019, 5, 1)
          new_date = []
      
          current_date = start_date
          while current_date < end_date:
              # using calendar.isleap here, there are more conditions
              # than year%4==0: https://stackoverflow.com/questions/11621740/how-to-determine-whether-a-year-is-a-leap-year
              _nextYearisLeap = calendar.isleap(current_date.year + 1)
              if _nextYearisLeap:
                  new_date.append(current_date + dt.timedelta(days=366))
              else: 
                  new_date.append(current_date + dt.timedelta(days=365))
              current_date = new_date[-1]
          return new_date
      
      
      for startDate in (dt.date(2009, 2, 10), dt.date(2009, 3, 10)):
          dates = increaseYear(startDate)
          pprint.pprint(dates)
      

      输出:

      [datetime.date(2010, 2, 10),
       datetime.date(2011, 2, 10),
       datetime.date(2012, 2, 11),
       datetime.date(2013, 2, 10),
       datetime.date(2014, 2, 10),
       datetime.date(2015, 2, 10),
       datetime.date(2016, 2, 11),
       datetime.date(2017, 2, 10),
       datetime.date(2018, 2, 10),
       datetime.date(2019, 2, 10),
       datetime.date(2020, 2, 11)]
      [datetime.date(2010, 3, 10),
       datetime.date(2011, 3, 10),
       datetime.date(2012, 3, 10),
       datetime.date(2013, 3, 10),
       datetime.date(2014, 3, 10),
       datetime.date(2015, 3, 10),
       datetime.date(2016, 3, 10),
       datetime.date(2017, 3, 10),
       datetime.date(2018, 3, 10),
       datetime.date(2019, 3, 10),
       datetime.date(2020, 3, 10)]
      

      【讨论】:

        【解决方案4】:

        您可以使用dateutil 模块来计算闰年:

        import datetime as dt
        from dateutil.relativedelta import relativedelta
        
        dt.date(2011, 3, 10) + relativedelta(years=1)
        Output:
        datetime.date(2012, 3, 10)
        
        dt.date(2020, 2, 29) + relativedelta(years=1)
        Output:
        datetime.date(2021, 2, 28)
        

        【讨论】:

          【解决方案5】:

          要了解您的解决方案出了什么问题,首先要了解,如果是闰年或非闰年,每年的计数会有所不同:

                  Leap  ~Leap
          Jan 31    31     31
             ...   ...    ...
          Feb 28    59     59
          Feb 29    60    n/a
          Mar 01    61     60
          Mar 02    62     61
          

          观察 2 月 29 日之后闰年和非闰年之间的天数如何偏移 1 天。因此,您的第一个示例类似于(假设不存在 is_leap 的便利属性):

          map(lambda x: x + 366 if x.is_leap else 365, [41, 41, 41, 41, ...])
          

          而你的第二个例子是:

          map(lambda x: x + 366 if x.is_leap else 365, [69, 69, 70, 69, ...])
          

          归结为 - 您对何时以及如何抵消闰年的处理已经错了。

          您已经有了一些可以为您提供替代方案的答案,所以我将缩短这个答案,并解释为什么您的实施工作。就我个人而言,我会像 MrFuppes 那样处理它,并通过直接添加年份来创建一个新的 datetime 对象:

          start_date = dt.date(2009, 3, 10)
          end_date = dt.date(2019, 5, 1)
          def create_new_dates(start, end):
              cur = start
              while cur < end:
                  cur = dt.date(cur.year + 1, cur.month, cur.day)
                  yield cur
          
          new_dates = list(create_new_dates(start_date, end_date))
          

          结果:

          [datetime.date(2010, 3, 10),
           datetime.date(2011, 3, 10),
           datetime.date(2012, 3, 10),
           datetime.date(2013, 3, 10),
           datetime.date(2014, 3, 10),
           datetime.date(2015, 3, 10),
           datetime.date(2016, 3, 10),
           datetime.date(2017, 3, 10),
           datetime.date(2018, 3, 10),
           datetime.date(2019, 3, 10),
           datetime.date(2020, 3, 10)]
          

          【讨论】:

            猜你喜欢
            • 2021-10-17
            • 1970-01-01
            • 2015-07-24
            • 1970-01-01
            • 1970-01-01
            • 2022-11-29
            • 2017-01-13
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多