Reputation: 1079
Im using Django 1.8, and i have one of my field defined as DurationField, but i dont find any way to correctly display it on my template, if i output it like this:
{{runtime}}
i just get 0:00:00.007980
is there any filter or any other way of displaying something more like
2hours 30 min
Upvotes: 15
Views: 9646
Reputation: 2273
This is the one I use, it rounds minutes and display only the information needed :
@register.filter
def duration(timedelta):
"""
Format a duration field
"2h and 30 min" or only "45 min" for example
:rtype: str
"""
total_seconds = int(timedelta.total_seconds())
hours = total_seconds // 3600
minutes = round((total_seconds % 3600) / 60)
if minutes == 60:
hours += 1
minutes = 0
if hours and minutes:
# Display both
return f'{hours}h and {minutes} min'
elif hours:
# Display only hours
return f'{hours}h'
# Display only minutes
return f'{minutes} min'
Upvotes: 1
Reputation: 1079
In order to make this as readable as possible for crawlers and screen readers, so as to improve your SEO rating, the best practice is to use the html time
tag, with the datetime
attribute.
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time
https://www.w3.org/TR/2014/REC-html5-20141028/infrastructure.html#valid-duration-string
I have written a template tag to accomplish the formatting of the duration, both in machine and human-readable forms. This displays days, hours, minutes, seconds, and microseconds, using the largest increments possible and hiding any values that are 0. It also handles pluralizing the labels and adding commas and spaces as necessary.
import datetime
from django import template
register = template.Library()
from django.template.defaultfilters import pluralize
@register.filter
def duration(value, mode=""):
assert mode in ["machine", "phrase", "clock"]
remainder = value
response = ""
days = 0
hours = 0
minutes = 0
seconds = 0
microseconds = 0
if remainder.days > 0:
days = remainder.days
remainder -= datetime.timedelta(days=remainder.days)
if round(remainder.seconds/3600) > 1:
hours = round(remainder.seconds/3600)
remainder -= datetime.timedelta(hours=hours)
if round(remainder.seconds/60) > 1:
minutes = round(remainder.seconds/60)
remainder -= datetime.timedelta(minutes=minutes)
if remainder.seconds > 0:
seconds = remainder.seconds
remainder -= datetime.timedelta(seconds=seconds)
if remainder.microseconds > 0:
microseconds = remainder.microseconds
remainder -= datetime.timedelta(microseconds=microseconds)
if mode == "machine":
response = "P{days}DT{hours}H{minutes}M{seconds}.{microseconds}S".format(
days=days,
hours=hours,
minutes=minutes,
seconds=seconds,
microseconds=str(microseconds).zfill(6),
)
elif mode == "phrase":
response = []
if days:
response.append(
"{days} day{plural_suffix}".format(
days=days,
plural_suffix=pluralize(days),
)
)
if hours:
response.append(
"{hours} hour{plural_suffix}".format(
hours=hours,
plural_suffix=pluralize(hours),
)
)
if minutes:
response.append(
"{minutes} minute{plural_suffix}".format(
minutes=minutes,
plural_suffix=pluralize(minutes),
)
)
if seconds:
response.append(
"{seconds} second{plural_suffix}".format(
seconds=seconds,
plural_suffix=pluralize(seconds),
)
)
if microseconds:
response.append(
"{microseconds} microsecond{plural_suffix}".format(
microseconds=microseconds,
plural_suffix=pluralize(microseconds),
)
)
response = ", ".join(response)
elif mode == "clock":
response = []
if days:
response.append(
"{days} day{plural_suffix}".format(
days=days,
plural_suffix=pluralize(days),
)
)
if hours or minutes or seconds or microseconds:
time_string = "{hours}:{minutes}".format(
hours = str(hours).zfill(2),
minutes = str(minutes).zfill(2),
)
if seconds or microseconds:
time_string += ":{seconds}".format(
seconds = str(seconds).zfill(2),
)
if microseconds:
time_string += ".{microseconds}".format(
microseconds = str(microseconds).zfill(6),
)
response.append(time_string)
response = ", ".join(response)
return response
{% load datetime %}
<!-- phrase format -->
<time datetime="{{ event.duration|duration:'machine' }}">
{{ event.duration|duration:'phrase' }}
</time>
<!-- clock format -->
<time datetime="{{ event.duration|duration:'machine' }}">
{{ event.duration|duration:'clock' }}
</time>
<!-- phrase format -->
<time datetime="P2DT1H0M0.000000S">2 days, 1 hour</time>
<!-- clock format -->
<time datetime="P2DT1H0M0.000000S">2 days, 01:00</time>
Upvotes: 0
Reputation: 5554
No, I don't think there is any built in filter to format a timedelta
, it should be fairly easy to write one yourself though.
Here is a basic example:
from django import template
register = template.Library()
@register.filter
def duration(td):
total_seconds = int(td.total_seconds())
hours = total_seconds // 3600
minutes = (total_seconds % 3600) // 60
return '{} hours {} min'.format(hours, minutes)
Upvotes: 19
Reputation: 493
Contribution for Aumo answer:
from django import template
register = template.Library()
@register.filter
def duration(td):
total_seconds = int(td.total_seconds())
days = total_seconds // 86400
remaining_hours = total_seconds % 86400
remaining_minutes = remaining_hours % 3600
hours = remaining_hours // 3600
minutes = remaining_minutes // 60
seconds = remaining_minutes % 60
days_str = f'{days}d ' if days else ''
hours_str = f'{hours}h ' if hours else ''
minutes_str = f'{minutes}m ' if minutes else ''
seconds_str = f'{seconds}s' if seconds and not hours_str else ''
return f'{days_str}{hours_str}{minutes_str}{seconds_str}'
Upvotes: 4