[Fixed]-Display and format Django DurationField in template

18๐Ÿ‘

โœ…

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)

    
๐Ÿ‘คaumo

4๐Ÿ‘

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}'

1๐Ÿ‘

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'
๐Ÿ‘คBenbb96

0๐Ÿ‘

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.

datetime.py (templatetag file)

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

template.html (template file)

{% 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>

Example Output

<!-- phrase format -->
<time datetime="P2DT1H0M0.000000S">2 days, 1 hour</time>

<!-- clock format -->
<time datetime="P2DT1H0M0.000000S">2 days, 01:00</time>
๐Ÿ‘คkloddant

Leave a comment