Difference between two dates python/Django - django

I need to know how to get the time elapsed between the edit_date(a column from one of my models) and datetime.now(). My edit_date column is under the DateTimeField format. (I'm using Python 2.7 and Django 1.10)
This is the function I'm trying to do:
def time_in_status(request):
for item in Reporteots.objects.exclude(edit_date__exact=None):
date_format = "%Y-%m-%d %H:%M:%S"
a = datetime.now()
b = item.edit_date
c = a - b
dif = divmod(c.days * 86400 + c.minute, 60)
days = str(dif)
print days
The only thing I'm getting from this fuction are the minutes elapsed and seconds. What I need is to get this date in the following format:
Time_elapsed = 3d 47m 23s
Any ideas? let me know if I'm not clear of if you need more information
Thanks for your attention,

Take a look at dateutil.relativedelta:
http://dateutil.readthedocs.io/en/stable/relativedelta.html
from dateutil.relativedelta import relativedelta
from datetime import datetime
now = datetime.now()
ago = datetime(2017, 2, 11, 13, 5, 22)
diff = relativedelta(ago, now)
print "%dd %dm %ds" % (diff.days, diff.minutes, diff.seconds)
I did that code from memory, so you may have to tweak it to your needs.

Try something like
c = a - b
minutes = (c.seconds % 3600) // 60
seconds = c.seconds % 60
print "%sd %sm %ss" % (c.days, minutes, seconds)

Related

Python script | long running | Need suggestions to optimize

I have written this script to generate a dataset which would contain 15 minute time intervals based on the inputs provided for operational hours for all days of a week for 365 days.
example: Let us say Store 1 opens at 9 AM and closes at 9 PM on all days. That is 12 hours everyday. 12*4 = 48(15 minute periods a day). 48 * 365 = 17520 (15 minute periods for a year).
The sample dataset only contains 5 sites but there are about 9000 sites that this script needs to generate data for.
The script obviously runs for a handful of sites(100) and couple of days(2) but needs to run for sites(9000) and 365 days.
Looking for suggestions to make this run faster. This will be running on a local machine.
input data: https://drive.google.com/open?id=1uLYRUsJ2vM-TIGPvt5RhHDhTq3vr4V2y
output data: https://drive.google.com/open?id=13MZCQXfVDLBLFbbmmVagIJtm6LFDOk_T
Please let me know if I can help with anything more to get this answered.
def datetime_range(start, end, delta):
current = start
while current < end:
yield current
current += delta
import pandas as pd
import numpy as np
import cProfile
from datetime import timedelta, date, datetime
#inputs
empty_data = pd.DataFrame(columns=['store','timestamp'])
start_dt = date(2019, 1, 1)
days = 365
data = "input data | attached to the post"
for i in range(days):
for j in range(len(data.store)):
curr_date = start_dt + timedelta(days=i)
curr_date_year = curr_date.year
curr_date_month = curr_date.month
curr_date_day = curr_date.day
weekno = curr_date.weekday()
if weekno<5:
dts = [dt.strftime('%Y-%m-%d %H:%M') for dt in
datetime_range(datetime(curr_date_year,curr_date_month,curr_date_day,data['m_f_open_hrs'].iloc[j],data['m_f_open_min'].iloc[j]), datetime(curr_date_year,curr_date_month,curr_date_day, data['m_f_close_hrs'].iloc[j],data['m_f_close_min'].iloc[j]),
timedelta(minutes=15))]
vert = pd.DataFrame(dts,columns = ['timestamp'])
vert['store']= data['store'].iloc[j]
empty_data = pd.concat([vert, empty_data])
elif weekno==5:
dts = [dt.strftime('%Y-%m-%d %H:%M') for dt in
datetime_range(datetime(curr_date_year,curr_date_month,curr_date_day,data['sat_open_hrs'].iloc[j],data['sat_open_min'].iloc[j]), datetime(curr_date_year,curr_date_month,curr_date_day, data['sat_close_hrs'].iloc[j],data['sat_close_min'].iloc[j]),
timedelta(minutes=15))]
vert = pd.DataFrame(dts,columns = ['timestamp'])
vert['store']= data['store'].iloc[j]
empty_data = pd.concat([vert, empty_data])
else:
dts = [dt.strftime('%Y-%m-%d %H:%M') for dt in
datetime_range(datetime(curr_date_year,curr_date_month,curr_date_day,data['sun_open_hrs'].iloc[j],data['sun_open_min'].iloc[j]), datetime(curr_date_year,curr_date_month,curr_date_day, data['sun_close_hrs'].iloc[j],data['sun_close_min'].iloc[j]),
timedelta(minutes=15))]
vert = pd.DataFrame(dts,columns = ['timestamp'])
vert['store']= data['store'].iloc[j]
empty_data = pd.concat([vert, empty_data])
final_data = empty_data
I think the most time consuming tasks in your script are the datetime calculations.
You should try to make all of those calculations using UNIX Time. It basically represents time as an integer that counts seconds... so you could take two UNIX dates and see the difference just by doing simple subtraction.
In my opinion you should perform all the operations like that... and when the process has finished you can make all the datetime conversions to a more readable date format.
Other thing that you should change in your script is all the code repetition that is almost identical. It won't improve the performance, but it improves readability, debugging and your skills as a programmer. As a simple example I have refactored some of the code (you probably can do better than what I did, but this is just an example).
def datetime_range(start, end, delta):
current = start
while current < end:
yield current
current += delta
from datetime import timedelta, date, datetime
import numpy as np
import cProfile
import pandas as pd
# inputs
empty_data = pd.DataFrame(columns=['store', 'timestamp'])
start_dt = date(2019, 1, 1)
days = 365
data = "input data | attached to the post"
for i in range(days):
for j in range(len(data.store)):
curr_date = start_dt + timedelta(days=i)
curr_date_year = curr_date.year
curr_date_month = curr_date.month
curr_date_day = curr_date.day
weekno = curr_date.weekday()
week_range = 'sun'
if weekno < 5:
week_range = 'm_f'
elif weekno == 5:
week_range = 'sat'
first_time = datetime(curr_date_year,curr_date_month,curr_date_day,data[week_range + '_open_hrs'].iloc[j],data[week_range + '_open_min'].iloc[j])
second_time = datetime(curr_date_year,curr_date_month,curr_date_day, data[week_range + '_close_hrs'].iloc[j],data[week_range + '_close_min'].iloc[j])
dts = [ dt.strftime('%Y-%m-%d %H:%M') for dt in datetime_range(first_time, second_time, timedelta(minutes=15)) ]
vert = pd.DataFrame(dts, columns = ['timestamp'])
vert['store']= data['store'].iloc[j]
empty_data = pd.concat([vert, empty_data])
final_data = empty_data
Good luck!

Django: DurationField with resolution in microseconds

The django DurationField displays only HH:MM:SS in the django admin interface.
Unfortunately this is not enough in my current context.
I need to be able to show/edit microseconds in the admin interface.
How could this be done?
Update
This was a mistake. My data in the database was wrong. The microseconds where removed in a process before the data came into the database.
Django displayes the microseconds if there are any. You don't need to do anything to show them.
Have a look on source:
https://docs.djangoproject.com/en/2.0/_modules/django/db/models/fields/#DurationField
I think the way is to override forms.DurationField (https://docs.djangoproject.com/en/2.0/_modules/django/forms/fields/#DurationField) and to be exact these method:
from django.utils.duration import duration_string
def duration_string(duration):
"""Version of str(timedelta) which is not English specific."""
days, hours, minutes, seconds, microseconds = _get_duration_components(duration)
string = '{:02d}:{:02d}:{:02d}'.format(hours, minutes, seconds)
if days:
string = '{} '.format(days) + string
if microseconds:
string += '.{:06d}'.format(microseconds)
return string
be aware that there may be need to override these too django.utils.dateparse.parse_duration
def parse_duration(value):
"""Parse a duration string and return a datetime.timedelta.
The preferred format for durations in Django is '%d %H:%M:%S.%f'.
Also supports ISO 8601 representation and PostgreSQL's day-time interval
format.
"""
match = standard_duration_re.match(value)
if not match:
match = iso8601_duration_re.match(value) or postgres_interval_re.match(value)
if match:
kw = match.groupdict()
days = datetime.timedelta(float(kw.pop('days', 0) or 0))
sign = -1 if kw.pop('sign', '+') == '-' else 1
if kw.get('microseconds'):
kw['microseconds'] = kw['microseconds'].ljust(6, '0')
if kw.get('seconds') and kw.get('microseconds') and kw['seconds'].startswith('-'):
kw['microseconds'] = '-' + kw['microseconds']
kw = {k: float(v) for k, v in kw.items() if v is not None}
return days + sign * datetime.timedelta(**kw)

Convert two Timestamps to Datetime and get their difference in Python

import datetime
start = datetime.fromtimestamp(float(1485008513.00000))
end = datetime.fromtimestamp(float(1485788517.80000))
#Duration
duration = end - start
My result is :
9 days, 0:40:04.800000
But it must be like this (without days, only hours, minutes and seconds) :
216:40:04.800000
Thanks a lot !
Not elegant, but works (for your example, durations less then a day and much more then 1000 days) - but its ugly:
import datetime
start = datetime.datetime.fromtimestamp(float(1485788515.0000))
end = datetime.datetime.fromtimestamp(float(1485788517.80000))
#Duration
duration = end - start
dur = str(duration).split(',')
print dur
# less then a day is not str() as 0days, ... so we fix that here by introducing artificial
# zero day if a split only retunrs 1 element
if len(dur) < 2:
d = ["0", dur[0]]
dur = d
dayHours = int(dur[0].replace('days',''))*24 # remove the days, mult with 24
hours = dur[1].split(':')[0] # get the partial hours of this part
minsSecs = ':'.join(dur[1].split(':')[1:]) # split+join the rest from the hours
# print all combined
print (str( dayHours+ int(hours) ) + ':' + minsSecs)
Output:
216:40:04.800000
Maybe better:
totSec = duration.total_seconds()
hours = totSec // (60*60)
mins = (totSec - (hours*60*60)) // 60
secs = totSec - (hours*60*60) - mins * 60
print "{:2}:{:2}:{:09.6f}".format(int(hours),int(mins),secs)

How to query to fetch last 5 months records?

I have a model named 'DemoModel' it has a field called demo_date.
I want to fetch the last 5 months i.e;(from current month records to past 5 months records) records by querying on the demo_date field.
My models look like
class DemoModel(models.Model):
demo_date = models.DateTimeField()
from datetime import datetime, timedelta
today = datetime.today()
long_ago = today + timedelta(days=-150)
retrieved_data = DemoModel.objects.filter(demo_date__gte=long_ago)
Use
dateutil.relativedelta import relativedelta
to calculate the five_months_ago parameter accurately.
And then get the objects like this:
target_set = DemoModel.objects.filter(demo_date__gte=five_months_ago)
This function give subscription or add months
def monthdelta(date, delta):
m, y = (date.month+delta) % 12, date.year + ((date.month)+delta-1) // 12
if not m: m = 12
d = min(date.day, [31,
29 if y%4==0 and not y%400==0 else 28,31,30,31,30,31,31,30,31,30,31][m-1])
return date.replace(day=d,month=m, year=y)
query goes here
from datetime import datetime
query= DemoModel.objects.filter(demo_date__gte=monthdelta(datetime.now(), -5)
)

Django: Total birthdays each day for the next 30 days

I've got a model similar to this:
class Person(models.Model):
name = models.CharField(max_length=40)
birthday = DateTimeField() # their next birthday
I would like to get a list of the total birthdays for each day for the next 30 days. So for example, the list would look like this:
[[9, 0], [10, 3], [11, 1], [12, 1], [13, 5], ... #30 entries in list
Each list entry in the list is a date number followed by the number of birthdays on that day. So for example on the 9th of May there are 0 birthdays.
UPDATES
My db is sqlite3 - will be moving to postgres in the future.
from django.db.models import Count
import datetime
today = datetime.date.today()
thirty_days = today + datetime.timedelta(days=30)
birthdays = dict(Person.objects.filter(
birthday__range=[today, thirty_days]
).values_list('birthday').annotate(Count('birthday')))
for day in range(30):
date = today + datetime.timedelta(day)
print "[%s, %s]" % (date, birthdays.get(date, 0))
I would get the list of days and birthday count this way:
from datetime import date, timedelta
today = date.today()
thirty_days = today + timedelta(days=30)
# get everyone with a birthday
people = Person.objects.filter(birthday__range=[today, thirty_days])
birthday_counts = []
for date in [today + timedelta(x) for x in range(30)]:
# use filter to get only birthdays on given date's day, use len to get total
birthdays = [date.day, len(filter(lambda x: x.birthday.day == date.day, people))]
birthday_counts.append(birthdays)
Something like this --
from datetime import date, timedelta
class Person(models.Model):
name = models.CharField(max_length=40)
birthday = models.DateField()
#staticmethod
def upcoming_birthdays(days=30):
today = date.today()
where = 'DATE_ADD(birthday, INTERVAL (YEAR(NOW()) - YEAR(birthday)) YEAR) BETWEEN DATE(NOW()) AND DATE_ADD(NOW(), INTERVAL %S DAY)'
birthdays = Person.objects.extra(where=where, params=[days]).values_list('birthday', flat=True)
data = []
for offset in range(0, days):
i = 0
d = today + timedelta(days=offset)
for b in birthdays:
if b.day == d.day and b.month == d.month:
i += 1
data.append((d.day, i))
return data
print Person.upcoming_birthdays()
(Queryset of people with a birthday in the next X days)
Found cool solution for this!
For me it works!
from datetime import datetime, timedelta
import operator
from django.db.models import Q
def birthdays_within(days):
now = datetime.now()
then = now + timedelta(days)
# Build the list of month/day tuples.
monthdays = [(now.month, now.day)]
while now <= then:
monthdays.append((now.month, now.day))
now += timedelta(days=1)
# Tranform each into queryset keyword args.
monthdays = (dict(zip(("birthday__month", "birthday__day"), t))
for t in monthdays)
# Compose the djano.db.models.Q objects together for a single query.
query = reduce(operator.or_, (Q(**d) for d in monthdays))
# Run the query.
return Person.objects.filter(query)
But it get a list of persons that have a birthday in date range. You should change a bit.