How to truncate the time on a datetime object?

292,153

Solution 1

I think this is what you're looking for...

>>> import datetime
>>> dt = datetime.datetime.now()
>>> dt = dt.replace(hour=0, minute=0, second=0, microsecond=0) # Returns a copy
>>> dt
datetime.datetime(2011, 3, 29, 0, 0)

But if you really don't care about the time aspect of things, then you should really only be passing around date objects...

>>> d_truncated = datetime.date(dt.year, dt.month, dt.day)
>>> d_truncated
datetime.date(2011, 3, 29)

Solution 2

Use a date not a datetime if you dont care about the time.

>>> now = datetime.now()
>>> now.date()
datetime.date(2011, 3, 29)

You can update a datetime like this:

>>> now.replace(minute=0, hour=0, second=0, microsecond=0)
datetime.datetime(2011, 3, 29, 0, 0)

Solution 3

Four years later: another way, avoiding replace

I know the accepted answer from four years ago works, but this seems a tad lighter than using replace:

dt = datetime.date.today()
dt = datetime.datetime(dt.year, dt.month, dt.day)

Notes

  • When you create a datetime object without passing time properties to the constructor, you get midnight.
  • As others have noted, this assumes you want a datetime object for later use with timedeltas.
  • You can, of course, substitute this for the first line: dt = datetime.datetime.now()

Solution 4

You cannot truncate a datetime object because it is immutable.

However, here is one way to construct a new datetime with 0 hour, minute, second, and microsecond fields, without throwing away the original date or tzinfo:

newdatetime = now.replace(hour=0, minute=0, second=0, microsecond=0)

Solution 5

To get a midnight corresponding to a given datetime object, you could use datetime.combine() method:

>>> from datetime import datetime, time
>>> dt = datetime.utcnow()
>>> dt.date()
datetime.date(2015, 2, 3)
>>> datetime.combine(dt, time.min)
datetime.datetime(2015, 2, 3, 0, 0)

The advantage compared to the .replace() method is that datetime.combine()-based solution will continue to work even if datetime module introduces the nanoseconds support.

tzinfo can be preserved if necessary but the utc offset may be different at midnight e.g., due to a DST transition and therefore a naive solution (setting tzinfo time attribute) may fail. See How do I get the UTC time of “midnight” for a given timezone?

Share:
292,153

Related videos on Youtube

Kyle Brandt
Author by

Kyle Brandt

Developer at Grafana Labs - formerly SRE at Stack Overflow.

Updated on May 05, 2022

Comments

  • Kyle Brandt
    Kyle Brandt almost 2 years

    What is a classy way to way truncate a python datetime object?

    In this particular case, to the day. So basically setting hour, minute, seconds, and microseconds to 0.

    I would like the output to also be a datetime object, not a string.

  • Kyle Brandt
    Kyle Brandt about 13 years
    Well the thing is I already do this once, so that might have more overhead then just setting the hour min etc fields in the datetime object.
  • Jochen Ritzel
    Jochen Ritzel about 13 years
    Heh, thats a weird way to do it, you can actually just do d.day etc.
  • ʇsәɹoɈ
    ʇsәɹoɈ about 13 years
    With timezone-aware datetimes, now.date() throws away the tzinfo information.
  • ʇsәɹoɈ
    ʇsәɹoɈ about 13 years
    With a timezone-aware dt, datetime.datetime(dt.year, dt.month, dt.day) throws away the tzinfo information.
  • user1066101
    user1066101 about 13 years
    +1: if you put the replace option first, since that's probably what they want.
  • galarant
    galarant almost 11 years
    if you're looking for just today, you can also do datetime.date.today()
  • Brad M
    Brad M over 9 years
    Note that python 2 and python 3 docs both state that the replace() method returns a datetime object, so the correct incantation would be: dt = datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
  • jfs
    jfs about 9 years
    OP wants datetime, not date object (that you could get using dt.date() call (no need to use the explicit constructor)). The .replace() method may fail if datetime adds nanosecond support. You could use datetime.combine() instead.
  • jfs
    jfs about 9 years
    @ʇsәɹoɈ: here's how you could get the timezone-aware midnight
  • jfs
    jfs about 9 years
    it returns the wrong time (wrong utc offset) if the result time has a different utc offset e.g., due to a DST transition. See How do I get the UTC time of “midnight” for a given timezone?
  • jfs
    jfs about 9 years
    It is incorrect to use tzinfo=now.tzinfo. The tzinfo at midnight may be different e.g., utc offset at 2012-04-01 00:09:00 (9am) in Australia/Melbourne timezone is AEST+10:00 but it is AEDT+11:00 at 2012-04-01 00:00:00 (midnight) -- there is end-of-DST transition on that day. You could use pytz module to fix it, see my answer.
  • 3kstc
    3kstc over 5 years
    @chrisw Why not just write it up in one line datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) ?
  • slfan
    slfan over 5 years
    The same answer has been given before, there is nothing new.
  • Bordotti
    Bordotti about 5 years
    Sorry @slfan but i didn't see any post with your name, even using "search" from browser.
  • slfan
    slfan about 5 years
    Not by me, by zx81 3 years ago. search for datetime.date.today(). If an answer is correct, you should upvote, not answer again.
  • mathtick
    mathtick over 3 years
    cast as datetime
  • ebk
    ebk about 3 years
    and it saves you a lot of typing (compared to replace()).
  • Eiffelbear
    Eiffelbear about 3 years
    this answer is the best but got only 1 upvote, so I upvoted your answer:)
  • Eiffelbear
    Eiffelbear about 3 years
    yeah, let's use dt.floor!!
  • Skippy le Grand Gourou
    Skippy le Grand Gourou almost 3 years
    Note that it’s kind of a pain to convert it back to datetime, though.
  • Seyfi
    Seyfi over 2 years
    With timezone-aware datetimes, tzinfo information does not share between the two.
  • Clej
    Clej about 2 years
    Waw, exactly what I've been looking for the last three hours. Thanks a lot. That's the only solution that enables to pass the truncating frequency as a parameter...
  • cph_sto
    cph_sto almost 2 years
    Well, df['timestamp'] = df['timestamp'].dt.floor('d') will not work. Column name must be different and this is a serious drawback.