Відповіді:
Я раніше цього не помічав, коли переглядав документацію calendar
модуля , але метод, який називається, monthrange
надає цю інформацію:
місячний діапазон (рік, місяць)
Повертає будній день першого дня місяця та кількість днів у місяці за вказаний рік та місяць.
>>> import calendar
>>> calendar.monthrange(2002,1)
(1, 31)
>>> calendar.monthrange(2008,2)
(4, 29)
>>> calendar.monthrange(2100,2)
(0, 28)
тому:
calendar.monthrange(year, month)[1]
видається найпростішим шляхом.
Щоб було зрозуміло, monthrange
підтримує і високосні роки:
>>> from calendar import monthrange
>>> monthrange(2012, 2)
(2, 29)
Моя попередня відповідь все ще працює, але явно неоптимальна.
28
, що відповідь правильна, використовуючи правила григоріанського календаря
monthrange
, що це заплутане ім’я. Ви могли б подумати, що воно повернеться (first_day, last_day)
, ні(week_day_of_first_day, number_of_days)
Якщо ви не хочете імпортувати calendar
модуль, простим двоетапним функцією також може бути:
import datetime
def last_day_of_month(any_day):
next_month = any_day.replace(day=28) + datetime.timedelta(days=4) # this will never fail
return next_month - datetime.timedelta(days=next_month.day)
Виходи:
>>> for month in range(1, 13):
... print last_day_of_month(datetime.date(2012, month, 1))
...
2012-01-31
2012-02-29
2012-03-31
2012-04-30
2012-05-31
2012-06-30
2012-07-31
2012-08-31
2012-09-30
2012-10-31
2012-11-30
2012-12-31
EDIT: Дивіться відповідь @Blair Conrad для більш чистого рішення
>>> import datetime
>>> datetime.date(2000, 2, 1) - datetime.timedelta(days=1)
datetime.date(2000, 1, 31)
today.month + 1 == 13
ви отримаєте ValueError
.
(today.month % 12) + 1
оскільки 12 % 12
дає 0
Це насправді досить просто dateutil.relativedelta
(пакет python-datetutil для pip).day=31
завжди буде повертати останній день місяця.
Приклад:
from datetime import datetime
from dateutil.relativedelta import relativedelta
date_in_feb = datetime.datetime(2013, 2, 21)
print datetime.datetime(2013, 2, 21) + relativedelta(day=31) # End-of-month
>>> datetime.datetime(2013, 2, 28, 0, 0)
datetime(2014, 2, 1) + relativedelta(days=31)
дає datetime(2014, 3, 4, 0, 0)
...
months
додається, а потім seconds
віднімається. Оскільки вони проходять так, як **kwargs
я б не покладався на це і роблю низку окремих операцій: now + relative(months=+1) + relative(day=1) + relative(days=-1)
що однозначно.
last_day = (<datetime_object> + relativedelta(day=31)).day
РЕДАКТУЙТЕ: дивіться мою іншу відповідь . Він має кращу реалізацію, ніж цей, який я залишаю тут лише на випадок, коли хтось зацікавиться бачити, як можна «скатати свій» калькулятор.
@John Millikin дає хорошу відповідь із додатковими ускладненнями підрахунку першого дня наступного місяця.
Далі не особливо елегантно, але щоб зрозуміти останній день місяця, в якому живе будь-яка дата, ви можете спробувати:
def last_day_of_month(date):
if date.month == 12:
return date.replace(day=31)
return date.replace(month=date.month+1, day=1) - datetime.timedelta(days=1)
>>> last_day_of_month(datetime.date(2002, 1, 17))
datetime.date(2002, 1, 31)
>>> last_day_of_month(datetime.date(2002, 12, 9))
datetime.date(2002, 12, 31)
>>> last_day_of_month(datetime.date(2008, 2, 14))
datetime.date(2008, 2, 29)
today = datetime.date.today(); start_date = today.replace(day=1)
. Ви хочете уникнути дзвінка datetime.now двічі, якщо ви зателефонували йому лише до півночі 31 грудня, а потім лише після півночі. Ви отримаєте 2016-01-01 замість 2016-12-01 чи 2017-01-01.
date
є екземпляром datetime.date
, datetime.datetime
а також pandas.Timestamp
.
Використовуючи, dateutil.relativedelta
ви отримаєте останню дату місяця так:
from dateutil.relativedelta import relativedelta
last_date_of_month = datetime(mydate.year, mydate.month, 1) + relativedelta(months=1, days=-1)
Ідея полягає в тому, щоб отримати перший день місяця і використовувати relativedelta
1 місяць вперед і 1 день назад, щоб ви отримали останній день місяця, якого ви хотіли.
Іншим рішенням було б зробити щось подібне:
from datetime import datetime
def last_day_of_month(year, month):
""" Work out the last day of the month """
last_days = [31, 30, 29, 28, 27]
for i in last_days:
try:
end = datetime(year, month, i)
except ValueError:
continue
else:
return end.date()
return None
І скористайтеся такою функцією:
>>>
>>> last_day_of_month(2008, 2)
datetime.date(2008, 2, 29)
>>> last_day_of_month(2009, 2)
datetime.date(2009, 2, 28)
>>> last_day_of_month(2008, 11)
datetime.date(2008, 11, 30)
>>> last_day_of_month(2008, 12)
datetime.date(2008, 12, 31)
from datetime import timedelta
(any_day.replace(day=1) + timedelta(days=32)).replace(day=1) - timedelta(days=1)
якщо ви бажаєте використовувати зовнішню бібліотеку, ознайомтесь http://crsmithdev.com/arrow/
Потім ви можете отримати останній день місяця за допомогою:
import arrow
arrow.utcnow().ceil('month').date()
Це повертає об'єкт дати, який ви потім можете зробити маніпуляцією.
Щоб отримати останню дату місяця, ми робимо щось подібне:
from datetime import date, timedelta
import calendar
last_day = date.today().replace(day=calendar.monthrange(date.today().year, date.today().month)[1])
Тепер, щоб пояснити, що ми робимо тут, розбимо це на дві частини:
По-перше, це кількість днів поточного місяця, за який ми використовуємомісячний діапазон, про який Блер Конрад уже згадував своє рішення:
calendar.monthrange(date.today().year, date.today().month)[1]
друге - це отримання останньої дати, яку ми робимо за допомогою заміни, наприклад
>>> date.today()
datetime.date(2017, 1, 3)
>>> date.today().replace(day=31)
datetime.date(2017, 1, 31)
і коли ми поєднуємо їх, як згадувалося вгорі, ми отримуємо динамічне рішення.
date.replace(day=day)
щоб усі знали, що відбувається.
У Python 3.7 є недокументована calendar.monthlen(year, month)
функція :
>>> calendar.monthlen(2002, 1)
31
>>> calendar.monthlen(2008, 2)
29
>>> calendar.monthlen(2100, 2)
28
Це еквівалент документованому calendar.monthrange(year, month)[1]
дзвінку .
import datetime
now = datetime.datetime.now()
start_month = datetime.datetime(now.year, now.month, 1)
date_on_next_month = start_month + datetime.timedelta(35)
start_next_month = datetime.datetime(date_on_next_month.year, date_on_next_month.month, 1)
last_day_month = start_next_month - datetime.timedelta(1)
Використовуйте панди!
def isMonthEnd(date):
return date + pd.offsets.MonthEnd(0) == date
isMonthEnd(datetime(1999, 12, 31))
True
isMonthEnd(pd.Timestamp('1999-12-31'))
True
isMonthEnd(pd.Timestamp(1965, 1, 10))
False
now=datetime.datetime.now(); pd_now = pd.to_datetime(now); print(pd_now.days_in_month)
Для мене це найпростіший спосіб:
selected_date = date(some_year, some_month, some_day)
if selected_date.month == 12: # December
last_day_selected_month = date(selected_date.year, selected_date.month, 31)
else:
last_day_selected_month = date(selected_date.year, selected_date.month + 1, 1) - timedelta(days=1)
Найпростіший спосіб (без імпорту календаря) - отримати перший день наступного місяця, а потім відняти з нього день.
import datetime as dt
from dateutil.relativedelta import relativedelta
thisDate = dt.datetime(2017, 11, 17)
last_day_of_the_month = dt.datetime(thisDate.year, (thisDate + relativedelta(months=1)).month, 1) - dt.timedelta(days=1)
print last_day_of_the_month
Вихід:
datetime.datetime(2017, 11, 30, 0, 0)
PS: Цей код працює швидше порівняно з import calendar
підходом; Дивись нижче:
import datetime as dt
import calendar
from dateutil.relativedelta import relativedelta
someDates = [dt.datetime.today() - dt.timedelta(days=x) for x in range(0, 10000)]
start1 = dt.datetime.now()
for thisDate in someDates:
lastDay = dt.datetime(thisDate.year, (thisDate + relativedelta(months=1)).month, 1) - dt.timedelta(days=1)
print ('Time Spent= ', dt.datetime.now() - start1)
start2 = dt.datetime.now()
for thisDate in someDates:
lastDay = dt.datetime(thisDate.year,
thisDate.month,
calendar.monthrange(thisDate.year, thisDate.month)[1])
print ('Time Spent= ', dt.datetime.now() - start2)
ВИХІД:
Time Spent= 0:00:00.097814
Time Spent= 0:00:00.109791
Цей код передбачає, що ви хочете дати останнього дня місяця (тобто, не тільки DD-частини, а всієї дати РРРРММДД)
import calendar
?
Ось ще одна відповідь. Не потрібні додаткові пакети.
datetime.date(year + int(month/12), month%12+1, 1)-datetime.timedelta(days=1)
Отримайте перший день наступного місяця і відніміть з нього день.
Ви можете самостійно розрахувати кінцеву дату. проста логіка - відняти день від start_date наступного місяця. :)
Отже, напишіть спеціальний метод,
import datetime
def end_date_of_a_month(date):
start_date_of_this_month = date.replace(day=1)
month = start_date_of_this_month.month
year = start_date_of_this_month.year
if month == 12:
month = 1
year += 1
else:
month += 1
next_month_start_date = start_date_of_this_month.replace(month=month, year=year)
this_month_end_date = next_month_start_date - datetime.timedelta(days=1)
return this_month_end_date
Дзвінки,
end_date_of_a_month(datetime.datetime.now().date())
Він поверне кінцеву дату цього місяця. Передайте даній функції будь-яку дату. повертає вам кінцеву дату цього місяця.
ви можете використовувати Reladelta
https://dateutil.readthedocs.io/en/stable/relativedelta.html,
month_end = <your datetime value within the month> + relativedelta(day=31)
який дасть вам останній день.
Це найпростіше рішення для мене, використовуючи лише стандартну бібліотеку дат:
import datetime
def get_month_end(dt):
first_of_month = datetime.datetime(dt.year, dt.month, 1)
next_month_date = first_of_month + datetime.timedelta(days=32)
new_dt = datetime.datetime(next_month_date.year, next_month_date.month, 1)
return new_dt - datetime.timedelta(days=1)
Це не стосується головного питання, але одна приємна хитрість отримати останній будній день в місяці - це використовувати calendar.monthcalendar
, який повертає матрицю дат, організовану з понеділка як перший стовпчик по неділю як останній.
# Some random date.
some_date = datetime.date(2012, 5, 23)
# Get last weekday
last_weekday = np.asarray(calendar.monthcalendar(some_date.year, some_date.month))[:,0:-2].ravel().max()
print last_weekday
31
Вся [0:-2]
справа в тому, щоб стригти колони на вихідних і викидати їх. Дати, які випадають поза місяцем, позначаються 0, тому максимум ефективно їх ігнорує.
Використання numpy.ravel
не є суворо необхідним, але я ненавиджу, покладаючись на просту умову, яка numpy.ndarray.max
згладить масив, якщо не буде сказано, яку вісь обчислити.
import calendar
from time import gmtime, strftime
calendar.monthrange(int(strftime("%Y", gmtime())), int(strftime("%m", gmtime())))[1]
Вихід:
31
Це надрукує останній день будь-якого поточного місяця. У цьому прикладі це було 15 травня 2016 року. Таким чином, ваш результат може бути різним, однак результат буде стільки днів, скільки поточного місяця. Чудово, якщо ви хочете перевірити останній день місяця, виконуючи щоденну роботу з крон.
Тому:
import calendar
from time import gmtime, strftime
lastDay = calendar.monthrange(int(strftime("%Y", gmtime())), int(strftime("%m", gmtime())))[1]
today = strftime("%d", gmtime())
lastDay == today
Вихід:
False
Якщо це не останній день місяця.
Якщо ви хочете зробити свою маленьку функцію, це хороший вихідний пункт:
def eomday(year, month):
"""returns the number of days in a given month"""
days_per_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
d = days_per_month[month - 1]
if month == 2 and (year % 4 == 0 and year % 100 != 0 or year % 400 == 0):
d = 29
return d
Для цього ви повинні знати правила для високосних років:
У наведеному нижче коді 'get_last_day_of_month (dt)' дасть вам це, з датою у форматі рядка типу "РРРР-MM-DD".
import datetime
def DateTime( d ):
return datetime.datetime.strptime( d, '%Y-%m-%d').date()
def RelativeDate( start, num_days ):
d = DateTime( start )
return str( d + datetime.timedelta( days = num_days ) )
def get_first_day_of_month( dt ):
return dt[:-2] + '01'
def get_last_day_of_month( dt ):
fd = get_first_day_of_month( dt )
fd_next_month = get_first_day_of_month( RelativeDate( fd, 31 ) )
return RelativeDate( fd_next_month, -1 )
Найпростіший спосіб - використовувати datetime
математику дати, наприклад, відняти день з першого дня наступного місяця:
import datetime
def last_day_of_month(d: datetime.date) -> datetime.date:
return (
datetime.date(d.year + d.month//12, d.month % 12 + 1, 1) -
datetime.timedelta(days=1)
)
Крім того, ви можете використовувати calendar.monthrange()
кількість днів у місяці (враховуючи високосні роки) та оновити дату відповідно:
import calendar, datetime
def last_day_of_month(d: datetime.date) -> datetime.date:
return d.replace(day=calendar.monthrange(d.year, d.month)[1])
Швидкий показник показує, що перша версія помітно швидша:
In [14]: today = datetime.date.today()
In [15]: %timeit last_day_of_month_dt(today)
918 ns ± 3.54 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [16]: %timeit last_day_of_month_calendar(today)
1.4 µs ± 17.3 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
Ось довга (проста для розуміння) версія, але вона піклується про високосні роки.
ура, Ж.К.
def last_day_month(year, month):
leap_year_flag = 0
end_dates = {
1: 31,
2: 28,
3: 31,
4: 30,
5: 31,
6: 30,
7: 31,
8: 31,
9: 30,
10: 31,
11: 30,
12: 31
}
# Checking for regular leap year
if year % 4 == 0:
leap_year_flag = 1
else:
leap_year_flag = 0
# Checking for century leap year
if year % 100 == 0:
if year % 400 == 0:
leap_year_flag = 1
else:
leap_year_flag = 0
else:
pass
# return end date of the year-month
if leap_year_flag == 1 and month == 2:
return 29
elif leap_year_flag == 1 and month != 2:
return end_dates[month]
else:
return end_dates[month]
else: pass
а також позбутися if year % 400 == 0: leap_year_flag = 1
незначних змін
Ось рішення на базі лямбда пітона:
next_month = lambda y, m, d: (y, m + 1, 1) if m + 1 < 13 else ( y+1 , 1, 1)
month_end = lambda dte: date( *next_month( *dte.timetuple()[:3] ) ) - timedelta(days=1)
next_month
Лямбда знаходить кортеж подання до перший день наступного місяця, а роли на наступний рік. month_end
Лямбда перетворює дату ( dte
) до кортежу, застосовується next_month
і створює нову дату. Тоді "кінець місяця" - це лише мінус першого місяця наступного місяця timedelta(days=1)
.