4
\$\begingroup\$

The goal is displaying the date and time of an article in a humanized and localized way:

Yesterday
13:21

or if the Swedish language parameter is set it will display:

Igår 
13:21

And if the date wasn't yesterday or today, it will print the date and 24-hour time. I think I succeeded with everything except handling the timezone:

def datetimeformat_jinja(value, format='%H:%M / %d-%m-%Y', locale='en'):
    now= datetime.now()
    info = None
    if datetime.date(value) == datetime.date(now):
      info= _('Today')
    elif (now - value).days < 2:
      info= _('Yesterday')
    else:
      month = value.month
      if month == 1:
        info = str(value.day)+' '+_('Jan')
      elif month == 2:
        info = str(value.day)+' '+_('Feb')
      elif month == 3:
        info = str(value.day)+' '+_('Mar')
      elif month == 4:
        info = str(value.day)+' '+_('April')
      elif month == 5:
        info = str(value.day)+' '+_('May')
      elif month == 6:
        info = str(value.day)+' '+_('June')
      elif month == 7:
        info = str(value.day)+' '+_('July')
      elif month == 8:
        info = str(value.day)+' '+_('Aug')
      elif month == 9:
        info = str(value.day)+' '+_('Sep')
      elif month == 10:
        info = str(value.day)+' '+_('Oct')
      elif month == 11:
        info = str(value.day)+' '+_('Nov')
      else:
        info = str(value.day)+' '+_('Dec')
    return info+'<br>'+format_time(value, 'H:mm', locale=locale)

The above code localizes and humanizes the output:

Today
3:29

I can also switch languages to, for example, Portuguese:

Hoje
3:29

Where "Hoje" means "today," so the filter appears to work.

Could you please review it or give me a hint on how I can admit my code to also allow time zones? I use babel for localization and Jinja2 for rendering.

\$\endgroup\$
4
\$\begingroup\$

Adding time zone support

Add a new argument to your function: now. This should be the localized datetime of the client. Compare now to the current server datetime and adjust the formatted time by the difference between them. Boom, timezone support added.

That said you cannot get the client timezone server-side. Look into client-side solutions with JavaScript (either full client-side date formatting, or phoning home). This should start you off on the right track.

On with the code review.

Tidying up the code

Variable and function naming

  • info and value are excessively vague.
  • datetimeformat_jinja() is a bit eccentric as a function name, as its not in imperative form (eg format_datetime).

String formatting

Concatenating strings with addition is considered bad practice is Python. You should format individual elements of your message together, rather than starting with info='' and adding new parts as you go along. Use str.format() (or %s-formatting with Python 2.5 and below) to achieve this.

The If-elif-else chain

In lines 9 through 33 you copy-pasted the same logic twelve times over; that's a sure-fire sign you need to express things more generically.

Style

Take this suggestion for the petty nothing it is, but—please, please, please—unless you're following the style guidelines of your team or employer, read and obey PEP 8.

Result

MONTHS = ('Jan', 'Feb', 'Mar', 'April', 'May', 'June',
          'July', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')
FORMAT = '%H:%M / %d-%m-%Y'

def format_datetime(to_format, format=FORMAT, locale='en'):
    now = datetime.now()
    if datetime.date(to_format) == datetime.date(now):
        date_str = _('Today')
    elif (now - to_format).days < 2:
        date_str = _('Yesterday')
    else:
        month = MONTHS[to_format.month - 1]
        date_str = '{0} {1}'.format(to_format.day, _(month))
    time_str = format_time(to_format, 'H:mm', locale=locale)
    return "{0}<br>{1}".format(date_str, time_str)
\$\endgroup\$
2
\$\begingroup\$
def datetimeformat_jinja(value, format='%H:%M / %d-%m-%Y', locale='en'):
    now= datetime.now()

Put a space after now and before =

    info = None

Info will always be assigned something later, there is no reason to assign here.

    if datetime.date(value) == datetime.date(now):

Compare with datetime.date.today(), that'll be a bit clearer.

      info= _('Today')
    elif (now - value).days < 2:

I'd also use today here.

      info= _('Yesterday')
    else:
      month = value.month
      if month == 1:
        info = str(value.day)+' '+_('Jan')
      elif month == 2:
        info = str(value.day)+' '+_('Feb')
      elif month == 3:
        info = str(value.day)+' '+_('Mar')
      elif month == 4:
        info = str(value.day)+' '+_('April')
      elif month == 5:
        info = str(value.day)+' '+_('May')
      elif month == 6:
        info = str(value.day)+' '+_('June')
      elif month == 7:
        info = str(value.day)+' '+_('July')
      elif month == 8:
        info = str(value.day)+' '+_('Aug')
      elif month == 9:
        info = str(value.day)+' '+_('Sep')
      elif month == 10:
        info = str(value.day)+' '+_('Oct')
      elif month == 11:
        info = str(value.day)+' '+_('Nov')
      else:
        info = str(value.day)+' '+_('Dec')

Firstly, no reason to duplicate the code that much. You should have at least put the months in a list and taken advantage of that. But you can get the month name by value.strftime("%b").

Python itself really doesn't have support for time zones. You need a third party library for that. pytz is often used for this purpose.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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