955

Is there a built-in method for converting a date to a datetime in Python, for example getting the datetime for the midnight of the given date? The opposite conversion is easy: datetime has a .date() method.

Do I really have to manually call datetime(d.year, d.month, d.day)?

apaderno
  • 28,547
  • 16
  • 75
  • 90
EMP
  • 59,148
  • 53
  • 164
  • 220
  • 208
    Not a silly question; date objects should have a `.datetime()` method; what's silly is that they don't have such a method. – Zags Oct 14 '13 at 03:57
  • 25
    @Zags: or `datetime.from_date()` constructor. – jfs May 09 '15 at 21:36
  • 15
    no it shouldn't a date is a subset of datetime (it's even in the name). It's unambiguous what the date is from a datetime. But the other way round, a date is a block of 24h (usually), so it can have many datetimes. What datetime would come from a date? You can't always so 00:00 because what if that time doesn't exist, like for example daylight savings skipped it. Not so easy# – dalore Mar 01 '17 at 12:33
  • 7
    @Dalore Daylight savings time is between 1am and 2am to avoid exactly the problem you are describing. – Zags Jan 25 '19 at 21:28
  • 3
    @zags it all depends on the timezone source and destination, you can have a daylight savings time change at 1am but that would correspond to a midnight change in some other timezone. and if you're programming which timezone are you using? you could very well end up trying to get a time that doesn't exist. My point still stands, adding a time to a date is not straight forward. Then you also got to consider is your new datetime also timezone aware or naive. – dalore Feb 04 '19 at 15:44
  • Silly question: This problem might occurs when you parse date with a .date in the end as many example shows on this forum. Instead skip this .date() and you can directly do date comparisons like: ''' import datetime.datetime def is_date_earlier_than_date(d1, d2, parse_format) d1_time_obj = datetime.strptime(d1, parse_format) d2_time_obj = datetime.strptime(d2, parse_format) return d1_time_obj < d2_time_obj ''' – Daniel Nelson Jun 23 '22 at 10:08
  • @dalore, wouldn't any other method used to construct a datetime from a date have the same issue? – braaterAfrikaaner Dec 31 '22 at 16:15

14 Answers14

1202

You can use datetime.combine(date, time); for the time, you create a datetime.time object initialized to midnight.

from datetime import date
from datetime import datetime

dt = datetime.combine(date.today(), datetime.min.time())
apaderno
  • 28,547
  • 16
  • 75
  • 90
  • 115
    Thanks. Combined with the fact that time() returns (0,0) I think this comes out the cleanest: `datetime.combine(d, time())` – EMP Dec 21 '09 at 00:38
  • 42
    Just be careful not to let your code get datetime.time() confused with time.time(). Qualified names FTW! – Dustin Dec 21 '09 at 01:19
  • 10
    Yes, good point. Fortunately, combine() raises an exception if you pass in a `time.time` or anything else other than a `datetime.time`. – EMP Dec 21 '09 at 08:36
  • 22
    For midnight, there's a python constant at either datetime.time.min (2.7.1+) or datetime.min.time() (older python) – larham1 Jun 04 '11 at 23:05
  • 2
    @EMP, I've added an example using that technique. Let's hope it gets accepted. – mlissner Oct 16 '13 at 23:00
  • 25
    Good solution, but I don't think datetime.min.time() is the cleanest way of getting a 00:00:00 time. That is because what it does is first retrieving the minimum value representable by datetime and then getting its time component. Incidentally, datetime.min = datetime(MINYEAR, 1, 1, tzinfo=None) and has a time of 00:00:00. However, I think it is cleaner to explicitly create a 00:00:00 time either through time.min or time(0, 0, 0, 0). – Florin Dumitrescu Dec 02 '13 at 14:02
  • 1
    Why importing _date_ at all? `datetime.combine(datetime.today(), datetime.min.time())` – el.atomo Jul 10 '14 at 09:16
  • 3
    @el.atomo because the actual *question* didn't explicitly ask for just today – kaleissin Jan 22 '15 at 11:50
  • 1
    Timezone like this (with variables my_date & my_timezone) : `import datetime; datetime.datetime.combine(my_date, datetime.time(datetime.MINYEAR, 1, 1, tzinfo=my_timezone)` – Mark Chackerian Mar 09 '17 at 19:21
  • I got error named: "AttributeError: module 'datetime' has no attribute 'combine'" – farisfath25 Sep 01 '23 at 10:32
203

There are several ways, although I do believe the one you mention (and dislike) is the most readable one.

>>> import datetime
>>> t=datetime.date.today()
>>> datetime.datetime.fromordinal(t.toordinal())
datetime.datetime(2009, 12, 20, 0, 0)

>>> datetime.datetime(t.year, t.month, t.day)
datetime.datetime(2009, 12, 20, 0, 0)

>>> datetime.datetime(*t.timetuple()[:-4])
datetime.datetime(2009, 12, 20, 0, 0)

and so forth -- but basically they all hinge on appropriately extracting info from the date object and ploughing it back into the suitable ctor or classfunction for datetime.

Alan W. Smith
  • 24,647
  • 4
  • 70
  • 96
Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • 31
    Very good point. `datetime(d.year, d.month, d.day)` does seem much more readable than the accepted answer `datetime.combine(d, datetime.min.time())`. – Simon Elms Jun 28 '14 at 12:03
  • 3
    @SimonTewsi: `midnight = datetime.datetime.combine(d, datetime.time.min)` seems cleaner (suggested by [`@`larham1](http://stackoverflow.com/questions/1937622/convert-date-to-datetime-in-python#comment7273463_1937636)) – jfs May 09 '15 at 21:36
  • 2
    @J. F. Sebastian: It is somewhat cleaner but I agree with Wes Winham in his answer that it requires the reader to know what datetime.time.min represents. Whereas it seems to me that `datetime(d.year, d.month, d.day)` is more obvious (a datetime without the time component). It's a minor point, though, and using a variable name like "midnight" makes it obvious what the result is even if the reader doesn't know what `datetime.time.min` represents. – Simon Elms May 10 '15 at 23:48
  • 7
    @SimonTewsi: note: [`datetime(d.year, d.mont, d.day)` is not only more obvious, it is faster too](http://stackoverflow.com/a/33276464/4279). – jfs Dec 04 '15 at 11:49
  • Another variant just for fun: `datetime.datetime(*(getattr(d, attr) for attr in ("year", "month", "day")))`. That way, you can easily modify the list of attributes that matter to you. Hours could be added dynamically for example. – Guimoute Mar 12 '22 at 20:31
  • `d = d.replace(hour=0, minute=0, second=0, microsecond=0)` is possible too but I hate this approach as it forces you to know all the keywords and set them to 0 manually. It works for 4 parameters, but if there was 12 we would start to question this approach. – Guimoute Mar 12 '22 at 20:34
124

The accepted answer is correct, but I would prefer to avoid using datetime.min.time() because it's not obvious to me exactly what it does. If it's obvious to you, then more power to you. I also feel the same way about the timetuple method and the reliance on the ordering.

In my opinion, the most readable, explicit way of doing this without relying on the reader to be very familiar with the datetime module API is:

from datetime import date, datetime
today = date.today()
today_with_time = datetime(
    year=today.year, 
    month=today.month,
    day=today.day,
)

That's my take on "explicit is better than implicit."

Wes Winham
  • 2,080
  • 4
  • 18
  • 16
  • And a hat tip to Kyle Gibson for the original idea: http://stackoverflow.com/users/513197/kyle-gibson – Wes Winham Mar 19 '14 at 21:25
  • 1
    I agree with your sentiment. All these other answers with timetuple and asterisks and min.time() require research, and the Python way of handling dates and datetimes is a bit cumbersome anyway compared to the ease that Microsoft handles them in VBA. It as simple as wrapping them in hash symbols in that language. But I digress. – Bobort Oct 27 '16 at 15:46
  • 2
    ```d2dt = lambda date: datetime(year=date.year, month=date.month, day=date.day)``` on-liner function of this answer. – aliqandil May 09 '17 at 15:29
  • 2
    This works, but you may need the `datetime` to be timezone aware. In which case, you can `import pytz` and then add `tzinfo=pytz.timezone('Europe/London')`, for example. – alstr May 21 '20 at 09:28
59

You can use the date.timetuple() method and unpack operator *.

args = d.timetuple()[:6]
datetime.datetime(*args)
Nam G VU
  • 33,193
  • 69
  • 233
  • 372
teepark
  • 1,795
  • 13
  • 10
  • 5
    This is useful for my situation. I don't know if it's a date or a datetime I'm being passed, and it's not very pythonic to check which class it is. This method looks like it will work for both datetime and date objects. – Gattster Oct 14 '10 at 20:51
  • I used to use this before discovering `datetime.combine` via @kiamlaluno's answer. I think it's fairly pythonic, especially given constructing a `datetime.time` object is likely to look something like `datetime.time(*map(int,"H:M:S".split(":")))` anyway... – Tom Jan 29 '11 at 01:45
  • useful in many other contexts too for example truncating seconds, minutes, or hours by indexing down to as low as [:3] and even lower if you pad with + (n,) as in : `dt.datetime(*(dt.datetime.utcnow().timetuple()[:2] + (1, )))` – Thomas Browne Aug 30 '21 at 13:10
  • Nice approach, for convenience here's the [documentation](https://docs.python.org/3/library/datetime.html#datetime.date.timetuple) for `timetuple()` – Andrew Richards Sep 28 '22 at 16:18
13

Today being 2016, I think the cleanest solution is provided by pandas Timestamp:

from datetime import date
import pandas as pd
d = date.today()
pd.Timestamp(d)

Timestamp is the pandas equivalent of datetime and is interchangable with it in most cases. Check:

from datetime import datetime
isinstance(pd.Timestamp(d), datetime)

But in case you really want a vanilla datetime, you can still do:

pd.Timestamp(d).to_datetime()

Timestamps are a lot more powerful than datetimes, amongst others when dealing with timezones. Actually, Timestamps are so powerful that it's a pity they are so poorly documented...

kadee
  • 8,067
  • 1
  • 39
  • 31
  • Good call. Came here as I thought it would need a python datetime solution to a pandas DatetimeIndex issue. Shame pandas doesn't have the concept of a DateIndex. – Little Bobby Tables Nov 16 '16 at 17:13
  • 34
    why do we require a heavyweight package just to do datetime manipulation, even in 2016?! – cowbert Jul 24 '18 at 00:50
  • 4
    Bringing out pandas for this is like taking a hammer to an ant – Dee S May 26 '20 at 02:35
  • using pandas to solve this problem is major overkill – Tomasz Giba Apr 20 '21 at 11:25
  • 1
    `to_datetime()` has been [deprecated](https://pandas.pydata.org/pandas-docs/version/0.22/generated/pandas.Timestamp.to_datetime.html), so use `to_pydatetime()` instead. This is a good answer for those that already have to use Pandas for reasons and need datetime format as well. – NumesSanguis Aug 08 '22 at 08:41
9

you can also use

date = datetime.utcnow().date()
dt = datetime.fromisoformat(date.isoformat())

print(dt)

datetime.datetime(2021, 11, 15, 0, 0)

NONONONONO
  • 612
  • 1
  • 6
  • 10
7

One way to convert from date to datetime that hasn't been mentioned yet:

from datetime import date, datetime
d = date.today()
datetime.strptime(d.strftime('%Y%m%d'), '%Y%m%d')
kadee
  • 8,067
  • 1
  • 39
  • 31
  • 3
    True, but converting ints to strings and back is probably rather more expensive than working directly with ints, so I'd only use this one for low-volume processing – Egor Kraev Mar 09 '18 at 13:02
5

You can use easy_date to make it easy:

import date_converter
my_datetime = date_converter.date_to_datetime(my_date)
Raphael Amoedo
  • 4,233
  • 4
  • 28
  • 37
4

Do I really have to manually call datetime(d.year, d.month, d.day)

No, you'd rather like to call

date_to_datetime(dt)

which you can implement once in some utils/time.py in your project:

from typing import Optional
from datetime import date, datetime

def date_to_datetime(
    dt: date,
    hour: Optional[int] = 0,
    minute: Optional[int] = 0, 
    second: Optional[int] = 0) -> datetime:

    return datetime(dt.year, dt.month, dt.day, hour, minute, second)
Sławomir Lenart
  • 7,543
  • 4
  • 45
  • 61
2

An alternative to toisoformat/fromisoformat: you can use date.toordinal and datetime.fromordinal:

import datetime

start_date = datetime.date(1991, 2, 20)
start_date_midnight = datetime.datetime.fromordinal(start_date.toordinal())

I suspect this is more efficient than converting to/from a string.

You can test this process as so:

def test_datetime_from_date():
    for x in range(1,1000000):
        date_ = datetime.date.fromordinal(x)
        datetime_ = datetime.datetime.fromordinal(date_.toordinal())
        datetime_iso_date, t, datetime_iso_time = datetime_.isoformat().partition("T")
        assert datetime_iso_date == date_.isoformat()
Jack Deeth
  • 3,062
  • 3
  • 24
  • 39
1

To make dt timezone aware datetime (with Django timezone util):

from django.utils import timezone


timezone.now().replace(*(*dt.timetuple()[:6], 0))
dqd
  • 1,501
  • 14
  • 10
0

You can use this class:

import time 

import datetime

class TimingClass():

    def __init__(self):

        self.YEAR        = datetime.date.today().year
        self.MONTH       = datetime.date.today().month
        self.DATE        = datetime.date.today().day
        self.HOUR        = datetime.datetime.now().hour
        self.MINUTE      = datetime.datetime.now().minute
        self.SECONDS     = datetime.datetime.now().second
        
        self.TODAY       = datetime.date.today()
        self.YESTERDAY   = datetime.datetime.strftime( (self.TODAY - datetime.timedelta(days = 1)) , '%Y-%m-%d')
        self.TOMORROW   = datetime.datetime.strftime( (self.TODAY + datetime.timedelta(days = 1)) , '%Y-%m-%d')
        
        self.TODAY_datetime = datetime.datetime.combine(datetime.date.today(), datetime.datetime.min.time())
ont.rif
  • 1,068
  • 9
  • 18
-11

If you need something quick, datetime_object.date() gives you a date of a datetime object.

apaderno
  • 28,547
  • 16
  • 75
  • 90
Bassdread
  • 350
  • 2
  • 6
-15

I am a newbie to Python. But this code worked for me which converts the specified input I provide to datetime. Here's the code. Correct me if I'm wrong.

import sys
from datetime import datetime
from time import mktime, strptime

user_date = '02/15/1989'
if user_date is not None:
     user_date = datetime.strptime(user_date,"%m/%d/%Y")
else:
     user_date = datetime.now()
print user_date
napier
  • 1