What's the most elegant way to get the end of the day (datetime)?
<p><strong>UPDATE: I've added a solution to the bottom of this question.</strong></p>

<p>I'm currently writing some reporting code that allows users to optionally specify a date range. The way it works (simplified), is:</p>

<ul>
<li>A user (optionally) specifies a year.</li>
<li>A user (optionally) specifies a month.</li>
<li>A user (optionally) specifies a day.</li>
</ul>

<p>Here's a code snippet, along with comments describing what I'd <strong>like</strong> to do:</p>

<pre><code>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.
</code></pre>

<p>I'm trying to find the most elegant way to write the code above--I've been trying to find a way to do it with timedelta, but can't seem to figure it out. Any advice would be appreciated.</p>

<p>Thanks.</p>

<p><strong>EDIT, ADDING SOLUTION:</strong></p>

<p>After looking at some of the answers here, and not really finding anything extremely elegant, I did some poking around the standard library, and found my current solution (which I like quite well): dateutil.</p>

<p>Here's how I implemented it:</p>

<pre><code>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 ...
</code></pre>

<p>What I like about this implementation, is that python's dateutil.relativedata.relativedata works really well on edge cases. It gets the days/months/years correct. If I have month=12, and do relativedata(months=1), it'll increment the year and set the month to 1 (works nicely).</p>

<p>Also: in the above implementation, if the user specifies none of the optional dates (year, month, or day)--we'll fallback to a nice default (start_time = this morning, stop_time = tonight), that way we'll default to doing stuff for the current day only.</p>

<p>Thanks to everyone for their answers--they were helpful in my research.</p>
