221

I am trying to filter a DateTimeField comparing with a date. I mean:

MyObject.objects.filter(datetime_attr=datetime.date(2009,8,22))

I get an empty queryset list as an answer because (I think) I am not considering time, but I want "any time".

Is there an easy way in Django for doing this?

I have the time in the datetime setted, it is not 00:00.

Serjik
  • 10,543
  • 8
  • 61
  • 70
Xidobix
  • 2,538
  • 3
  • 17
  • 10

20 Answers20

131

Such lookups are implemented in django.views.generic.date_based as follows:

{'date_time_field__range': (datetime.datetime.combine(date, datetime.time.min),
                            datetime.datetime.combine(date, datetime.time.max))} 

Because it is quite verbose there are plans to improve the syntax using __date operator. Check "#9596 Comparing a DateTimeField to a date is too hard" for more details.

Davide Pastore
  • 8,678
  • 10
  • 39
  • 53
Piotr Czapla
  • 25,734
  • 24
  • 99
  • 122
  • 5
    Using with range: `Q(created__gte=datetime.combine(created_value, time.min))` – Dingo Mar 01 '12 at 07:55
  • 10
    Looks like it will land in Django 1.9: https://github.com/django/django/commit/44f3ee77166bd5c0e8a4604f2d96015268dce100#diff-5e313dd49d68a0a867d830a7f442d2a6R2471 – amjoconn Oct 22 '15 at 19:45
  • 25
    New in Django 1.9: `Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))` – Non Apr 13 '17 at 08:54
119
YourModel.objects.filter(datetime_published__year='2008', 
                         datetime_published__month='03', 
                         datetime_published__day='27')

// edit after comments

YourModel.objects.filter(datetime_published=datetime(2008, 03, 27))

doest not work because it creates a datetime object with time values set to 0, so the time in database doesn't match.

zalew
  • 10,171
  • 3
  • 29
  • 32
  • thx for the answer! the first alternative doesn't work with datetimefields. The second alternative works ;). If someone knows another method please answer – Xidobix Aug 23 '09 at 04:14
  • http://docs.python.org/library/datetime.html#datetime-objects using datetime() from datetime module hrs,mins,secs is optional. the second is from a working project with vars replaced, you can look in the docs it's correct – zalew Aug 23 '09 at 04:19
  • i know it is optional, the problem is that my datetimefield has the time setted, it is not 00:00 – Xidobix Aug 23 '09 at 04:22
  • "the first alternative doesn't work with datetimefields." it'd be quite surprising, as datetime.datetime() returns a datetime object http://www.djangoproject.com/documentation/0.96/models/basic/ check the model definition and examples: pub_date = models.DateTimeField() pub_date=datetime(2005, 7, 30) – zalew Aug 23 '09 at 04:25
  • "i know it is optional, the problem is that my datetimefield has the time setted, it is not 00:00" Oh, now i get it. Yes, with no time arguments it sets to 00, so it does not return :) – zalew Aug 23 '09 at 04:26
  • I'm using django 1.9 and python 2.7. This solution did not work for me. I can filter on the year, but not the month or day. Thus `YourModel.objects.filter(datetime_published__year='2008')` is the only thing that worked. Bug in django 1.9? – Jim Paul Apr 05 '16 at 03:25
  • Simple is better than complex =) – wrivas Feb 15 '19 at 15:01
117

Here are the results I got with ipython's timeit function:

from datetime import date
today = date.today()

timeit[Model.objects.filter(date_created__year=today.year, date_created__month=today.month, date_created__day=today.day)]
1000 loops, best of 3: 652 us per loop

timeit[Model.objects.filter(date_created__gte=today)]
1000 loops, best of 3: 631 us per loop

timeit[Model.objects.filter(date_created__startswith=today)]
1000 loops, best of 3: 541 us per loop

timeit[Model.objects.filter(date_created__contains=today)]
1000 loops, best of 3: 536 us per loop

contains seems to be faster.

Moreno
  • 1,567
  • 1
  • 12
  • 15
  • This solution seems to be the most recent. I am surprised it got 4 upvotes, because when I try the `contains` solution, I get the error message: `Unable to get repr for ` – Houman Jan 13 '13 at 13:13
  • I recheck and update the results today and I don't think your error it's caused by the `__contains` filter. But if you're running into issues you should try the [django docs example](https://docs.djangoproject.com/en/dev/ref/models/querysets/#filter) which is using `__gte`. – Moreno Jan 14 '13 at 19:11
  • 7
    The __contains method works fine for me. I think this is probably the best answer since it provides performance comparisons. I've voted more than one, but I'm surprised it doesn't have more upvotes. – RobotHumans Nov 04 '13 at 03:28
77

Now Django has __date queryset filter to query datetime objects against dates in development version. Thus, it will be available in 1.9 soon.

onurmatik
  • 5,105
  • 7
  • 42
  • 67
51
Mymodel.objects.filter(date_time_field__contains=datetime.date(1986, 7, 28))

the above is what I've used. Not only does it work, it also has some inherent logical backing.

animuson
  • 53,861
  • 28
  • 137
  • 147
kettlehell
  • 519
  • 4
  • 2
48

As of Django 1.9, the way to do this is by using __date on a datetime object.

For example: MyObject.objects.filter(datetime_attr__date=datetime.date(2009,8,22))

Andrew B.
  • 1,225
  • 1
  • 13
  • 18
  • 1
    I read several answers stating about the '__date' , but I found all of them quite sophisticated, until I read your answer. It's simple and straight to the point. – Prajwal Kulkarni Aug 17 '20 at 10:44
27

This produces the same results as using __year, __month, and __day and seems to work for me:

YourModel.objects.filter(your_datetime_field__startswith=datetime.date(2009,8,22))
mhost
  • 6,930
  • 5
  • 38
  • 45
  • 19
    looks like this one turns date object to string and do a string comparison of dates therefore forces db to do a full table scan. for big tables this one kill your performance – yilmazhuseyin Feb 08 '11 at 12:55
8

assuming active_on is a date object, increment it by 1 day then do range

next_day = active_on + datetime.timedelta(1)
queryset = queryset.filter(date_created__range=(active_on, next_day) )
davidj411
  • 985
  • 10
  • 10
8

You can do like this

MyObject.objects.filter(datetime_field__date=datetime.date(2009,8,22))

or if you want to filter between 2 dates

MyObject.objects.filter(
    datetime_field__date__range=(datetime.date(2009,8,22), datetime.date(2009,9,22))
)
jeevu94
  • 477
  • 4
  • 16
7

You can filter by the Date as per as the date format is the same with your django date format. Default format is ISO YYYY-MM-DD

target_date = "2009-08-22"
qs = MyObject.objects.filter(datetime_attr__date=target_date)
salafi
  • 387
  • 6
  • 9
4

There's a fantastic blogpost that covers this here: Comparing Dates and Datetimes in the Django ORM

The best solution posted for Django>1.7,<1.9 is to register a transform:

from django.db import models

class MySQLDatetimeDate(models.Transform):
    """
    This implements a custom SQL lookup when using `__date` with datetimes.
    To enable filtering on datetimes that fall on a given date, import
    this transform and register it with the DateTimeField.
    """
    lookup_name = 'date'

    def as_sql(self, compiler, connection):
        lhs, params = compiler.compile(self.lhs)
        return 'DATE({})'.format(lhs), params

    @property
    def output_field(self):
        return models.DateField()

Then you can use it in your filters like this:

Foo.objects.filter(created_on__date=date)

EDIT

This solution is definitely back end dependent. From the article:

Of course, this implementation relies on your particular flavor of SQL having a DATE() function. MySQL does. So does SQLite. On the other hand, I haven’t worked with PostgreSQL personally, but some googling leads me to believe that it does not have a DATE() function. So an implementation this simple seems like it will necessarily be somewhat backend-dependent.

macmania314
  • 457
  • 2
  • 18
Dan Gayle
  • 2,277
  • 1
  • 24
  • 38
3
MyObject.objects.filter(datetime_attr__date=datetime.date(2009,8,22))
2

Here is an interesting technique-- I leveraged the startswith procedure as implemented with Django on MySQL to achieve the result of only looking up a datetime through only the date. Basically, when Django does the lookup in the database it has to do a string conversion for the DATETIME MySQL storage object, so you can filter on that, leaving out the timestamp portion of the date-- that way %LIKE% matches only the date object and you'll get every timestamp for the given date.

datetime_filter = datetime(2009, 8, 22) 
MyObject.objects.filter(datetime_attr__startswith=datetime_filter.date())

This will perform the following query:

SELECT (values) FROM myapp_my_object \ 
WHERE myapp_my_object.datetime_attr LIKE BINARY 2009-08-22%

The LIKE BINARY in this case will match everything for the date, no matter the timestamp. Including values like:

+---------------------+
| datetime_attr       |
+---------------------+
| 2009-08-22 11:05:08 |
+---------------------+

Hopefully this helps everyone until Django comes out with a solution!

bbengfort
  • 5,254
  • 4
  • 44
  • 57
  • Ok, so this does appear to be the same answer as mhost and kettlehell above, but with more description of what is happening in the backend. At least you have a reason to use contains or startswith along with the date() attribute of the datetime! – bbengfort Aug 20 '12 at 17:56
1

See the article Django Documentation

ur_data_model.objects.filter(ur_date_field__gte=datetime(2009, 8, 22), ur_date_field__lt=datetime(2009, 8, 23))
shahjapan
  • 13,637
  • 22
  • 74
  • 104
0
Model.objects.filter(datetime__year=2011, datetime__month=2, datetime__day=30)
Community
  • 1
  • 1
Skylar Saveland
  • 11,116
  • 9
  • 75
  • 91
0

Hm.. My solution is working:

Mymodel.objects.filter(date_time_field__startswith=datetime.datetime(1986, 7, 28))
satels
  • 789
  • 6
  • 13
0

In Django 1.7.6 works:

MyObject.objects.filter(datetime_attr__startswith=datetime.date(2009,8,22))
FACode
  • 951
  • 1
  • 11
  • 19
0
person = Profile.objects.get(id=1)

tasks = Task.objects.filter(assigned_to=person, time_stamp__year=person.time_stamp.utcnow().year)

all my model do have time_stamp so I used the person objects to obtain the current year

0

You can filter between some day ranges

2016-01-01 00:00:00 <--> 2016-04-01 23:59:59.99999

User.objects.filter(date_joined__gte=datetime.combine(datetime.strptime('2016- 
01-01', '%Y-%d-%m'), datetime.min.time()), 
date_joined__lte=datetime.combine(datetime.strptime('2016-04-01', '%Y-%d-%m'), 
datetime.max.time())).count()

2016-01-01 00:00:00 <--> 2016-01-14 00:00:00

User.objects.filter(date_joined__gte='2016-01-01', date_joined__lte='2016-1-14').count()
Umar Asghar
  • 3,808
  • 1
  • 36
  • 32
-1

Just as simple as that if you have a datetimefield your can use datetime.date.today()

context['now'] = Mymodel.objects.filter(date_time_field=datetime.date.today())