【问题标题】:How can I get the first day of the next month in Python?如何在 Python 中获取下个月的第一天?
【发布时间】:2019-12-12 16:49:55
【问题描述】:

如何在 Python 中获取下个月的第一个日期?例如,如果现在是 2019 年 12 月 31 日,那么下个月的第一天就是 2020 年 1 月 1 日。如果现在是 2019-08-01,那么下个月的第一天就是 2019-09-01。

我想出了这个:

import datetime

def first_day_of_next_month(dt):
    '''Get the first day of the next month. Preserves the timezone.

    Args:
        dt (datetime.datetime): The current datetime

    Returns:
        datetime.datetime: The first day of the next month at 00:00:00.
    '''
    if dt.month == 12:
        return datetime.datetime(year=dt.year+1,
                                 month=1,
                                 day=1,
                                 tzinfo=dt.tzinfo)
    else:
        return datetime.datetime(year=dt.year,
                                 month=dt.month+1,
                                 day=1,
                                 tzinfo=dt.tzinfo)


# Example usage (assuming that today is 2021-01-28):
first_day_of_next_month(datetime.datetime.now())
# Returns: datetime.datetime(2021, 2, 1, 0, 0)

正确吗?有没有更好的办法?

【问题讨论】:

  • 看起来正确。你得到错误的答案吗?
  • 另外,只有 4 行代码,所以你所拥有的几乎是最优化的解决方案,除非你有一些非常具体的要求。你可以把它减少到 1 行,但这只会降低它的可读性。

标签: python python-datetime


【解决方案1】:

试试这个,对于每个月的开始日期,将MonthEnd(1) 更改为MonthBegin(1)

import pandas as pd
from pandas.tseries.offsets import MonthBegin, MonthEnd

date_list = (pd.date_range('2021-01-01', '2022-01-31', 
              freq='MS') + MonthEnd(1)).strftime('%Y-%m-%d').tolist()
date_list

输出:

['2021-01-31',
 '2021-02-28',
 '2021-03-31',
 '2021-04-30',
 '2021-05-31',
 '2021-06-30',
 '2021-07-31',
 '2021-08-31',
 '2021-09-30',
 '2021-10-31',
 '2021-11-30',
 '2021-12-31',
 '2022-01-31']

【讨论】:

    【解决方案2】:

    提取年月,加1,用年月日=1形成新的日期:

    from datetime import date
    
    now       = date(2020,12,18)
    y,m       = divmod(now.year*12+now.month,12)
    nextMonth = date(y,m+1,1)
    
    print(now,nextMonth)
    # 2020-12-18 2021-01-01
    

    【讨论】:

      【解决方案3】:

      当我偶然发现这个问题时,我个人正在寻找一个解决方案来获得上个月的第一天和最后一天。

      但这里有一个我认为非常简单和优雅的解决方案:

      date = datetime.datetime.now().date()
      same_time_next_month = date + datetime.timedelta(days = date.day)
      first_day_of_next_month_from_date = same_time_next_month - datetime.timedelta(days = same_time_next_month.day - 1)
      

      这里我们只是简单的将目标日期的天数加上得到下个月相同时间的日期,然后去掉新得到的日期后经过的天数。

      【讨论】:

        【解决方案4】:

        使用 Zope 的 DateTime 库,一个非常简单的解决方案是可能的

        from DateTime.DateTime import DateTime
        
        date = DateTime()  # today
        
        while date.day() != 1:
            date += 1
        
        print(date)
        

        【讨论】:

          【解决方案5】:

          仅使用 python 标准库:

          import datetime
          today = datetime.date.today()
          first_of_next_month = return date.replace(
              day=1,
              month=date.month % 12 + 1,
              year=date.year + (date.month // 12)
          )
          

          可以推广到...

          def get_first_of_month(date, month_offset=0):
              # zero based indexing of month to make math work
              month_count = date.month - 1 + month_offset
              return date.replace(
                  day=1, month=month_count % 12 + 1, year=date.year + (month_count // 12)
              )
          
          first_of_next_month = get_first_of_month(today, 1)
          

          其他不需要 3rd 方库的解决方案包括:

          • Toby Petty's answer 是另一个不错的选择。
          • 如果确切的时间增量对您有帮助, 对Adam.Er8's answer 稍作修改可能会很方便:
            import calendar, datetime
            
            today = datetime.date.today()
            time_until_next_month = datetime.timedelta(
                calendar.monthrange(today.year, today.month)[1] - today.day + 1
            )
            first_of_next_month = today + time_until_next_month
            

          【讨论】:

          • datetime.today().date() 不是datetime.date.today()
          【解决方案6】:

          这是一个仅使用标准 datetime 库的单行解决方案:

          (dt.replace(day=1) + datetime.timedelta(days=32)).replace(day=1)
          

          例子:

          >>> dt = datetime.datetime(2016, 2, 29)
          >>> print((dt.replace(day=1) + datetime.timedelta(days=32)).replace(day=1))
          2016-03-01 00:00:00
          
          >>> dt = datetime.datetime(2019, 12, 31)
          >>> print((dt.replace(day=1) + datetime.timedelta(days=32)).replace(day=1))
          2020-01-01 00:00:00
          
          >>> dt = datetime.datetime(2019, 12, 1)
          >>> print((dt.replace(day=1) + datetime.timedelta(days=32)).replace(day=1))
          2020-01-01 00:00:00
          

          【讨论】:

          • 值得澄清的是,您需要在移动到该月的第一天之后添加 32。不错的解决方案:)
          • 非常聪明!无需知道任何特定月份的确切天数,只需选择一个肯定大于任何一个月但小于任何 2 个月的总和的数字! (所以 31-58 之间的每个数字都可以,对吧..?)
          【解决方案7】:

          使用dateutil 你可以做到最真实:

          import datetime
          from dateutil import relativedelta
          today = datetime.date.today()
          
          next_month = today + relativedelta.relativedelta(months=1, day=1)
          

          在英语中:在今天的日期上加上 1 个月,并将日期(月份)设置为 1。注意 day(s)month(s) 的单复数形式的用法。单数将属性设置为一个值,复数添加句点数。

          您可以将此relativedelta.relativedelta 对象存储到一个变量中并传递它。其他答案涉及更多的编程逻辑。

          编辑您也可以使用标准的 datetime 库来实现,但它不是那么漂亮:

          next_month = (today.replace(day=1) + datetime.timedelta(days=32)).replace(day=1)
          

          将日期设置为当月的 1 日,加上 32 天(或 31 到 59 之间的任何数字,保证跳转到下个月),然后将日期设置为该月的 1 日。

          【讨论】:

            【解决方案8】:

            您可以使用calendar 获取给定月份的天数,然后添加timedelta(days=...),如下所示:

            from datetime import date, timedelta
            from calendar import monthrange
            
            days_in_month = lambda dt: monthrange(dt.year, dt.month)[1]
            today = date.today()
            first_day = today.replace(day=1) + timedelta(days_in_month(today))
            print(first_day)
            

            如果你对外部部门没问题,你可以使用dateutil(我喜欢...)

            from datetime import date
            from dateutil.relativedelta import relativedelta
            
            today = date.today()
            first_day = today.replace(day=1) + relativedelta(months=1)
            print(first_day)
            

            【讨论】:

            • 这会自动照顾年吗?例如如果现在是 12 月,+ relativedelta(months=1) 会延续到明年吗?
            • @Flux 是的,当然 :) 还可以查看我对没有外部库的解决方案的编辑
            • 关于您使用 calendar 的解决方案:如果是闰年,今天是 1 月 31 日,您的代码不会跳过 2 月并因此产生 3 月 1 日吗?
            • @Flux 不,它确切地知道特定年份的特定月份有多少天(闰和一切)。您可以运行它并查看:ideone.com/9HiP5S
            【解决方案9】:

            你的方法看起来不错,但我会这样做:

            import datetime
            from dateutil import relativedelta
            
            dt = datetime.datetime(year=1998,
                                         month=12,
                                         day=12)
            
            nextmonth = dt + relativedelta.relativedelta(months=1)
            nextmonth.replace(day=1)
            print(nextmonth)
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2012-11-05
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多