1
\$\begingroup\$

I'm creating a filter in python/django. I want to make a datetime human-readable. You have multiple options: Plain Datetime ("04. Februar 2018, 18:18")

(bools="False,False"),

sentence Datetime ("am 04. Februar 2018 um 18:18")

(bools="True,False"), 

onlydate Datetime ("04. Februar 2018")

(bools="False,True"), 

sentence and onlyDate ("am 04. Februar 2018")

(bools="True,True")

Here's my filter:

@register.filter
def naturaldaytime(daytime, bools="False,False"):
    if daytime is None:
        return daytime

    sentence = str2bool(bools.split(",")[0])
    onlyDate = str2bool(bools.split(",")[1])

    now = datetime.datetime.now()
    difference = now.day - daytime.replace(tzinfo=None).day
    loc = locale.setlocale(locale.LC_TIME, "de")

    if onlyDate:
        if difference == 0:
            return daytime.strftime(settings.DATE_FORMAT_TODAY) if not sentence else daytime.strftime(settings.DATE_FORMAT_SENTENCE)
        elif difference == 1:
            return daytime.strftime(settings.DATE_FORMAT_YESTERDAY) if not sentence else daytime.strftime(settings.DATE_FORMAT_YESTERDAY)
        elif now.isocalendar()[0] == daytime.isocalendar()[0]:
            if now.isocalendar()[1] == daytime.isocalendar()[1]:
                return daytime.strftime(settings.DATE_FORMAT_THIS_WEEK) if not sentence else daytime.strftime(settings.DATE_FORMAT_THIS_WEEK_SENTENCE)
            return daytime.strftime(settings.DATE_FORMAT_THIS_YEAR) if not sentence else daytime.strftime(settings.DATE_FORMAT_THIS_YEAR_SENTENCE)
        return daytime.strftime(settings.DATE_FORMAT) if not sentence else daytime.strftime(settings.DATE_FORMAT_SENTENCE)
    else:
        pass
        #so what...


def str2bool(v):
    return v.lower() in ("yes", "true", "t", "1")

and my settings vars:

TIME_FORMAT = "%H:%M"

DATE_FORMAT = "%d. %B %Y, {0}".format(TIME_FORMAT)
DATE_FORMAT_THIS_YEAR = "%d. %B, {0}".format(TIME_FORMAT)
DATE_FORMAT_THIS_WEEK = "%A, {0}".format(TIME_FORMAT)
DATE_FORMAT_YESTERDAY = "Gestern, {0}".format(TIME_FORMAT)
DATE_FORMAT_TODAY = "Heute, {0}".format(TIME_FORMAT)

DATE_FORMAT_SENTENCE = "am %d. %B %Y um {0} Uhr".format(TIME_FORMAT)
DATE_FORMAT_THIS_YEAR_SENTENCE = "am %d. %B um {0} Uhr".format(TIME_FORMAT)
DATE_FORMAT_THIS_WEEK_SENTENCE = "diesen %A um {0} Uhr".format(TIME_FORMAT)
DATE_FORMAT_YESTERDAY_SENTENCE = "gestern um {0} Uhr".format(TIME_FORMAT)
DATE_FORMAT_TODAY_SENTENCE = "heute um {0} Uhr".format(TIME_FORMAT)
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

Set your locale globally

I would recommend doing this in your settings.py file or within a global __init__.py file.

locale.setlocale(locale.LC_TIME, "de")

Django template tags allow multiple parameters, use them

Right now you have a single complex parameter which is being used to store two pieces of information: whether to use a sentence and if only the date should be rendered. Since this is passed in as a single string, you have to use the str2bool function from Stack Overflow in order to parse it. You don't need to parse that string if you use separate arguments.

@register.filter
def naturaldaytime(daytime, sentence=False, only_date=False):
    from django.conf import settings

    if daytime is None:
        return daytime

Use descriptive variable names in your comparisons

Right now you have to look into nested calls to understand what comparisons are actually being done. While this still needs to happen, you can make us to intermediate variables which makes it a lot more clear what is happening.

todays_date = datetime.now().date()
yesterdays_date = todays_date - timedelta(days=1)

daytime_week_number = daytime.date().isocalendar()[1]
todays_week_number = todays_date.isocalendar()[1]

Construct the format key you need so you can avoid repetition

Right now you have a complex set of conditional statements that you are using to determine what date format to use. This is resulting in a ton of repetition when it comes to doing the actual formatting, so I would recommend separating the "getting the right format settings" part from the actual "do the formatting".

if not only_date:
    setting_key = "DATETIME_FORMAT"
else:
    setting_key = "DATE_FORMAT"

if daytime.date() == todays_date:
    setting_key += "_TODAY"
elif daytime.date() == yesterdays_date:
    setting_key += "_YESTERDAY"
elif daytime_week_number == todays_week_number:
    setting_key += "_THIS_WEEK"
elif daytime.date().year == todays_date.year:
    setting_key += "_THIS_YEAR"

if sentence:
    setting_key += "_SENTENCE"

This way you don't need to keep repeating the same logic for every "sentence" or "not only the date" variant, and you can keep settings keys consistent at the same time.

Get the right date format using that settings key you already built

This part doesn't change much, except you now need to dynamically retrieve the setting from the settings object.

date_format = getattr(settings, setting_key)

return daytime.strptime(date_format)
\$\endgroup\$
2
  • \$\begingroup\$ Great, thank you! But I dont understand why you need to import settings from django.conf (Second code, line 3, "from django.conf import settings") \$\endgroup\$
    – Myzel394
    Feb 11, 2019 at 17:06
  • \$\begingroup\$ And how do I use this filter in a template? \$\endgroup\$
    – Myzel394
    Feb 11, 2019 at 17:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.