#3143 Improve human-readable date/time display in web UI
Merged 6 years ago by pingou. Opened 6 years ago by adamwill.
adamwill/pagure more-local-times  into  master

@@ -113,7 +113,7 @@ 

      <a class="headerlink pull-xs-right" title="Permalink to this headline"

        href="#comment-{{ id }}">

        <span title="{{

-         comment.date_created.strftime('%Y-%m-%d %H:%M:%S')

+         comment.date_created | format_datetime

          }}">{{ comment.date_created | humanize}}</span>

      </a>

    </div>
@@ -121,7 +121,7 @@ 

      <section class="issue_comment">

        <div>

          <span class="edit_date" title="{{

-             comment.edited_on.strftime('%Y-%m-%d %H:%M:%S') if comment.edited_on

+             comment.edited_on | format_datetime if comment.edited_on

          }}">

          </span>

          <span class="comment_text comment_body">

file modified
+1 -1
@@ -30,7 +30,7 @@ 

                </a> forked this project

              </span>

              <span class="commitdate" data-toggle="tooltip" title="{{

-                 fork.date_created.strftime('%Y-%m-%d %H:%M:%S') }}">

+                 fork.date_created | format_datetime }}">

                  <strong>{{ fork.date_created|humanize }}</strong>

              </span>

            </div>

file modified
+3 -3
@@ -33,14 +33,14 @@ 

  

    <h5 class="text-muted">

      Created <a href="#" data-toggle="tooltip" title="{{

-         issue.date_created.strftime('%b %d %Y %H:%M:%S')

+         issue.date_created | format_datetime

        }}">{{ issue.date_created | humanize}}</a> by <span title="{{

        issue.user.html_title }}">{{ issue.user.user }}</span>

    </h5>

    {% if issue.last_updated %}

      <h5 class="text-muted">

        Modified <a href="#" data-toggle="tooltip" title="{{

-         issue.last_updated.strftime('%b %d %Y %H:%M:%S')

+         issue.last_updated | format_datetime

        }}">{{ issue.last_updated | humanize}}</a>

      </h5>

    {% endif %}
@@ -63,7 +63,7 @@ 

              <div class="card-header">

                <div>

                  <div class="pull-xs-right text-muted">

-                     <span title="{{ comment.date_created.strftime('%Y-%m-%d %H:%M:%S') }}">{{

+                     <span title="{{ comment.date_created | format_datetime }}">{{

                          comment.date_created | humanize }}</span>

                  </div>

                  <small>{{ comment.comment | markdown | noJS | safe }}</small>

file modified
+3 -3
@@ -263,19 +263,19 @@ 

                {% endfor%}

              </td>

              <td class="td-open_date nowrap">

-                 <span title="{{issue.date_created.strftime('%Y-%m-%d %H:%M:%S')}}">{{

+                 <span title="{{issue.date_created | format_datetime}}">{{

                          issue.date_created | humanize}}</span>

              </td>

              {% if status and status|lower != 'open' %}

              <td class="nowrap">

                  {% if issue.closed_at != None %}

-                 <span title="{{issue.closed_at.strftime('%Y-%m-%d %H:%M:%S')}}">{{

+                 <span title="{{issue.closed_at | format_datetime}}">{{

                          issue.closed_at | humanize}}</span>

                  {% endif %}

              </td>

              {% else %}

              <td class="td-mod_date nowrap">

-                 <span title="{{issue.last_updated.strftime('%Y-%m-%d %H:%M:%S')}}">{{

+                 <span title="{{issue.last_updated | format_datetime}}">{{

                          issue.last_updated | humanize}}</span>

              </td>

              {% endif %}

@@ -85,14 +85,14 @@ 

  

    <h5 class="text-muted">

      Proposed <a href="#" data-toggle="tooltip" title="{{

-         pull_request.date_created.strftime('%b %d %Y %H:%M:%S')

+         pull_request.date_created | format_datetime

        }}">{{ pull_request.date_created |humanize }}</a>

      by {{ pull_request.user.default_email | avatar(16) | safe

        }} <span title="{{ pull_request.user.html_title }}">{{ pull_request.user.user }}</span>

    </h5>

    <h5 class="text-muted">

      Modified <a href="#" data-toggle="tooltip" title="{{

-         pull_request.last_updated.strftime('%b %d %Y %H:%M:%S')

+         pull_request.last_updated | format_datetime

        }}">{{ pull_request.last_updated |humanize }}</a>

    </h5>

  
@@ -576,7 +576,7 @@ 

                          onclick="showTab()">{{

                          comment.line }} of {{ comment.filename }}</a>.

                  <div class="pull-xs-right text-muted">

-                     <span title="{{ comment.date_created.strftime('%Y-%m-%d %H:%M:%S') }}">{{

+                     <span title="{{ comment.date_created | format_datetime }}">{{

                          comment.date_created | humanize }}</span></div>

                </div>

                  (<a class="inline-btn small" data-id="{{ loop.index }}">Hide</a>)
@@ -590,7 +590,7 @@ 

              <div class="card-header">

                <div>

                  <div class="pull-xs-right text-muted">

-                     <span title="{{ comment.date_created.strftime('%Y-%m-%d %H:%M:%S') }}">{{

+                     <span title="{{ comment.date_created | format_datetime }}">{{

                          comment.date_created | humanize }}</span>

                  </div>

                  <small>{{ comment.comment | markdown | noJS | safe }}</small>

@@ -66,7 +66,7 @@ 

                      </a> forked this project

                    </span>

                    <span class="commitdate" data-toggle="tooltip" title="{{

-                       fork.date_created.strftime('%Y-%m-%d %H:%M:%S') }}">

+                       fork.date_created | format_datetime }}">

                        <strong>{{ fork.date_created|humanize }}</strong>

                    </span>

                  </div>

@@ -148,17 +148,17 @@ 

                <span>{{ request.branch }}</span>

              </td>

              <td class="nowrap">

-               <span title="{{request.date_created.strftime('%Y-%m-%d %H:%M:%S')}}">{{

+               <span title="{{request.date_created | format_datetime}}">{{

                  request.date_created | humanize}}</span>

              </td>

              <td class="nowrap">

-               <span title="{{request.last_updated.strftime('%Y-%m-%d %H:%M:%S')}}">{{

+               <span title="{{request.last_updated | format_datetime}}">{{

                  request.updated_on | humanize}}</span>

              </td>

              {% if status|lower not in ['open', 'true'] %}

              <td class="nowrap">

                <span title="{{

-                 request.closed_at.strftime('%Y-%m-%d %H:%M:%S')

+                 request.closed_at | format_datetime

                  if request.closed_at

                }}">{{

                  request.closed_at | humanize

@@ -68,18 +68,18 @@ 

                {% endfor %}

              </td>

              <td class="td-open_date nowrap">

-               <span title="{{ issue.date_created.strftime('%Y-%m-%d %H:%M:%S')}}">{{

+               <span title="{{ issue.date_created | format_datetime}}">{{

                        issue.date_created | humanize }}</span>

              </td>

              <td class="td-mod_date nowrap">

                {% if status and status|lower == 'closed' %}

                  <span title="{{

-                     issue.closed_at.strftime('%Y-%m-%d %H:%M:%S')

+                     issue.closed_at | format_datetime

                      if issue.closed_at

                      }}">{{

                        issue.closed_at | humanize }}</span>

                {% else %}

-               <span title="{{ issue.last_updated.strftime('%Y-%m-%d %H:%M:%S')}}">{{

+               <span title="{{ issue.last_updated | format_datetime}}">{{

                        issue.last_updated | humanize }}</span>

                {% endif %}

              </td>

@@ -66,11 +66,11 @@ 

                    </a>

                  </td>

                  <td class="nowrap">

-                     <span title="{{issue.date_created.strftime('%Y-%m-%d %H:%M:%S')}}">{{

+                     <span title="{{issue.date_created | format_datetime}}">{{

                          issue.date_created | humanize}}</span>

                  </td>

                  <td class="nowrap">

-                     <span title="{{issue.last_updated.strftime('%Y-%m-%d %H:%M:%S')}}">{{

+                     <span title="{{issue.last_updated | format_datetime}}">{{

                          issue.last_updated | humanize}}</span>

                  </td>

                  <td>

@@ -59,7 +59,7 @@ 

            </a>

            <div class="repo_desc">

              <small>{{ user.user }} joined <span title="{{

-             user.created.strftime('%Y-%m-%d %H:%M:%S') }}">{{

+             user.created | format_datetime }}">{{

              user.created | humanize }}</span></small>

            </div>

            <div class="project_metadata">

@@ -90,7 +90,7 @@ 

                </span>

              </td>

              <td class="nowrap">

-               <span title="{{request.date_created.strftime('%Y-%m-%d %H:%M:%S')}}">{{

+               <span title="{{request.date_created | format_datetime}}">{{

                  request.date_created | humanize}}</span>

              </td>

              <td class="nowrap">

file modified
+13 -8
@@ -13,7 +13,6 @@ 

  # pylint: disable=too-many-locals

  

  

- import datetime

  import textwrap

  import urlparse

  
@@ -62,11 +61,19 @@ 

  

  

  @UI_NS.app_template_filter('format_ts')

+ @UI_NS.app_template_filter('format_datetime')

  def format_ts(string):

-     """ Template filter transforming a timestamp to a date

+     """ Template filter transforming a timestamp, datetime or anything

+     else arrow.get() can handle to a human-readable date

      """

-     dattime = datetime.datetime.fromtimestamp(int(string))

-     return dattime.strftime('%b %d %Y %H:%M:%S')

+     # We *could* enhance this by allowing users to specify preferred

+     # timezone, localized time format etc. and customizing this display

+     # to user's preferences. But we don't have that, so for now, we

+     # always use UTC timezone, and we don't use localized forms like

+     # %b or %d because they will be 'localized' for the *server*.

+     # This format should be pretty 'locale-neutral'.

+     arr = arrow.get(string)

+     return arr.strftime('%Y-%m-%d %H:%M:%S %Z')

  

  

  @UI_NS.app_template_filter('format_loc')
@@ -210,8 +217,7 @@ 

  

                  if comment.edited_on:

                      templ_edited = tpl_edited % ({

-                         'edit_date': comment.edited_on.strftime(

-                             '%b %d %Y %H:%M:%S'),

+                         'edit_date': format_ts(comment.edited_on),

                          'human_edit_date': humanize_date(comment.edited_on),

                          'user': comment.editor.user,

                      })
@@ -253,8 +259,7 @@ 

                              'user_html': comment.user.html_title,

                              'avatar_url': avatar_url(

                                  comment.user.default_email, 16),

-                             'date': comment.date_created.strftime(

-                                 '%b %d %Y %H:%M:%S'),

+                             'date': format_ts(comment.date_created),

                              'human_date': humanize_date(comment.date_created),

                              'comment': markdown_filter(comment.comment),

                              'commentid': comment.id,

There are many places in the web UI where we take a date/time
(sourced either from a datetime or a timestamp) and turn it
into some kind of human-readable form. There are mouseovers
for the date/time a commit, PR or issue was created, the time
it was last modified, and the date/time on each comment in an
issue or PR, for instance. There were three problems with this.

First, these weren't all consistent. Some used the format string
'%b %d %Y %H:%M:%S', which gives something like "Mar 06 2018
23:44:27", while others used '%Y-%m-%d %H:%M:%S', which gives
something like "2018-03-06 23:44:27". There's no reason these
shouldn't be consistent, AFAICS.

Second, neither of the existing forms indicates the timezone of
the date/time, which is UTC. This isn't obvious for users: they
may well assume the date/time given is in their local timezone.
Ideally I'd actually like to do that, but doing it is hard; we
can at least explicitly give the timezone in the string.

Finally, we were constantly duplicating the format strings all
over the templates, where it would make much more sense to just
have a single filter function and use it every time we want to
do this. This enforces consistency, and means that if we do want
to change the format in future, we can change it in one place,
not dozens of them.

This was prompted by #3041 but doesn't entirely fix it.

Signed-off-by: Adam Williamson awilliam@redhat.com

Notes: as discussed a bit with @pingou on IRC, I picked the %Y-%m-%d %H:%M:%S format in preference to %b %d %Y %H:%M:%S because it's more locale-neutral. %b %d %Y, with the localization done on the server, reads nicely if the user happens to use or understand the same locale as the server, but may be much less intuitive for a user who doesn't (if the server's locale is English, it's going to say "Wed" for "Wednesday" no matter what the word for Wednesday is in the user's language, for e.g.) %Y-%m-%d is a pretty universally-understood (and standardized!) form.

Also, I really like that arrow.get() is sufficiently smart we can just re-use the same filter function for timestamps and datetime instances. :P

rebased onto 1f6dd3fff263a7fc1419b4bb20436537e28a2092

6 years ago

rebased onto 6205ad8

6 years ago

I was just wondering can we take the timezone of the user, I saw in one of the comment that @adamwill was talking about taking timezone from the user and then manipulate the UI accordingly and we always have the option of keeping a default timezone.

The timezone can be in the user setting.

@farhaan definitely doable but needs a little more work, this PR will make it easier though as there will then be only one place to change :)

Pull-Request has been merged by pingou

6 years ago

@farhaan the problem with doing that is basically the question of how we get the user timezone from javascript on the page (which is the only place we can get it) to the displayed date time (which is currently formatted in the Python page templating system). That's not an obvious flow, at least unless I'm missing something, it requires more substantial re-engineering than just a 'plug tab A into slot B' fix.

One way to do it would be to do the formatting of dates in Javascript instead of in the templating, for e.g., but that requires rejigging all this and also working out how to display something sensible when JS is disabled.