Cách đơn giản nhất để trừ một tháng cho một ngày trong Python là gì?


99

Nếu thời gian chỉ có một đối số tháng trong hàm tạo của nó. Vậy cách đơn giản nhất để làm điều này là gì?

CHỈNH SỬA: Tôi đã không suy nghĩ quá nhiều về điều này như được chỉ ra bên dưới. Thực sự những gì tôi muốn là bất kỳ ngày nào trong tháng trước vì cuối cùng tôi sẽ chỉ lấy năm và tháng. Vì vậy, với một đối tượng datetime, cách đơn giản nhất để trả về bất kỳ đối tượng datetime nào rơi vào tháng trước là gì?

Câu trả lời:


62

Thử cái này:

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)

Chỉnh sửa Đã sửa chữa để xử lý cả ngày.

Chỉnh sửa Xem thêm câu trả lời từ câu đố chỉ ra một phép tính đơn giản hơn cho d:

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

Nếu tôi có nó ngay ((date.month+delta-1) % 12)+1công trình, phương tiện các if not m: m = 12thể loại bỏ được
Charles L.

@ dan-klasson bạn có thể nói rõ hơn được không?
Jean-François Fabre

@ Jean-FrançoisFabre Cái đầu tiên không đơn giản. Ngoài ra, người ta có thể dễ dàng làm điều này với datetime và timeelta.
dan-klasson

Vâng, sau khi OP chỉnh sửa, rõ ràng là những gì họ muốn có thể được thực hiện chỉ bằng dt - timedelta(days=dt.day)Như đã diễn giải ban đầu mặc dù có vẻ như họ muốn có thể trừ một số tháng tùy ý và điều đó khó hơn một chút.
Duncan

183

Bạn có thể sử dụng dateutilmô-đun của bên thứ ba (mục nhập PyPI tại đây ).

import datetime
import dateutil.relativedelta

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

đầu ra:

2013-02-28 00:00:00

Tôi nhầm lẫn với param monthmonthsdateutil. Trừ với param monthkhông hoạt động nhưng monthscông trình 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: Tôi nghĩ sử dụng monthssẽ tạo ra sự khác biệt trong 1 tháng. Việc sử dụng monthsẽ đặt tháng thành 1 (ví dụ: tháng 1), bất kể tháng nhập.
Frank Meulenaar,

70

Sau khi chỉnh sửa câu hỏi ban đầu thành "bất kỳ đối tượng datetime nào trong tháng trước", bạn có thể thực hiện điều đó khá dễ dàng bằng cách trừ đi 1 ngày từ ngày đầu tiên của tháng.

from datetime import datetime, timedelta

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

4
bạn có thể sử dụngdt.replace(day=1) - timedelta(1)
jfs

3
hoặcdt - timedelta(days=dt.day)
SergiyKolesnikov

23

Một biến thể trong câu trả lời của Duncan (tôi không đủ danh tiếng để bình luận), sử dụng calendar.monthrange để đơn giản hóa đáng kể việc tính toán ngày cuối cùng của tháng:

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)

Thông tin về phạm vi tháng từ Nhận ngày cuối cùng của tháng bằng Python


22

Một giải pháp vectơ, gấu trúc rất đơn giản:

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


Cảm ơn bạn! Nó đã giúp tôi dễ dàng thêm / rút ngắn một tháng vào / từ nhóm ngày của mình!
Mateus da Silva Teixeira

Điều này là tốt đẹp nếu bạn có thể sử dụng pandas. Cảm ơn!
igorkf

17

Nếu thời gian chỉ có một đối số tháng trong hàm tạo của nó. Vậy cách đơn giản nhất để làm điều này là gì?

Bạn muốn kết quả là bao nhiêu khi trừ đi một tháng, chẳng hạn như ngày 30 tháng 3? Đó là vấn đề với việc cộng hoặc trừ các tháng: các tháng có độ dài khác nhau! Trong một số ứng dụng, một ngoại lệ phù hợp trong những trường hợp như vậy, trong những trường hợp khác, "ngày cuối cùng của tháng trước" được sử dụng (nhưng đó là số học thực sự điên rồ, khi trừ đi một tháng rồi thêm một tháng thì không. tổng thể một phép toán!) , đối với những người khác nhưng bạn sẽ muốn giữ thêm vào ngày một số dấu hiệu về thực tế, ví dụ: "Tôi đang nói là ngày 28 tháng 2 nhưng tôi thực sự muốn ngày 30 tháng 2 nếu nó tồn tại", do đó, thêm hoặc bớt một tháng khác để điều đó có thể thiết lập lại mọi thứ (và thứ sau rõ ràng yêu cầu một lớp tùy chỉnh giữ một dữ liệu cộng với s / thứ khác).

Không thể có giải pháp thực sự nào có thể chấp nhận được cho tất cả các ứng dụng và bạn đã không cho chúng tôi biết nhu cầu của ứng dụng cụ thể của bạn là gì đối với ngữ nghĩa của hoạt động tồi tệ này, vì vậy không có nhiều trợ giúp hơn mà chúng tôi có thể cung cấp ở đây.


Bạn hoàn toàn đúng. Chỉ nói đùa về việc đồng thời có một lập luận cho điều đó. Tôi tự hỏi liệu logic tương tự có áp dụng cho việc không có đối số trong nhiều giờ hoặc vài phút hay không. Về mặt kỹ thuật, một ngày không chính xác là 24 giờ.
Bialecki

PHP xử lý điều đó bằng cách thêm 2 ngày vào ngày 28 tháng 2 và chuyển sang ngày 2 tháng 3. Có đang ước cho điều này, hãy để tôi tìm thấy chúng thật nhanh chóng
NullUserException

Nó thậm chí còn thú vị hơn nếu bạn đang tính toán ngày để trao đổi tiền tệ: ví dụ: bạn có thể cần phải tìm một ngày trong tương lai 3 hoặc 6 tháng, nhưng nó cũng phải là ngày làm việc ở cả hai quốc gia (không phải thứ sáu ở các quốc gia Hồi giáo) và không phải là một kỳ nghỉ ngân hàng ở cả hai quốc gia.
Duncan

@Bialecki: datetimemô-đun giả định rằng một ngày chính xác là 24 giờ (không có giây nhuận)
jfs

8

Tôi nghĩ cách đơn giản là sử dụng DateOffset từ Pandas như vậy:

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

Kết quả sẽ là một đối tượng datetime


2
Đó là giải pháp trực quan nhất
Kots

5

Nếu tất cả những gì bạn muốn là bất kỳ ngày nào trong tháng trước, điều đơn giản nhất bạn có thể làm là trừ số ngày của ngày hiện tại, sẽ cho bạn ngày cuối cùng của tháng trước.

Ví dụ: bắt đầu bằng bất kỳ ngày nào:

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

Trừ các ngày của ngày hiện tại, chúng tôi nhận được:

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

Điều này là đủ cho nhu cầu đơn giản hóa của bạn vào bất kỳ ngày nào trong tháng trước.

Nhưng bây giờ bạn đã có nó, bạn cũng có thể nhận được bất kỳ ngày nào trong tháng, bao gồm cùng ngày bạn bắt đầu (tức là nhiều hơn hoặc ít hơn khi trừ đi một tháng):

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

Tất nhiên, bạn cần phải cẩn thận với ngày 31 của một tháng 30 ngày hoặc những ngày bị thiếu từ tháng Hai (và lưu ý đến các năm nhuận), nhưng điều đó cũng dễ thực hiện:

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

Tôi sử dụng điều này cho các năm tài chính của chính phủ khi Q4 bắt đầu từ ngày 1 tháng 10. Lưu ý rằng tôi chuyển đổi ngày tháng thành quý và hoàn tác nó.

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

Đây là một số để làm điều đó. Bản thân mình cũng chưa thử ...

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

Với một bộ (năm, tháng), trong đó tháng đi từ 1-12, hãy thử điều này:

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

Giả sử có 12 tháng trong mỗi năm.


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

Tôi đã sử dụng mã sau để quay lại n Tháng từ một Ngày cụ thể:

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

Ví dụ: input date = 28/12/2015 Tính ngày 6 tháng trước.

I) TÍNH TOÁN THÁNG: Bước này sẽ cung cấp cho bạn ngày bắt đầu là 30/06/2015.
Lưu ý rằng sau bước tính toán tháng, bạn sẽ nhận được ngày cuối cùng của tháng bắt buộc.

II) NGÀY TÍNH TOÁN: Điều kiện if (start_date.day> your_date.day) kiểm tra xem ngày từ input_date có hiện diện trong tháng được yêu cầu hay không. Điều này xử lý điều kiện trong đó ngày nhập là 31 (hoặc 30) và tháng bắt buộc có ít hơn 31 (hoặc 30 trong trường hợp feb) ngày. Nó cũng xử lý trường hợp năm nhuận (Đối với tháng 2). Sau bước này, bạn sẽ nhận được kết quả là 28/06/2015

Nếu điều kiện này không được thỏa mãn, start_date vẫn là ngày cuối cùng của tháng trước. Vì vậy, nếu bạn cho ngày 31 tháng 12 năm 2015 là ngày nhập và muốn ngày 6 tháng trước đó, nó sẽ cung cấp cho bạn ngày 30 tháng 6 năm 2015


0

Bạn có thể sử dụng hàm dưới đây để lấy ngày trước / sau X tháng.

từ ngày nhập datetime

def next_month (ngày_cho_đến, tháng):
    yyyy = int ((((ngày_năm_năm * 12 + ngày_năm_ tháng_năm) + tháng) / 12)
    mm = int (((ngày_năm_năm * 12 + ngày_năm_năm) + tháng)% 12)

    nếu mm == 0:
        yyyy - = 1
        mm = 12

    return given_date.replace (year = yyyy, month = mm)


nếu __name__ == "__main__":
    hôm nay = date.today ()
    in (hôm nay)

    cho mm trong [-12, -1, 0, 1, 2, 12, 20]:
        next_date = next_month (hôm nay, mm)
        print (next_date)

0

Tôi nghĩ câu trả lời này khá dễ đọc:

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

Lót ?

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


0

Cách đơn giản nhất mà tôi đã thử vừa rồi

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

Cách đây một thời gian, tôi đã xem qua thuật toán sau đây hoạt động rất tốt để tăng và giảm các tháng trên a datehoặc datetime.

CAVEAT : Điều này sẽ không thành công nếu daykhông có trong tháng mới. Tôi sử dụng điều này trên các đối tượng ngày ở bất kỳ đâu 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)

Đối với Python 2.7, thay đổi //12thành chỉ /12vì phép chia số nguyên được ngụ ý.

Gần đây, tôi đã sử dụng điều này trong tệp mặc định khi một tập lệnh bắt đầu nhận được các hình cầu hữu ích sau:

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)

Giải pháp đơn giản, không cần thư viện đặc biệt.


2
Sẽ không hoạt động khi ngày giống như ngày 31 tháng 3.
firecast

1
Hoặc như ngày 1 tháng 1
LeandroHumb

-3

Bạn có thể làm theo hai dòng như sau:

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

nhớ nhập khẩu

from datetime import datetime

1
Nếu đó là Jan thì sao?
Sergei
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.