





Обновлено: см. Ответ @Blair Conrad для более чистого решения
>>> import datetime
>>> datetime.date(2000, 2, 1) - datetime.timedelta(days=1)
datetime.date(2000, 1, 31)
Вы можете решить эту проблему, используя (today.month % 12) + 1, поскольку 12 % 12 дает 0
Какой-то странный переключатель перехода на летнее время 31-го числа месяца (я думаю, что этого не существует сегодня, но будущее может быть другим) может отображать 31-е число этого месяца продолжительностью всего 23 часа, так что вычитание одного дня позволяет вам конец в 23:00:00 30 числа. Конечно, это странный случай, но он показывает, что подход ненадежен.
Обновлено: см. мой другой ответ. У него лучшая реализация, чем у этой, которую я оставляю здесь на всякий случай, если кому-то интересно посмотреть, как можно «свернуть свой собственный» калькулятор.
@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)
Звучит глупо, но как мне получить первый день месяца примерно так.
Разве не всегда 1?
Да, но я был сбит с толку, я выглядел примерно так: start_date = date (datetime.now (). Year, datetime.now (). Month, 1)
Ах. today = datetime.date.today(); start_date = today.replace(day=1). Вам следует избегать вызова datetime.now дважды, если вы вызвали его незадолго до полуночи 31 декабря, а затем сразу после полуночи. Вы получите 2016-01-01 вместо 2016-12-01 или 2017-01-01.
Это очень просто для понимания и возвращает экземпляр datetime, который может быть полезен во многих случаях. Еще одно преимущество заключается в том, что он работает, если вход date является экземпляром datetime.date, datetime.datetime, а также pandas.Timestamp.
calendar.monthrange предоставляет эту информацию:
calendar.monthrange(year, month)
Returns weekday of first day of the month and number of days in month, for the specified year and month.
>>> import calendar
>>> calendar.monthrange(2002, 1)
(1, 31)
>>> calendar.monthrange(2008, 2) # leap years are handled correctly
(4, 29)
>>> calendar.monthrange(2100, 2) # years divisible by 100 but not 400 aren't leap years
(0, 28)
так:
calendar.monthrange(year, month)[1]
кажется самым простым способом.
Это неправильный ответ на исходный вопрос! Что делать, если последний день месяца - сб / вс.
@mahdi: правильно, второе число - это «количество дней в месяце» == «последний день», независимо от того, какой это день.
Он возвращается в неправильный день для февраля 1800 года. Я не понимаю
@ sword1st, я вижу ответ 28, и это правильный ответ, используя правила григорианского календаря
Я не могу быть единственным, кто думает, что monthrange - сбивающее с толку имя. Можно подумать, он вернет (first_day, last_day), а не (week_day_of_first_day, number_of_days)
first_day не будет особенно полезен, так как он всегда будет 1. Но я согласен, week_day_of_first_day кажется не связанным с «диапазоном месяцев».
Другое решение - сделать что-то вроде этого:
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)
Слишком сложно, нарушает третье правило дзен питона.
Это не решает основной вопрос, но один хороший трюк для получения последнего будний день в месяце - это использовать 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 сгладит массив, если не указано, по какой оси рассчитывать.
Если вы не хотите импортировать модуль calendar, простая двухэтапная функция также может быть:
import datetime
def last_day_of_month(any_day):
# this will never fail
# get close to the end of the month for any day, and add 4 days 'over'
next_month = any_day.replace(day=28) + datetime.timedelta(days=4)
# subtract the number of remaining 'overage' days to get last day of current month, or said programattically said, the previous day of the first of next month
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
@VikramsinhGaikwad Что ты имеешь в виду? Первый день месяца всегда 1.
значение отметки времени? Я имею в виду значение отметки времени?
@VikramsinhGaikwad - просто используйте datetime.datetime (год, месяц, 1)
datetime не работает годами после 9999, тогда как calendar.monthrange работает.
@Boris Это вряд ли проблема с ответом, но сам Python. Кроме того, теперь мне очень любопытно, над чем вы работаете.
Это не проблема, просто предостережение, которое, как я думал, кому-то еще может пригодиться в будущем. Я ни над чем не работаю, но слышал, что это проблема для людей, занимающихся подсчетом хранилищ ядерных отходов и астрономией.
На самом деле это довольно просто с dateutil.relativedelta. day=31 всегда возвращает последний день месяца:
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)
Установите dateutil с
pip install python-datetutil
Мне лично нравится relativedelta (месяцы = +1, секунды = -1) кажется более очевидным, что происходит
Вы не правы. datetime(2014, 2, 1) + relativedelta(days=31) дает datetime(2014, 3, 4, 0, 0) ...
вы использовали days = вместо day =
@BenH Это основано на надежде (я не уверен, что это указано), что сначала добавляется months, а затем вычитается seconds. Поскольку они передаются как **kwargs, я бы не стал на это полагаться и выполнять ряд отдельных операций: now + relative(months=+1) + relative(day=1) + relative(days=-1), что однозначно.
last_day = (<datetime_object> + relativedelta(day=31)).day
@CarMoreno Да работает с високосными годами. Попробуйте (datetime(2020,2,2) + relativedelta(day=31)).day, дает 29
Для меня relativedelta(day=+31) выигрывает; datetime(2012, 2, 5) + relativedelta(day=+31) дал мне: 2012-02-29 00:00:00datetime(2012, 2, 5) + relativedelta(months=+1, seconds=-1) дал мне: 2012-03-04 23:59:59
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 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
Для этого необходимо знать правила для високосных лет:
Конечно, но в системных библиотеках уже есть эти данные, и если правила будут изменены постановлением, обновление библиотек - это чья-то проблема.
Что ж, возможно, но очень маловероятно, и даже если - он укусит вас примерно через 2000 лет ... en.wikipedia.org/wiki/Gregorian_calendar#Accuracy
Эти правила не работают на 500 лет назад. У меня нет уверенности, что они будут действовать на тысячи лет вперед.
from datetime import timedelta
(any_day.replace(day=1) + timedelta(days=32)).replace(day=1) - timedelta(days=1)
Вот из чего сделаны баги;) Попробуй с 31 января
@LeartS: у меня работает. Что происходит, когда вы пытаетесь?
Оно работает. any_day - 31 января, мы заменяем day на 1, поэтому 1 января, прибавляем 32 дня, получаем 2 февраля, снова заменяем на day = 1 и получаем 1 февраля. Вычитаем один день, и получаем 31 января. Я не вижу в чем проблема. Какой у тебя день?
Для меня это самый простой способ:
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)
Используя 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 день назад, чтобы получить последний день месяца, который вы хотите.
>>> import datetime
>>> import calendar
>>> date = datetime.datetime.now()
>>> print date
2015-03-06 01:25:14.939574
>>> print date.replace(day = 1)
2015-03-01 01:25:14.939574
>>> print date.replace(day = calendar.monthrange(date.year, date.month)[1])
2015-03-31 01:25:14.939574
Используйте панд!
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
также можно реализовать с помощью pd.series.dt.is_month_endсвязь
У объекта datetime Pandas есть специальный метод для этого: now=datetime.datetime.now(); pd_now = pd.to_datetime(now); print(pd_now.days_in_month)
Чтобы получить последнюю произвольную единицу месяца, см. этот ответ.
import calendar
from time import gmtime, strftime
calendar.monthrange(int(strftime("%Y", gmtime())), int(strftime("%m", gmtime())))[1]
Выход:
31
Это напечатает последний день текущего месяца. В этом примере это было 15 мая 2016 года. Таким образом, ваш результат может быть другим, но он будет таким же, как и в текущем месяце. Отлично, если вы хотите проверить последний день месяца, запустив ежедневное задание cron.
Итак:
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
Если это не последний день месяца.
Я предпочитаю этот путь
import datetime
import calendar
date=datetime.datetime.now()
month_end_date=datetime.datetime(date.year,date.month,1) + datetime.timedelta(days=calendar.monthrange(date.year,date.month)[1] - 1)
Чтобы получить последнюю дату месяца, мы делаем что-то вроде этого:
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), чтобы все знали, что происходит.
Вы можете рассчитать дату окончания самостоятельно. простая логика - вычесть день из 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())
Он вернет дату окончания этого месяца. Передайте в эту функцию любую дату. возвращает дату окончания этого месяца.
если вы хотите использовать внешнюю библиотеку, проверьте http://crsmithdev.com/arrow/
Затем U может получить последний день месяца с помощью:
import arrow
arrow.utcnow().ceil('month').date()
Это возвращает объект даты, с которым вы затем можете манипулировать.
Самый простой способ (без импорта календаря) - получить первый день следующего месяца, а затем вычесть из него день.
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, но и вся дата YYYYMMDD).
почему бы вам не захотеть import calendar?
Потому что это быстрее. Я изменил свой ответ выше, чтобы включить это.
@Vishal, вы правильно поняли концепцию, но следующая строка не была: `` dt.datetime (thisDate.year, (thisDate + relativedelta (months = 1)). Month, 1) - dt.timedelta (days = 1) ` `` Особенно, если месяц находится в конце года. попробуйте вместо этого `` last_date_of_month = \ first_date_of_month + relativedelta (months = 1) - relativedelta (days = 1) ''
Если вы перейдете в диапазон дат, вы можете использовать это:
def last_day_of_month(any_days):
res = []
for any_day in any_days:
nday = any_day.days_in_month -any_day.day
res.append(any_day + timedelta(days=nday))
return res
Вот решение на основе лямбда-выражений Python:
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).
В приведенном ниже коде 'get_last_day_of_month (dt)' даст вам это с датой в строковом формате, например «ГГГГ-ММ-ДД».
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.date(year + int(month/12), month%12+1, 1)-datetime.timedelta(days=1)
Возьмите первый день следующего месяца и вычтите из него день.
вы можете использовать relativedelta
https://dateutil.readthedocs.io/en/stable/relativedelta.html
month_end = <your datetime value within the month> + relativedelta(day=31)
это даст вам последний день.
В 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].
Похоже, это не работает на Python 3.8.1: AttributeError: модуль 'calendar' не имеет атрибута 'monthlen'
@ jeppoo1: да, он отмечен как частный в bpo-28292 - ожидается для недокументированной функции.
Начиная с Python 3.8 это calendar._monthlen.
Для меня это самое простое решение, использующее только стандартную библиотеку datetime:
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)
Самый простой способ - использовать 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)
Вот длинная (легкая для понимания) версия, но учитывающая високосные годы.
ваше здоровье, JK
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 с небольшими изменениями
Самый простой и надежный способ, который я нашел до сих пор:
from datetime import datetime
import calendar
days_in_month = calendar.monthrange(2020, 12)[1]
end_dt = datetime(2020, 12, days_in_month)
Как насчет попроще:
import datetime
now = datetime.datetime.now()
datetime.date(now.year, 1 if now.month==12 else now.month+1, 1) - datetime.timedelta(days=1)
Учитывая, что количество дней в разных месяцах неодинаково, вот стандартное решение, которое работает для каждого месяца.
import datetime
ref_date = datetime.today() # or what ever specified date
end_date_of_month = datetime.strptime(datetime.strftime(ref_date + relativedelta(months=1), '%Y-%m-01'),'%Y-%m-%d') + relativedelta(days=-1)
В приведенном выше коде мы просто добавляем месяц к выбранной дате, а затем переходим к первому дню этого месяца и затем вычитаем день из этой даты.
Я бы на самом деле назвал этот очиститель, за исключением того факта, что он выходит из строя в декабре, когда
today.month + 1 == 13и вы получаетеValueError.