Какой самый элегантный способ получить конец дня (datetime)?

#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 = сегодня вечером), таким образом, мы будем по умолчанию выполнять работу только для текущего дня.

Спасибо всем за их ответы — они были полезны в моих исследованиях.