How to compare two Datetime field in Django - django

I have used datetime.datetime.now() for storing datefield in my model which is saved as 2016-06-27 15:21:17.248951+05:30. Now I want to compare the datefield with the datetime value getting from the frontend, like Thu May 26 2016 00:00:00 GMT 0530 (IST). How should Django query the model to compare both datefields?
# models.py
datefield = models.DateTimeField(blank=True,null=True)
I have tried converting datefield getting from frontend by using split() and remove() function of Python to create it in format as 2016-06-27 13:25:35.
But still no solution and getting Null even I am comparing same date value like this (2016-06-27 13:25:35) value with this (2016-06-27 12:36:34.898593+00) value, as date in both values are same.
I have checked it using simple Django query as follows:
company_details.objects.filter(datefield=datefield).only('par1','par2',...)

Once you have converted the date from the front end into a ISO format like 2016-06-27 13:25:35, you can use it to query the model with one of these
Model.objects.filter(date_created__startswith=today)
Model.objects.filter(date_created__contains=today)
It works too if you are only looking to filter for a certain date like 2016-06-27.
Model.objects.filter(date_created__startswith=date(2016, 6, 27))
To parse the date string you are getting from the frontend, there are two options.
If your date string is well-formatted, you can simply parse it with for example datestring.strftime('%Y-%m-%d %H:%M:%S') to get a datetime() object.
If your date string is unreliable and may be formatted in different ways that you can't predict, I would recommend to using DateUtil. It comes with a parser that will try to convert any string into a datetime, for example
>>> from dateutil.parser import parse
>>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True)
(datetime.datetime(2011, 1, 1, 8, 21), (u'Today is ', u' ', u'at '))

Compare the date and time like this. Just give the variable like this '%Y-%m-%d %H:%M:%S' as much you want.
In [1]: from datetime import datetime
In [2]: past = datetime.now()
In [3]: present = datetime.now()
In [4]: present.strftime('%Y-%m-%d %H:%M:%S') == past.strftime('%Y-%m-%d %H:%M:%S')
Out[17]: False
In [5]: present.strftime('%Y-%m-%d %H:%M') == past.strftime('%Y-%m-%d %H:%M')
Out[2]: True
If you want to compare only the date use like this.
if present.date() == past.date():
#Do your stuff

Related

Filtering django model using date

I need to filter some data from models using the date. I see some posts that speaks about ranges but I just want to see for example the rows of my table from the 22/04/2020, in other words, just one day.
Reading the documentation, I understood that I have to do the following,
import datetime
prueba = DevData.objects.order_by('-data_timestamp').filter(data_timestamp=datetime.date(2020, 4, 22))
prueba = loads(serializers.serialize('json', prueba))
for p in prueba:
print(p)
But appears the following warning:
RuntimeWarning: DateTimeField DevData.data_timestamp received a naive datetime (2020-04-22 00:00:00) while time zone support is active.
And the list appears empty. I think the problem is that is just filtering using 2020-04-22 00:00:00 and not all the day. How can I fix this?
Between two dates is working but just one day I don't know how to do it.
import datetime
start_date = datetime.date(2020, 4, 22)
end_date = datetime.date(2020, 4, 23)
prueba = DevData.objects.order_by('-data_timestamp').filter(data_timestamp__range= (start_date,end_date))
PD: I have info rows in this day.
Thank you very much.
You can make use of the __date lookup [Django-doc] to filter on the date of the timestamp:
import datetime
prueba = DevData.objects.order_by(
'-data_timestamp'
).filter(
data_timestamp__date=datetime.date(2020, 4, 22)
)

Filter data for a given day (timezone aware datetime) from postgres directly

I want to filter all data for the following day (which is timezone aware).
Assumptions:
server data is in a specific timezone
client query is coming from different timezone
My current approach is:
date = received_date_timezone_aware # any time of that day
lower = date.replace(hour=0,minute=0,second=0)
upper = lower + datetime.timedelta(day=1)
data = Model.objects.filter(date__gte=lower, date__lt=upper)
Question
Is there a direct solution to this using django orm or raw query?
Note: I wanted to know if there is a better way which can save me a few lines of code of manipulating the datetime myself
No, there isn't. However what you're doing isn't the best way to do it either as using replace can result in some oddities with day lights savings. I'd recommend using lower = received_date_timezone_aware.date().
This assumes Model.date is a DateField. If it's a datetime, then do this:
from datetime import time, datetime
def min_datetime(date: datetime, tz):
"""Get the min datetime of the given datetime and timezone.
:return datetime: Minimum datetime of the given date.
"""
return tz.localize(
datetime.combine(date.astimezone(tz).date(), time.min))
def max_datetime(date: datetime, tz):
"""Get the max datetime of the given datetime and timezone.
:return datetime: Maximum datetime of the given date.
"""
return tz.localize(
datetime.combine(date.astimezone(tz).date(), time.max))
Model.objects.filter(date__range=(min_datetime(value, tz), max_datetime(value, tz)))

conversion of datetime Field to string in django queryset.values_list()

I have a queryset like:
qs = MyModel.objects.filter(name='me').values_list('activation_date')
here activation_date is DateTimeField in models.
When I download excel sheet from this qs I am not getting activation date in string format.
How can I convert this field('activation_date') in string or how to typecast it in qs?
https://docs.djangoproject.com/en/2.2/ref/models/fields/#datetimefield
A date and time, represented in Python by a datetime.datetime instance.
You can get a string representation of a DateTimeField casting it directly:
str(obj)
# obj = qs[0][0] ? or qs[0][1] ?
You'll get result like this (in this example I use datetime.datetime.now() since a DateTimeField is represented by datetime.datetime is the same behavior):
>>> now = datetime.datetime.now()
>>> str(now)
'2013-06-26 00:14:26.260524'
if you want less information or formatted in other mode you can use strftime() function for format them. see:
>>> now.strftime('%Y-%m-%d %H:%M')
'2013-06-26 00:14'
extra() is an old API that Django aims to deprecate at some point in the future. I would avoid using it.
Try the following instead:
from django.db.models import F, Func, Value, CharField
qs.annotate(
formatted_date=Func(
F('date'),
Value('dd.MM.yyyy hh:mm'),
function='to_char',
output_field=CharField()
)
)
This works only with a database that supports the to_char date type formatting function. Postgres provides this function by default.
If you use a MSSQL backend you could swap to_char with FORMAT.
For MySQL use DATE_FORMAT.
For Oracle consult their
documentation, etc.
After the queryset is evaluated this will add the annotation formatted_date to each object in the queryset that is returned.
extra is deprecated in Django 2.0
That's why I think the best solution to get a stringified datetime is:
foo_bar = FooBarModel.objects.annotate(
str_datetime=Cast(
TruncSecond('some_datetime_field', DateTimeField()), CharField()
)
).values('str_datetime').first()
The result is:
foo_bar.str_datetime:
(str)'2014-03-28 15:36:55'
Also I'd like to mention that you can format it as well in any way you want like:
from django.db.models import Value
foo_bar = FooBarModel.objects.annotate(
day=Cast(ExtractDay('some_datetime_field'), CharField()),
hour=Cast(ExtractHour('some_datetime_field'), CharField()),
str_datetime=Concat(
Value('Days: '), 'day', Value(' Hours: '), 'hour',
output_field=CharField()
)
).values('str_datetime').first()
The result is:
foo_bar.str_datetime:
(str)'Days: 28 Hours: 15'
If you are using Postgres, you can do it like this (date format options here). The solution is database dependent, but it sure beats looping though a long list in Python land after your perform the query.
qs = MyModel.objects.filter(name='me')
qs = qs.extra(select={'datestr':"to_char(activation_date, 'YYYY-MM-DD HH24:MI:SS')"})
qs = qs.values_list('datestr')
I am sure MySQL has some equivalent function as Postgres's to_char, but you'll have to find that on your own as I am not a MySQL guy.
Very surprised to see that no one suggested the cast to a simple TextField (note, I'm using Postgres so I can't confirm for other RDBMSes):
from django.db.models.functions import Cast
from django.db.models import TextField
queryset = FooBarModel.objects.values(my_datetime=Cast('some_datetime_field', TextField()))
foo_bar = queryset.first()
foo_bar['my_datetime']
>>> u'2019-10-03 17:59:37.979578+00'
It similarly also works fine for nested fields:
queryset = FooBarModel.objects.values(Cast('baz__some_datetime_field', TextField()))
Alternatively, a custom Func can also be used (also specific to Postgres here, but can be modified for any other RDBMS):
class FullDateTimeCast(Func):
"""
Coerce an expression to a new field type.
"""
function = 'TO_CHAR'
template = '%(function)s(%(expressions)s, \'FMDay, Month DD, YYYY at HH12:MI:SS AM\')'
queryset = FooBarModel.objects.values(my_datetime=FullDateTimeCast('some_datetime_field', TextField()))
foo_bar = queryset.first()
foo_bar['my_datetime']
>>> u' Thursday, October 03, 2019 at 17:59:37 PM'
qs = MyModel.objects.filter(name='me')
qs = qs.extra(select={'datestr':"DATE_FORMAT(activation_date, '%Y-%m-%d')"})
qs = qs.values_list('datestr')
You can also convert the date in queryset to string using map function. Example:
qs = MyModel.objects.filter(name='me').values_list('activation_date', flat=True)
data = map(str, qs)
I did it this way
.annotate(date_str=ExpressionWrapper(
Func(F('date'), Value('%d/%m/%Y %H:%i'), function='DATE_FORMAT'), output_field=CharField()
))
If you are doing this once, refer to Yannic Hamann's answer. However if you find yourself converting to str from the database a lot, you can define the Func as a class to avoid having to type output_field and function a bunch.
class DateToChar(models.Func):
"""
Custom Func expression to convert datetimes to str's in database query
Params for initializer
------
expression_1
expression resulting in a date: ex: F('date')
expression_2
Format string as an expression: Value('YYYY-MM-DD')
"""
arity = 2
function = 'to_char'
output_field = models.CharField()
Note that function will change depending on the database backend. This is written for Postgres.
This can be used like
qs = qs.annotate(date_str=DateToChar(F('date'), Value('YYYY-MM-DD'))
or with any expression that results in a date/datetime/Integer/Float/Decimal field (See Postgres to_char. Varies by database) and an expression resulting in a CharField or TextField.
See Func documentation for more information
I had a similar issue then I solved this in the following way:
from django.db.models.functions import Substr
list( Model.objects.values('when_date').annotate(date= Substr('when_date',1,10), total=Sum('amount')) )
(Django 4.1, python 3.10)

Handling dates over request.GET

I am sending two date values over request.GET for filtering a querySet:
if 'from_date' in request.GET:
from_date = request.GET['from_date']
if 'to_date' in request.GET:
to_date = request.GET['to_date']
else:
to_date = datetime.date.today()
calls_queryset = calls_queryset.filter(contact_date__range=(from_date, to_date))
The filter__range breaks though. It seems it doesn't like the date format I am sending over.
?from_date=08/08/2012&to_date=08/29/2012
I think I have to cast them to be a date before placing them in range, is this correct? What is the most efficient way to do this?
Many Thanks
The DateField supports converting from 'YYYY-mm-dd' formats to python datetime.date:
>>> from django.db import models
>>> models.DateField().to_python('2012-08-22')
datetime.date(2012, 8, 22)
Hence the range lookup accepts strings as parameters:
>>> from django.contrib.auth.models import User
>>> print User.objects.filter(last_login__range=('1970-01-01', '2012-12-31')).values('pk').query
SELECT "auth_user"."id" FROM "auth_user" WHERE "auth_user"."last_login"
BETWEEN 1969-12-31 16:00:00 and 2012-12-30 16:00:00
note that last_login is a DateTimeField which tries to convert string value to datetime.datetime (and I'm in +8 timezone, hence default 00:00:00 becomes 16:00:00 one day before)
On client side, ref How do I output an ISO 8601 formatted string in JavaScript? to generate date string in the format of 'YYYY-mm-dd'.
don't use slashs in url... replace with dots or use yyymmdd format.
Slashes is a no no, I'd convert to UNIX timestamp for convenience
Then to convert it to a python readable format you can do something like:
import time
time.ctime(int(retrieved_timestamp))

How do I compare DateTime objects to UNIX timestamps when filtering results in django?

I am using the FullCalendar plugin for JQuery.
This plugin passes a unix timestamp to a url (both start and end times), which is then used to query the event objects in the database and pull out events which fall within the range of start-end.
However, I keep getting a validation error because the times in my database are stored as datetimes and the value I am comparing them to is a UNIX timestamp.
How can I compare these two dates when running a query?
def fetch_events(request):
start = request.GET.get('start')
end = request.GET.get('end')
e = Event.objects.filter(start__gte=start, end__lte=end).values('id','title','start','end')
data = simplejson.dumps(list(e), cls=DjangoJSONEncoder)
return HttpResponse(data)
Try making a datetime object from a timestamp by using the datetime.datetime.fromtimestamp function. Here's a quick demonstration:
In [7]: import datetime
In [8]: datetime.datetime.fromtimestamp(2344353453)
Out[8]: datetime.datetime(2044, 4, 15, 19, 17, 33)