Pythonで日付から月を減算する最も簡単な方法は何ですか?


99

timedeltaのみがコンストラクタで月の引数を持っていた場合。これを行う最も簡単な方法は何ですか?

編集:以下で指摘されているように、私はこれについてあまり考えていませんでした。本当に私が欲しかったのは、先月のいつの日かでした。結局、年と月だけをつかむことになるからです。したがって、datetimeオブジェクトが与えられた場合、前月に該当するdatetimeオブジェクトを返す最も簡単な方法は何ですか?

回答:


62

これを試して:

def monthdelta(date, delta):
    m, y = (date.month+delta) % 12, date.year + ((date.month)+delta-1) // 12
    if not m: m = 12
    d = min(date.day, [31,
        29 if y%4==0 and not y%400==0 else 28,31,30,31,30,31,31,30,31,30,31][m-1])
    return date.replace(day=d,month=m, year=y)

>>> for m in range(-12, 12):
    print(monthdelta(datetime.now(), m))


2009-08-06 16:12:27.823000
2009-09-06 16:12:27.855000
2009-10-06 16:12:27.870000
2009-11-06 16:12:27.870000
2009-12-06 16:12:27.870000
2010-01-06 16:12:27.870000
2010-02-06 16:12:27.870000
2010-03-06 16:12:27.886000
2010-04-06 16:12:27.886000
2010-05-06 16:12:27.886000
2010-06-06 16:12:27.886000
2010-07-06 16:12:27.886000
2010-08-06 16:12:27.901000
2010-09-06 16:12:27.901000
2010-10-06 16:12:27.901000
2010-11-06 16:12:27.901000
2010-12-06 16:12:27.901000
2011-01-06 16:12:27.917000
2011-02-06 16:12:27.917000
2011-03-06 16:12:27.917000
2011-04-06 16:12:27.917000
2011-05-06 16:12:27.917000
2011-06-06 16:12:27.933000
2011-07-06 16:12:27.933000
>>> monthdelta(datetime(2010,3,30), -1)
datetime.datetime(2010, 2, 28, 0, 0)
>>> monthdelta(datetime(2008,3,30), -1)
datetime.datetime(2008, 2, 29, 0, 0)

編集にも一日に処理するために修正しました。

編集についてのより簡単な計算を指摘するパズルからの回答も参照してくださいd

d = min(date.day, calendar.monthrange(y, m)[1])

私にそれが適切に((date.month+delta-1) % 12)+1機能し、if not m: m = 12缶が取り除かれることを意味する場合
Charles L.

@ dan-klassonについて詳しく教えてください。
ジャン=フランソワ・ファーブル

@Jean-FrançoisFabre最初のものは単純ではありません。また、これはdatetimeとtimedeltaで簡単に行うことができます。
dan-klasson

はい、OPが編集された後、彼らが望むことは、dt - timedelta(days=dt.day)元々のように、任意の月数を減算できるようにしたかったように聞こえたように聞こえただけで実行できることが明らかになり、それは少し難しいです。
ダンカン

183

サードパーティのdateutilモジュールを使用できます(PyPIエントリはこちら)。

import datetime
import dateutil.relativedelta

d = datetime.datetime.strptime("2013-03-31", "%Y-%m-%d")
d2 = d - dateutil.relativedelta.relativedelta(months=1)
print d2

出力:

2013-02-28 00:00:00

私はparam monthmonthsdateutilで混同しています。パラメータによる減算はmonth機能しませんがmonths機能します In [23]: created_datetime__lt - relativedelta(months=1) Out[23]: datetime.datetime(2016, 11, 29, 0, 0, tzinfo=<StaticTzInfo 'Etc/GMT-8'>) In [24]: created_datetime__lt - relativedelta(month=1) Out[24]: datetime.datetime(2016, 1, 29, 0, 0, tzinfo=<StaticTzInfo 'Etc/GMT-8'>)
Simin Jie

1
@SiminJie:使用monthsすると1か月の差が出ると思います。を使用monthすると、入力の月に関係なく、月が1(1月など)に設定されます。
Frank Meulenaar 2017年

70

元の質問を「前月の任意の日時オブジェクト」に編集した後は、月の最初の日から1日を引くことで簡単に行うことができます。

from datetime import datetime, timedelta

def a_day_in_previous_month(dt):
   return dt.replace(day=1) - timedelta(days=1)

4
あなたが使用することができますdt.replace(day=1) - timedelta(1)
jfs 2014年

3
またはdt - timedelta(days=dt.day)
SergiyKolesnikov

23

Duncanの回答のバリエーション(コメントする十分な評判がありません)。これは、calendar.monthrangeを使用して、月の最終日の計算を大幅に簡略化します。

import calendar
def monthdelta(date, delta):
    m, y = (date.month+delta) % 12, date.year + ((date.month)+delta-1) // 12
    if not m: m = 12
    d = min(date.day, calendar.monthrange(y, m)[1])
    return date.replace(day=d,month=m, year=y)

Pythonで「月末日を取得」からの月範囲に関する情報


22

ベクトル化されたパンダソリューションは非常にシンプルです。

df['date'] - pd.DateOffset(months=1)


ありがとうございました!それは私が私の日付のセットに/月を簡単に追加/減算するのに役立ちました!
Mateus da Silva Teixeira

使えればいいですねpandas。ありがとう!
igorkf

17

timedeltaのみがコンストラクタで月の引数を持っていた場合。これを行う最も簡単な方法は何ですか?

たとえば、3月30日の日付から1か月差し引いた結果をどうしますか?これは、月を加算または減算する場合の問題です。月の長さが異なります。一部のアプリケーションでは、そのような場合に例外が適切であり、他の場合には、「前月の最終日」を使用しても問題ありません(ただし、月を減算してから月を追加することは全体的に無操作ではありません!) 、他の場合では、日付に加えて、事実についての何らかの指示を保持する必要があります。たとえば、「2月28日と言っていますが、2月30日が存在する場合は本当に必要です」ので、それは物事を再び正しく設定できます(そして後者は明らかにデータとその他のものを保持するカスタムクラスを必要とします)。

すべてのアプリケーションに耐えられる実際のソリューションはあり得ません。また、この惨めな操作のセマンティクスに対する特定のアプリのニーズを私たちに教えていないため、ここで提供できるヘルプはそれほど多くありません。


あなたは絶対的に正しいです。そのための議論があるtimedeltaについて冗談を言っていました。同じロジックが数時間または数分間引数を持たない場合にも当てはまるのでしょうか。技術的には、1日は正確に24時間ではありません。
Bialecki

PHP は、 2月28日に2日を追加することでそれを処理し、3月2日になります。これには慣例があります。すぐにわかります
NullUserException

外貨両替の日付を計算している場合はさらに面白くなります。たとえば、日付を3〜6か月先に見つける必要があるかもしれませんが、両方の国で営業日である必要があります(イスラム教の国では金曜日ではありません)。どちらの国でも祝日ではありません。
Duncanは

@Bialecki:datetimeモジュールは、1日が正確に24時間(うるう秒なし)であると想定しています
jfs

8

簡単な方法は、PandasのDateOffsetを次のように使用することだと思います。

import pandas as pd
date_1 = pd.to_datetime("2013-03-31", format="%Y-%m-%d") - pd.DateOffset(months=1)

結果は日時オブジェクトになります


2
これが最も直感的なソリューションです
Kots

5

必要なのが前月の任意の日であれば、最も簡単なことは、現在の日付から日数を引くことです。これにより、前月の最終日が得られます。

たとえば、任意の日付で始まる:

>>> import datetime                                                                                                                                                                 
>>> today = datetime.date.today()                                                                                                                                                   
>>> today
datetime.date(2016, 5, 24)

取得する現在の日付の日数を引く:

>>> last_day_previous_month = today - datetime.timedelta(days=today.day)
>>> last_day_previous_month
datetime.date(2016, 4, 30)

これは、先月の任意の日の単純化されたニーズには十分です。

しかし、それができたので、最初の同じ日を含め、その月の任意の日を取得することもできます(つまり、月を差し引くのとほぼ同じです)。

>>> same_day_last_month = last_day_previous_month.replace(day=today.day)
>>> same_day_last_month
datetime.date(2016, 4, 24)

もちろん、30日の月の31日に注意する必要があります。

>>> a_date = datetime.date(2016, 3, 31)                                                                                                                                             
>>> last_day_previous_month = a_date - datetime.timedelta(days=a_date.day)
>>> a_date_minus_month = (
...     last_day_previous_month.replace(day=a_date.day)
...     if a_date.day < last_day_previous_month.day
...     else last_day_previous_month
... )
>>> a_date_minus_month
datetime.date(2016, 2, 29)

1

これは、Q4が10月1日から始まる政府会計年度に使用します。注日付を四半期に変換し、それを元に戻します。

import pandas as pd

df['Date'] = '1/1/2020'
df['Date'] = pd.to_datetime(df['Date'])              #returns 2020-01-01
df['NewDate'] = df.Date - pd.DateOffset(months=3)    #returns 2019-10-01 <---- answer

# For fun, change it to FY Quarter '2019Q4'
df['NewDate'] = df['NewDate'].dt.year.astype(str) + 'Q' + df['NewDate'].dt.quarter.astype(str)

# Convert '2019Q4' back to 2019-10-01
df['NewDate'] = pd.to_datetime(df.NewDate)

0

これを行うためのコードを次に示します。自分で試したことがありません...

def add_one_month(t):
    """Return a `datetime.date` or `datetime.datetime` (as given) that is
    one month earlier.

    Note that the resultant day of the month might change if the following
    month has fewer days:

        >>> add_one_month(datetime.date(2010, 1, 31))
        datetime.date(2010, 2, 28)
    """
    import datetime
    one_day = datetime.timedelta(days=1)
    one_month_later = t + one_day
    while one_month_later.month == t.month:  # advance to start of next month
        one_month_later += one_day
    target_month = one_month_later.month
    while one_month_later.day < t.day:  # advance to appropriate day
        one_month_later += one_day
        if one_month_later.month != target_month:  # gone too far
            one_month_later -= one_day
            break
    return one_month_later

def subtract_one_month(t):
    """Return a `datetime.date` or `datetime.datetime` (as given) that is
    one month later.

    Note that the resultant day of the month might change if the following
    month has fewer days:

        >>> subtract_one_month(datetime.date(2010, 3, 31))
        datetime.date(2010, 2, 28)
    """
    import datetime
    one_day = datetime.timedelta(days=1)
    one_month_earlier = t - one_day
    while one_month_earlier.month == t.month or one_month_earlier.day > t.day:
        one_month_earlier -= one_day
    return one_month_earlier

0

(年、月)のタプルがあり、月が1〜12の場合、これを試してください。

>>> from datetime import datetime
>>> today = datetime.today()
>>> today
datetime.datetime(2010, 8, 6, 10, 15, 21, 310000)
>>> thismonth = today.year, today.month
>>> thismonth
(2010, 8)
>>> lastmonth = lambda (yr,mo): [(y,m+1) for y,m in (divmod((yr*12+mo-2), 12),)][0]
>>> lastmonth(thismonth)
(2010, 7)
>>> lastmonth( (2010,1) )
(2009, 12)

毎年12か月あると仮定します。


0
def month_sub(year, month, sub_month):
    result_month = 0
    result_year = 0
    if month > (sub_month % 12):
        result_month = month - (sub_month % 12)
        result_year = year - (sub_month / 12)
    else:
        result_month = 12 - (sub_month % 12) + month
        result_year = year - (sub_month / 12 + 1)
    return (result_year, result_month)

>>> month_sub(2015, 7, 1)    
(2015, 6)
>>> month_sub(2015, 7, -1)
(2015, 8)
>>> month_sub(2015, 7, 13)
(2014, 6)
>>> month_sub(2015, 7, -14)
(2016, 9)

0

次のコードを使用して、特定の日付からnか月前に戻りました。

your_date =  datetime.strptime(input_date, "%Y-%m-%d")  #to convert date(2016-01-01) to timestamp
start_date=your_date    #start from current date

#Calculate Month
for i in range(0,n):    #n = number of months you need to go back
    start_date=start_date.replace(day=1)    #1st day of current month
    start_date=start_date-timedelta(days=1) #last day of previous month

#Calculate Day
if(start_date.day>your_date.day):   
    start_date=start_date.replace(day=your_date.day)            

print start_date

例:入力日= 2015年12月28日6か月前の日付を計算します。

I)月の計算:このステップでは、start_dateが30/06/2015になります。
注意月の計算ステップの後に、必要な月の最終日が表示されることにて。

II)計算日:条件if(start_date.day> your_date.day) 日は、input_dateからの日が必要な月に存在するかどうかをチェックします。これは、入力日付が31(または30)日であり、必要な月が31(または2月の場合は30)日未満である条件を処理します。うるう年ケースにも対応(2月分)。この手順の後、2015年6月28日として結果を取得します

この条件が満たされない場合、start_dateは前月の最後の日付のままになります。したがって、2015年12月31日を入力日として指定し、6か月前の日付が必要な場合は、2015年6月30日になります。


0

以下の所定の関数を使用して、Xか月の前後の日付を取得できます。

日時インポート日から

def next_month(given_date、month):
    yyyy = int(((given_date.year * 12 + given_date.month)+ month)/ 12)
    mm = int(((given_date.year * 12 + given_date.month)+ month)%12)

    mm == 0の場合:
        yyyy-= 1
        mm = 12

    given_date.replace(年= yyyy、月= mm)を返します


__name__ == "__main__"の場合:
    今日= date.today()
    プリント(今日)

    mmの場合[-12、-1、0、1、2、12、20]:
        next_date = next_month(今日、mm)
        print(next_date)

0

この答えはかなり読みやすいと思います:

def month_delta(dt, delta):
    year_delta, month = divmod(dt.month + delta, 12)

    if month == 0:
        # convert a 0 to december
        month = 12
        if delta < 0:
            # if moving backwards, then it's december of last year
            year_delta -= 1

    year = dt.year + year_delta

    return dt.replace(month=month, year=year)

for delta in range(-20, 21):
    print(delta, "->", month_delta(datetime(2011, 1, 1), delta))

-20 -> 2009-05-01 00:00:00
-19 -> 2009-06-01 00:00:00
-18 -> 2009-07-01 00:00:00
-17 -> 2009-08-01 00:00:00
-16 -> 2009-09-01 00:00:00
-15 -> 2009-10-01 00:00:00
-14 -> 2009-11-01 00:00:00
-13 -> 2009-12-01 00:00:00
-12 -> 2010-01-01 00:00:00
-11 -> 2010-02-01 00:00:00
-10 -> 2010-03-01 00:00:00
-9 -> 2010-04-01 00:00:00
-8 -> 2010-05-01 00:00:00
-7 -> 2010-06-01 00:00:00
-6 -> 2010-07-01 00:00:00
-5 -> 2010-08-01 00:00:00
-4 -> 2010-09-01 00:00:00
-3 -> 2010-10-01 00:00:00
-2 -> 2010-11-01 00:00:00
-1 -> 2010-12-01 00:00:00
0 -> 2011-01-01 00:00:00
1 -> 2011-02-01 00:00:00
2 -> 2011-03-01 00:00:00
3 -> 2011-04-01 00:00:00
4 -> 2011-05-01 00:00:00
5 -> 2011-06-01 00:00:00
6 -> 2011-07-01 00:00:00
7 -> 2011-08-01 00:00:00
8 -> 2011-09-01 00:00:00
9 -> 2011-10-01 00:00:00
10 -> 2011-11-01 00:00:00
11 -> 2012-12-01 00:00:00
12 -> 2012-01-01 00:00:00
13 -> 2012-02-01 00:00:00
14 -> 2012-03-01 00:00:00
15 -> 2012-04-01 00:00:00
16 -> 2012-05-01 00:00:00
17 -> 2012-06-01 00:00:00
18 -> 2012-07-01 00:00:00
19 -> 2012-08-01 00:00:00
20 -> 2012-09-01 00:00:00

0

一発ギャグ ?

previous_month_date = (current_date - datetime.timedelta(days=current_date.day+1)).replace(day=current_date.day)


0

今試した最も簡単な方法

from datetime import datetime
from django.utils import timezone





current = timezone.now()
if current.month == 1:
     month = 12
else:
     month = current.month - 1
current = datetime(current.year, month, current.day)

0

少し前に、次のアルゴリズムに出くわしました。これは、dateまたはのいずれかで月を増分および減分するのに非常にうまく機能しますdatetime

警告day新しい月に利用できない場合、これは失敗します。私はこれをday == 1常に日付オブジェクトで使用します。

Python 3.x:

def increment_month(d, add=1):
    return date(d.year+(d.month+add-1)//12, (d.month+add-1) % 12+1, 1)

Python 2.7の場合、整数除算が暗黙に含まれるため、//12をに変更し/12ます。

最近、スクリプトがこれらの有用なグローバルを取得し始めたときに、これをデフォルトファイルで使用しました。

MONTH_THIS = datetime.date.today()
MONTH_THIS = datetime.date(MONTH_THIS.year, MONTH_THIS.month, 1)

MONTH_1AGO = datetime.date(MONTH_THIS.year+(MONTH_THIS.month-2)//12,
                           (MONTH_THIS.month-2) % 12+1, 1)

MONTH_2AGO = datetime.date(MONTH_THIS.year+(MONTH_THIS.month-3)//12,
                           (MONTH_THIS.month-3) % 12+1, 1)

-2
import datetime
date_str = '08/01/2018'
format_str = '%d/%m/%Y'
datetime_obj = datetime.datetime.strptime(date_str, format_str)   
datetime_obj.replace(month=datetime_obj.month-1)

シンプルなソリューションで、特別なライブラリは必要ありません。


2
日付が3月31日のような場合は機能しません。
18年

1
または1
LeandroHumb

-3

次のように2行で実行できます。

now = datetime.now()
last_month = datetime(now.year, now.month - 1, now.day)

輸入を覚えて

from datetime import datetime

1
Janの場合はどうなりますか?
Sergei
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.