Python'da bir aydan bir ayı çıkarmanın en basit yolu nedir?


103

Sadece timedelta'nın yapıcısında bir ay argümanı olsaydı. Peki bunu yapmanın en basit yolu nedir?

DÜZENLEME: Aşağıda belirtildiği gibi bu konuda çok düşünmüyordum. Gerçekten istediğim şey geçen ayın herhangi bir günüydü çünkü sonunda sadece yılı ve ayı alacağım. Öyleyse bir datetime nesnesi verildiğinde , önceki aya denk gelen herhangi bir datetime nesnesini döndürmenin en basit yolu nedir?

Yanıtlar:


64

Bunu dene:

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%100==0 or 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üzenleme Günü de işlemek için düzeltildi.

Düzenle Ayrıca şunun için daha basit bir hesaplamaya işaret eden şaşkın cevaba da bakın d:

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

Eğer doğru yaparsam ((date.month+delta-1) % 12)+1çalışır ve if not m: m = 12kutunun çıkarılması anlamına gelir
Charles L.

@ dan-klasson detaylandırır mısınız?
Jean-François Fabre

@ Jean-FrançoisFabre İlki basit değil. Ayrıca datetime ve timedelta ile bunu kolaylıkla yapabilirsiniz.
dan-klasson

Evet, OP düzenlendikten sonra, istedikleri şeyin tam olarak yapılabileceği açıkça ortaya çıkıyor. dt - timedelta(days=dt.day)Başlangıçta ifade edildiği gibi, kulağa keyfi bir sayıda ay çıkarmak istiyormuş gibi geliyor ve bu biraz daha zor.
Duncan

Artık yıllar için hesaplamanın yanlış olduğunu düşünüyorum - öyle olmalı 29 if y%4==0 and (not y%100==0 or y%400 == 0) else 28. örneğin 1600 artık yıl iken 1700 değil
Shawn Loewen

190

Üçüncü parti dateutilmodülü kullanabilirsiniz (PyPI girişi burada ).

import datetime
import dateutil.relativedelta

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

çıktı:

2013-02-28 00:00:00

Param monthve monthsdateutil ile karıştırdım . Param ile çıkarma monthçalışmıyor ama monthsişe yarıyor 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

2
@SiminJie: Kullanmanın months1 ay fark yaratacağını düşünüyorum . Kullanımı month, girişin ayına bakılmaksızın ayı 1'e (örneğin Ocak) ayarlar.
Frank Meulenaar

71

Orijinal sorunun "önceki aydaki herhangi bir tarih saat nesnesi" olarak düzenlenmesinden sonra, ayın ilk gününden 1 gün çıkararak bunu oldukça kolay bir şekilde yapabilirsiniz.

from datetime import datetime, timedelta

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

4
kullanabilirsinizdt.replace(day=1) - timedelta(1)
jfs

3
veyadt - timedelta(days=dt.day)
SergiyKolesnikov

25

Vektörize edilmiş pandaların çözümü çok basittir:

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


Teşekkür ederim! Tarihlerime kolayca bir ay eklememe / çıkarmama yardımcı oldu!
Mateus da Silva Teixeira

Kullanabilirsen bu güzel pandas. Teşekkürler!
igorkf

24

Duncan'ın cevabının bir varyasyonu (yorum yapacak kadar itibarım yok), takvim.monthrange'ı kullanarak ayın son gününün hesaplanmasını önemli ölçüde basitleştiriyor:

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'da Ayın Son Gününü Al'dan ay aralığı hakkında bilgi


17

Sadece timedelta'nın kurucusunda bir ay argümanı olsaydı. Peki bunu yapmanın en basit yolu nedir?

Örneğin 30 Mart tarihinden bir ay çıkardığınızda sonucun ne olmasını istiyorsunuz? Ayları toplama veya çıkarma ile ilgili sorun budur: ayların farklı uzunlukları vardır! Bazı uygulamasında bir istisna diğerlerinde, böyle durumlarda uygun olduğu "bir önceki ayın son günü" kullanımına Tamam (ama bu gerçekten deli aritmetik, bir ay ekleyerek daha sonra bir ay çıkarılarak zaman olduğu var değil genel no-operasyon!) , bazılarında ise tarihe ek olarak gerçekle ilgili bazı göstergeler tutmak isteyeceksiniz, örneğin, "28 Şubat diyorum ama eğer varsa 30 Şubat'ı gerçekten isterim", böylece bir ay daha ekleme veya çıkarma bu, işleri yeniden düzeltebilir (ve ikincisi, bir veri artı bir şey içeren özel bir sınıf gerektirir).

Tüm uygulamalar için tolere edilebilecek gerçek bir çözüm olamaz ve bu sefil işlemin anlambilimine yönelik özel uygulamanızın ihtiyaçlarının ne olduğunu bize söylemediniz, bu nedenle burada sağlayabileceğimiz çok fazla yardım yok.


Kesinlikle haklısın. Timedelta'nın bunun için tartışması hakkında şaka yapıyordum. Aynı mantığın saatler veya dakikalarca tartışmamak için de geçerli olup olmadığını merak ediyorum. Teknik olarak bir gün tam olarak 24 saat değildir.
Bialecki

PHP bunu 28 Şubat'a 2 gün ekleyerek halleder ve 2 Mart'a kadar gider. Bunun için kurallar var, onları hızlı bir şekilde bulmama izin verin
NullUserException

Döviz bozdurma tarihlerini hesaplıyorsanız daha da ilginç hale gelir: örneğin, ileride 3 veya 6 ay gibi bir tarih bulmanız gerekebilir, ancak bu her iki ülkede de bir iş günü olmalıdır (Müslüman ülkelerde Cuma değil) ve her iki ülkede de banka tatili değil.
Duncan

@Bialecki: datetimemodüller bir günün tam olarak 24 saat olduğunu varsayar (artık saniye yok)
jfs

9

Bence basit yol Pandas'tan DateOffset'i şu şekilde kullanmak:

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

Sonuç bir datetime nesnesi olacaktır


2
Bu en sezgisel çözüm
Kots

6

Tek istediğiniz son ayın herhangi bir günüyse, yapabileceğiniz en basit şey gün sayısını geçerli tarihten çıkarmaktır, bu size önceki ayın son gününü verecektir.

Örneğin, herhangi bir tarihten başlayarak:

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

Elde ettiğimiz mevcut tarihin günlerini çıkararak:

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

Bu, geçen ayın herhangi bir günündeki basitleştirilmiş ihtiyacınız için yeterlidir.

Ama artık buna sahip olduğunuza göre, başladığınız gün de dahil olmak üzere ayın herhangi bir gününü de alabilirsiniz (yani aşağı yukarı bir ay çıkarmakla aynıdır):

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

Elbette, 30 günlük bir ayın 31'ine veya Şubat ayından itibaren eksik olan günlere (ve artık yıllara dikkat etmenize) dikkat etmeniz gerekir, ancak bunu yapmak da kolaydır:

>>> 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

Bunu, Q4'ün 1 Ekim'de başladığı hükümet mali yılları için kullanıyorum. Not Tarihi çeyreklere dönüştürüyorum ve onu da geri alıyorum.

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)

1

Geçen ayın son günü:

>>> import datetime
>>> datetime.datetime.now() - datetime.timedelta(days=datetime.datetime.now().day)
datetime.datetime(2020, 9, 30, 14, 13, 15, 67582)

Geçen ay aynı günü döndürür:

>>> x = datetime.datetime.now() - datetime.timedelta(days=datetime.datetime.now().day)
>>> x.replace(day=datetime.datetime.now().day)
datetime.datetime(2020, 9, 7, 14, 22, 14, 362421)

0

İşte bunu yapmak için bazı kodlar . Ben kendim denemedim ...

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

Ayın 1-12 arasında gittiği bir (yıl, ay) demet verildiğinde şunu deneyin:

>>> 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)

Her yıl 12 ay olduğunu varsayar.


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

Belirli bir Tarihten n Ay geriye gitmek için aşağıdaki kodu kullandım:

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

Örneğin: giriş tarihi = 28/12/2015 6 ay önceki tarihi hesaplayın.

I) AYI HESAPLA: Bu adım size başlangıç_tarihini 30/06/2015 olarak verecektir.
Not hesapla aylık adımdan sonra gerekli ayın son gününü alacak.

II) GÜNÜ HESAPLA: (başlangıç_tarihi> tarih_günüz) giriş_tarihinden itibaren günün gerekli ayda mevcut olup olmadığını kontrol ederse koşul . Bu, giriş tarihinin 31 (veya 30) olduğu ve gerekli ayın 31 (veya feb olması durumunda 30) günden az olduğu durumu yönetir. Artık yıl durumunu da ele alır (Şubat için). Bu adımdan sonra 28/06/2015 olarak sonuç alacaksınız

Bu koşul karşılanmazsa, başlangıç_tarihi önceki ayın son tarihi olarak kalır. Yani giriş tarihi olarak 31/12/2015 verirseniz ve 6 ay önceki tarihi isterseniz size 30/06/2015 verir.


0

X aydan önceki / sonraki tarihi almak için aşağıda verilen işlevi kullanabilirsiniz.

tarih saatinden itibaren

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

    mm == 0 ise:
        yyyy - = 1
        mm = 12

    return given_date.replace (yıl = yyyy, ay = aa)


__name__ == "__main__" ise:
    today = date.today ()
    baskı (bugün)

    [-12, -1, 0, 1, 2, 12, 20] 'de mm için:
        next_date = next_month (bugün, mm)
        baskı (sonraki_tarih)

0

Bu cevabın oldukça okunabilir olduğunu düşünüyorum:

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

Tek astar mı?

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


0

Şimdi denediğim en basit yol

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

Bir süre önce, a dateveya üzerinde ayları artırmak ve azaltmak için çok iyi çalışan aşağıdaki algoritmaya rastladım datetime.

CAVEAT : dayYeni ayda mevcut değilse bu başarısız olur . Bunu day == 1her zaman tarih nesnelerinde kullanırım .

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 //12için /12tamsayı bölme ima edildiğinden beri olarak değiştirin .

Son zamanlarda bir komut dosyası bu yararlı globalleri almaya başladığında bunu varsayılan bir dosyada kullandım:

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)

Basit çözüm, özel kitaplıklara gerek yok.


2
Tarih 31 Mart gibi bir şey olduğunda işe yaramayacak.
firecast

1
Veya 1 Ocak
LeandroHumb

-3

Bunu iki satırda yapabilirsin:

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

ithalatı hatırla

from datetime import datetime

1
Ya Jan ise?
Sergei
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.