Suppose I have the following Date time fields and I would like to calculate the total time between them. Which is the best approach?
session_end_time = models.DateTimeField(null=True, blank=True)
discharged_at = models.DateTimeField(null=True, blank=True)
checked_in_at = models.DateTimeField(null=True, blank=True)
Django DateTime Fields are like datetime object of python, to calculate the total time between, you need to substract one from another one since they are same objects. This is an approach
result = datetime1 - datetime2
result.seconds # To have the output in seconds
In your case:
total_time = (checked_in_at - discharged_at).seconds
You can simply use - operator to calculate the time difference. The result will be a time delta object.
def time_diff(time1, time2):
"retun time2-time1 in 'seconds' "
if time1 and time2:
return (time2 - time1).seconds
return "one of the input is None"
This function returns the difference in seconds and it will handle TypeError exception if one of the input is a None type. (
You defined it as null=True in models)
Related
I have a Django model with the following structure:
class BBPerformance(models.Model):
marketcap_change = models.FloatField(verbose_name="marketcap change", null=True, blank=True)
bb_change = models.FloatField(verbose_name="bestbuy change", null=True, blank=True)
created_at = models.DateTimeField(verbose_name="created at", auto_now_add=True)
updated_at = models.DateTimeField(verbose_name="updated at", auto_now=True)
I would like to have an Avg aggregate function on objects for every 3 days.
for example I write a queryset that do this aggregation for each day or with something like TruncDay function.
queryset = BBPerformance.objects.annotate(day=TruncDay('created_at')).values('day').annotate(marketcap_avg=Avg('marketcap_change'),bb_avg=Avg('bb_change')
How can I have a queryset of the aggregated value with 3-days interval and the index of the second day of that interval?
I guess it's impossible on DB level (and Trunc is DB level function) as only month, days weeks and so on are supported in Postgres and Oracle.
So what I would suggest is to use TruncDay and then add python code to group those by 3 days.
The following should work, although it's slightly ugly.
If you get the difference in days between each row's date and the min date you can then take the Mod of this difference to work out how many days you need to shift to get "middle" date. This middle date can then be grouped on using a values query
import datetime
from django.db.models import F, IntegerField, Avg, Min, DurationField, DateField
from django.db.models.functions import Cast, Mod, Extract
BBPerformance.objects.order_by(
'created_at'
).annotate(
diff=F('created_at__date') - BBPerformance.objects.aggregate(min=Min('created_at__date'))['min']
).annotate(
diff_days=Cast(Extract('diff', 'days'), output_field=IntegerField())
).annotate(
to_shift=Mod('diff_days', 3) - 1
).annotate(
grouped_date=Cast(F('created_at__date') - Cast(F('to_shift') * datetime.timedelta(days=1), output_field=DurationField()), output_field=DateField())
).order_by(
'grouped_date'
).values(
'grouped_date'
).annotate(
marketcap_avg=Avg('marketcap_change'),
bb_avg=Avg('bb_change')
)
im having a problem trying to set a duration in my to-do tasks.
i've tried with DurationField and some people told me to try the timedelta in your forms.py but im not quite shure how to pass the difference like (6days) from my two model DateField (start and end).
Models.py
from django.db import models
from datetime import datetime, timedelta
class To_do (models.Model):
task = models.CharField(max_length=150)
topic = models.CharField(max_length=150)
how = models.TextField(max_length=600)
start = models.DateField(default=datetime.today)
end = models.DateField(blank=False)
duration = models.DurationField(default=timedelta)
i'd like to display the difference for the user and after set an alarm for less than 3 days etc.
How do I solve this?
The difference between two date or date/time values is a timedelta.
delta_time = end - start
Or if you need to code a delta-time constant from other numbers
from datetime import timedelta
my_delta = timedelta( days=3, hours=12, minutes=1 ) # half a week plus a minute
Don't use timedelta as the name as the default value if you are importing it! If what you mean to do is to pass a callable to calculate a timedelta, define a function to do that as above, and pass it as the default value.
I'm trying to get the number of months since a model is created.
My Model looks like this:
class Plan(models.Model):
title = models.CharField(max_length=50)
date_created = models.DateTimeField(default=timezone.now)
plan_type = models.IntegerField()
owner = models.ForeignKey(User, on_delete=models.CASCADE)
Now i want to make a method that returns the number of months since the date_created.
Tanks for any help :D
Comparing dates creates a datetime.timedelta object that you can use to get the difference between dates.
from datetime import timedelta
from django.utils.timezone import now
delta: timedelta = now() - plan.date_created
delta.days # Number of days between dates.
You can then use that value to convert it to months or years.
The other alternative would be a bit more complicated, but since DateTimeField returns a datetime.datetime object, you can also access the month number of the original date and compare it against todays date.
e.g.
from django.utils.timezone import now
month_diff = now().month - plan.date_created.month
The problem with the second alternative is that you then have to take into account if they are the same year or not, and if they are not you then have to take that into account when you get the month difference.
You can write a property in your model like
from django.utils import timezone
class Plan(models.Model):
title = models.CharField(max_length=50)
...
#property
def get_month(self):
return self.date_created.month - timezone.now().month
Then you can get the value like this
>>> Plan.objects.first().get_month
4
In a django application, I have a model like following:
class Doe(models.Model):
start = models.DateTimeField()
end = models.DateTimeField()
I want to find the average difference of end and start values for this model. I'd prefer not to use an extra column or another patchy solution. What is the django-ish way of doing this?
You can combine aggregate, F expressions and Avg:
Doe.objects.all().aggregate(average_difference=Avg(F('end') - F('start')))
In addition to #ErwinJunge answer, to handle TypeError: float() argument must be a string or a number issue.
Create your own Avg class which will work with timedelta total seconds value:
from datetime import timedelta
class AvgTimeDelta(Avg):
def convert_value(self, value, expression, connection, context):
if value is None:
return value
if isinstance(value, timedelta):
value = value.total_seconds()
return float(value)
Then use it in the aggregate method:
average = Doe.objects.all().aggregate(
average_difference=AvgTimeDelta(F('end') - F('start'))
)['average_difference']
And convert your average seconds amount back to timedelta:
average = timedelta(seconds=average)
Say I have a class in model
class Post(models.Model):
time_posted = models.DateTimeField(auto_now_add=True, blank=True)
def get_time_diff(self):
timediff = timediff = datetime.datetime.now() - self.time_posted
print timediff # this line is never executed
return timediff
I defined a get_time_diff to get the time difference from the time when the Post is posted up to now, according to the document, the DateTimeField should be able to be converted to datetime automatically, is that correct? Why the print statement is never being run? How can you extract the time difference?
Beside, if you get a time difference, is there an easy way to convert the time difference to an integer, like the number of seconds of the total time.
Your code is already working; a datetime.timedelta object is returned.
To get the total number of seconds instead, you need to call the .total_seconds() method on the resulting timedelta:
from django.utils.timezone import utc
def get_time_diff(self):
if self.time_posted:
now = datetime.datetime.utcnow().replace(tzinfo=utc)
timediff = now - self.time_posted
return timediff.total_seconds()
.total_seconds() returns a float value, including microseconds.
Note that you need to use a timezone aware datetime object, since the Django DateTimeField handles timezone aware datetime objects as well. See Django Timezones documentation.
Demonstration of .total_seconds() (with naive datetime objects, but the principles are the same):
>>> import datetime
>>> time_posted = datetime.datetime(2013, 3, 31, 12, 55, 10)
>>> timediff = datetime.datetime.now() - time_posted
>>> timediff.total_seconds()
1304529.299168
Because both objects are timezone aware (have a .tzinfo attribute that is not None), calculations between them take care of timezones and subtracting one from the other will do the right thing when it comes to taking into account the timezones of either object.
Assuming you are doing this within a template, you can also use the timesince template tag.
For example:
{{ blog_date|timesince:comment_date }}
Your code
timediff = datetime.datetime.now() - self.pub_date
should work to get the time difference. However, this returns timedelta object. To get difference in seconds you use .seconds attribute
timediff = datetime.datetime.now() - self.pub_date
timediff.seconds # difference in seconds.
Just in case you want to put this process in you Django signals. Here's the one that is working for me. Hope this helps!
from django.db.models.signals import pre_save
from django.dispatch import receiver
from .models import YourModel
from datetime import datetime
#receiver(pre_save, sender = YourModel)
def beforeSave(sender, instance, **kwargs):
date_format = "%H:%M:%S"
# Getting the instances in your model.
time_start = str(instance.time_start)
time_end = str(instance.time_end)
# Now to get the time difference.
diff = datetime.strptime(time_end, date_format) - datetime.strptime(time_start, date_format)
# Get the time in hours i.e. 9.60, 8.5
result = diff.seconds / 3600;
Simply we can add the custom property to calculate the time difference with the help #property decorator in that model.
from django.utils import timezone
class Post(models.Model):
time_posted = models.DateTimeField(auto_now_add=True, blank=True)
content = models.TextField()
#property
def time_diff(self):
return timezone.now() - self.time_posted
time_diff will return object of datetime.timedelta
post = Post.objects.get(pk=1) # Post model object
# time diff in seconds.
post.time_diff.seconds
>>> 652
# time diff in days.
post.time_diff.days
>>> 0
Already answered above nicely by Martijn Pieters, just adding #property, and django.utils.timezone to calculate the difference with respective timezone from settings.py