#python #python-datetime
#python #python-datetime
Вопрос:
В настоящее время я пишу некоторый код отчетности, который позволяет пользователям необязательно указывать диапазон дат. Способ, которым это работает (упрощенный), заключается в:
- Пользователь (необязательно) указывает год.
- Пользователь (необязательно) указывает месяц.
- Пользователь (необязательно) указывает день.
Вот фрагмент кода вместе с комментариями, описывающими, что я хотел бы сделать:
from datetime import datetime, timedelta
# ...
now = datetime.now()
start_time = now.replace(hour=0, minute=0, second=0, microsecond=0)
stop_time = now
# If the user enters no year, month, or day--then we'll simply run a
# report that only spans the current day (from the start of today to now).
if options['year']:
start_time = start_time.replace(year=options['year'], month=0, day=0)
stop_time = stop_time.replace(year=options['year'])
# If the user specifies a year value, we should set stop_time to the last
# day / minute / hour / second / microsecond of the year, that way we'll
# only generate reports from the start of the specified year, to the end
# of the specified year.
if options['month']:
start_time = start_time.replace(month=options['month'], day=0)
stop_time = stop_time.replace(month=options['month'])
# If the user specifies a month value, then set stop_time to the last
# day / minute / hour / second / microsecond of the specified month, that
# way we'll only generate reports for the specified month.
if options['day']:
start_time = start_time.replace(day=options['day'])
stop_time = stop_time.replace(day=options['day'])
# If the user specifies a day value, then set stop_time to the last moment of
# the current day, so that reports ONLY run on the current day.
Я пытаюсь найти самый элегантный способ написания кода выше — я пытался найти способ сделать это с помощью timedelta, но, похоже, не могу понять это. Любые советы будут оценены.
Комментарии:
1. Конец любого данного дня всегда будет
23:59:59.999
2. @MarcB Это неверно во время високосных секунд, которые равны 23:59:60.
3. Верно, но тогда достаточно легко добавить проверки за июнь / декабрь — они достаточно редки, чтобы не беспокоить большинство систем. Или настройте логику даты так, чтобы
< tomorrow:00:00:00
вместо<= today:23:59:59
4. Вы должны добавить свое решение в качестве ответа
Ответ №1:
Чтобы установить stop_time
, продвиньте start_time
один год, месяц или день в зависимости от обстоятельств, затем вычтите единицу timedelta(microseconds=1)
if options['year']:
start_time = start_time.replace(year=options['year'], month=1, day=1)
stop_time = stop_time.replace(year=options['year'] 1)-timedelta(microseconds=1)
elif options['month']:
start_time = start_time.replace(month=options['month'], day=1)
months=options['month']%12 1
stop_time = stop_time.replace(month=months,day=1)-timedelta(microseconds=1)
else:
start_time = start_time.replace(day=options['day'])
stop_time = stop_time.replace(day=options['day']) timedelta(days=1,microseconds=-1)
Ответ №2:
Использование dict.get
может упростить ваш код. Это немного чище, чем использование объектов datetime.replace и timedelta.
Вот что поможет вам начать:
from datetime import datetime
options = dict(month=5, day=20)
now = datetime.now()
start_time = datetime(year=options.get('year', now.year),
month=options.get('month', 1),
day=options.get('day', 1)
hour=0,
minute=0,
second=0)
stop_time = datetime(year=options.get('year', now.year),
month=options.get('month', now.month),
day=options.get('day', now.day),
hour=now.hour,
minute=now.minute,
second=now.second)
Комментарии:
1. Это было полезно, однако, поиграв, я обнаружил, что dateutil.relativedelta выглядит более элегантно. Я обновил свой ответ с помощью этого метода, если у вас есть какие-либо отзывы.
Ответ №3:
today = datetime.date.today()
begintime = today.strftime("%Y-%m-%d 00:00:00")
endtime = today.strftime("%Y-%m-%d 23:59:59")
Ответ №4:
from datetime import datetime, date, timedelta
def get_current_timestamp():
return int(datetime.now().timestamp())
def get_end_today_timestamp():
# get 23:59:59
result = datetime.combine(date.today() timedelta(days=1), datetime.min.time())
return int(result.timestamp()) - 1
def get_datetime_from_timestamp(timestamp):
return datetime.fromtimestamp(timestamp)
end_today = get_datetime_from_timestamp(get_end_today_timestamp())
Ответ №5:
date = datetime.strftime('<input date str>')
date.replace(hour=0, minute=0, second=0, microsecond=0) # now we get begin of the day
date = timedelta(days=1, microseconds=-1) # now end of the day
Ответ №6:
Просмотрев некоторые ответы здесь и не найдя ничего особенно элегантного, я немного покопался в стандартной библиотеке и нашел свое текущее решение (которое мне очень нравится) : dateutil
.
Вот как я это реализовал:
from datetime import date
from dateutil.relativedelta import relativedelta
now = date.today()
stop_time = now relativedelta(days=1)
start_time = date(
# NOTE: I'm not doing dict.get() since in my implementation, these dict
# keys are guaranteed to exist.
year = options['year'] or now.year,
month = options['month'] or now.month,
day = options['day'] or now.day
)
if options['year']:
start_time = date(year=options['year'] or now.year, month=1, day=1)
stop_time = start_time relativedelta(years=1)
if options['month']:
start_time = date(
year = options['year'] or now.year,
month = options['month'] or now.month,
day = 1
)
stop_time = start_time relativedelta(months=1)
if options['day']:
start_time = date(
year = options['year'] or now.year,
month = options['month'] or now.month,
day = options['day'] or now.day,
)
stop_time = start_time relativedelta(days=1)
# ... do stuff with start_time and stop_time here ...
Что мне нравится в этой реализации, так это то, что python dateutil.relativedata.relativedata
действительно хорошо работает в крайних случаях. Он получает правильные дни / месяцы / годы. Если у меня есть month=12
и relativedata(months=1)
есть, он увеличит год и установит месяц на 1 (работает хорошо).
Также: в приведенной выше реализации, если пользователь не указывает ни одну из необязательных дат (год, месяц или день) — мы вернемся к хорошему значению по умолчанию (start_time = сегодня утром, stop_time = сегодня вечером), таким образом, мы будем по умолчанию выполнять работу только для текущего дня.
Спасибо всем за их ответы — они были полезны в моих исследованиях.