【问题标题】:Get (year,month) for the last X months获取最近 X 个月的(年,月)
【发布时间】:2011-07-04 21:24:14
【问题描述】:

我在 python 中做了一件非常简单的事情: 从今天开始(包括),我需要最近 x 个月的元组列表 (year,month)。因此,对于 x=10 和今天(2011 年 7 月),该命令应输出:

[(2011, 7), (2011, 6), (2011, 5), (2011, 4), (2011, 3), 
(2011, 2), (2011, 1), (2010, 12), (2010, 11), (2010, 10)]

只应使用 python 的默认日期时间实现。我想出了以下解决方案:

import datetime
[(d.year, d.month) for d in [datetime.date.today()-datetime.timedelta(weeks=4*i) for i in range(0,10)]]

这个解决方案为我的测试用例输出了正确的解决方案,但我对这个解决方案并不满意:它假设一个月有四个星期,这根本不正确。我可以将weeks=4 替换为days=30,这将是一个更好的解决方案,但它仍然不正确。

我想到的另一个解决方案是使用简单的数学并从月份计数器中减去 1,如果月份计数器为 0,则从年份计数器中减去 1。这个解决方案的问题:它需要更多的代码,而且可读性也不是很好。

那么如何才能正确地做到这一点呢?

【问题讨论】:

  • 仅供参考,如果您在 7 月 31 日运行此代码,weeks=4days=30 都会失败。
  • 谢谢,这是我的代码不起作用的最简单的边界情况。如果你使用days=31,你可以让它为那些人工作,但它仍然不正确。

标签: python datetime


【解决方案1】:

我没有看到它在任何地方记录,但time.mktime 会在超出范围(包括负月份值)时“滚动”到正确的年份:

x = 10
now = time.localtime()
print([time.localtime(time.mktime((now.tm_year, now.tm_mon - n, 1, 0, 0, 0, 0, 0, 0)))[:2] for n in range(x)])

【讨论】:

  • J.F.塞巴斯蒂安:固定。
【解决方案2】:

使用relativedelta ...

import datetime
from dateutil.relativedelta import relativedelta

def get_last_months(start_date, months):
    for i in range(months):
        yield (start_date.year,start_date.month)
        start_date += relativedelta(months = -1)

>>> X = 10       
>>> [i for i in get_last_months(datetime.datetime.today(), X)]
>>> [(2013, 2), (2013, 1), (2012, 12), (2012, 11), (2012, 10), (2012, 9), (2012, 8), (2012, 7), (2012, 6), (2012, 5)]

【讨论】:

    【解决方案3】:

    最好的方法是使用 integer division (//) 和 modulus (%) 函数,用自 0 年以来的月数表示月份:

    months = year * 12 + month - 1 # Months since year 0 minus 1
    tuples = [((months - i) // 12, (months - i) % 12 + 1) for i in range(10)]
    

    months 表达式中的 - 1 需要在我们稍后将模函数的结果加 1 以获得 1 索引时获得正确答案(即月份从 1 到 12 而不是 0 到 11 )。

    或者您可能想要创建一个生成器:

    def year_month_tuples(year, month):
        months = year * 12 + month - 1 # -1 to reflect 1-indexing
        while True:
            yield (months // 12, months % 12 + 1) # +1 to reflect 1-indexing
            months -= 1 # next time we want the previous month
    

    可以用作:

    >>> tuples = year_month_tuples(2011, 7)
    >>> [tuples.next() for i in range(10)]
    

    【讨论】:

      【解决方案4】:

      更新:还是添加一个timedelta 版本,因为它看起来更漂亮:)

      def get_years_months(start_date, months):
          for i in range(months):
              yield (start_date.year, start_date.month)
              start_date -= datetime.timedelta(days=calendar.monthrange(start_date.year, start_date.month)[1])
      

      您不需要使用timedelta,因为您只需要固定的年份和月份。

      def get_years_months(my_date, num_months):
          cur_month = my_date.month
          cur_year = my_date.year
      
          result = []
          for i in range(num_months):
              if cur_month == 0:
                  cur_month = 12
                  cur_year -= 1
              result.append((cur_year, cur_month))
              cur_month -= 1
      
          return result
      
      if __name__ == "__main__":
          import datetime
          result = get_years_months(datetime.date.today(), 10)
          print result
      

      【讨论】:

        【解决方案5】:

        如果您创建一个函数来进行日期数学运算,它几乎与您的原始实现一样好:

        def next_month(this_year, this_month):
          if this_month == 0: 
            return (this_year - 1, 12)
          else:
            return (this_year, this_month - 1)
        
        this_month = datetime.date.today().month()
        this_year = datetime.date.today().year()
        for m in range(0, 10):
          yield (this_year, this_month)
          this_year, this_month = next_month(this_year, this_month)
        

        【讨论】:

          【解决方案6】:

          如果你想在没有日期时间库的情况下这样做,你可以转换为自 0 年以来的月份,然后再转换回来

          end_year = 2014
          end_month = 5
          start_year = 2013
          start_month = 7
          
          print list = [(a/12,a % 12+1) for a in range(12*end_year+end_month-1,12*start_year+start_month-2,-1)]
          

          python 3(// 而不是/):

          list = [(a//12,a % 12+1) for a in range(12*end_year+end_month-1,12*start_year+start_month-2,-1)]
          print(list)
          

          [(2014, 5), (2014, 4), (2014, 3), (2014, 2), (2014, 1), (2013, 12), (2013, 11), (2013, 10), (2013, 9), (2013, 8), (2013, 7)]

          【讨论】:

            【解决方案7】:

            或者你可以定义一个函数来获取上个月,然后打印月份( 有点简陋)

            def last_month(year_month):#format YYYY-MM
                aux = year_month.split('-')
                m = int(aux[1])
                y = int(aux[0])
            
                if m-1 == 0:
                    return str(y-1)+"-12"
                else:
                    return str(y)+"-"+str(m-1)
            
            def print_last_month(ran, year_month= str(datetime.datetime.today().year)+'-'+str(datetime.datetime.today().month)):
                i = 1 
                if ran != 10:
                    print( last_month(year_month) )
                    print_last_month(i+1, year_month= last_month(year_month))
            

            【讨论】:

              【解决方案8】:
                  def list_last_year_month(self):
                  last_day_of_prev_month = date.today()
                  number_of_years = self.number_of_years
                  time_list = collections.defaultdict(list)
                  for y in range(number_of_years+1):
                      for m in range(13):
                          last_day_of_prev_month = last_day_of_prev_month.replace(day=1) - timedelta(days=1)
                          last_month = str(last_day_of_prev_month.month)
                          last_year = str(last_day_of_prev_month.year)
                          time_list[last_year].append(last_month)
                  return time_list
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2014-04-22
                • 1970-01-01
                • 2020-03-20
                • 2014-05-12
                • 2021-06-29
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多