diff options
-rw-r--r-- | fietsboek/__init__.py | 1 | ||||
-rw-r--r-- | fietsboek/jinja2.py | 40 | ||||
-rw-r--r-- | fietsboek/static/fietsboek.js | 8 | ||||
-rw-r--r-- | fietsboek/templates/details.jinja2 | 2 | ||||
-rw-r--r-- | fietsboek/templates/layout.jinja2 | 3 |
5 files changed, 52 insertions, 2 deletions
diff --git a/fietsboek/__init__.py b/fietsboek/__init__.py index 4a28dd4..9de9cbd 100644 --- a/fietsboek/__init__.py +++ b/fietsboek/__init__.py @@ -67,5 +67,6 @@ def main(global_config, **settings): jinja2_env = config.get_jinja2_environment() jinja2_env.filters['format_decimal'] = fiets_jinja2.filter_format_decimal jinja2_env.filters['format_datetime'] = fiets_jinja2.filter_format_datetime + jinja2_env.filters['local_datetime'] = fiets_jinja2.filter_local_datetime return config.make_wsgi_app() diff --git a/fietsboek/jinja2.py b/fietsboek/jinja2.py index def61a6..6043791 100644 --- a/fietsboek/jinja2.py +++ b/fietsboek/jinja2.py @@ -1,5 +1,8 @@ """Custom filters for Jinja2.""" +import datetime + import jinja2 +from markupsafe import Markup from babel.numbers import format_decimal from babel.dates import format_datetime @@ -37,3 +40,40 @@ def filter_format_datetime(ctx, value): request = ctx.get('request') locale = request.localizer.locale_name return format_datetime(value, locale=locale) + + +@jinja2.pass_context +def filter_local_datetime(ctx, value): + """Format a UTC datetime to show in the user's local timezone. + + This is done by embedding the UTC timestamp in the page, such that we can + do some client-side magic and replace it with the time in the user's local + timezone. As a fallback value, we do apply the locale's standard + formatting, in case JavaScript is disabled - however, that will not + represent the time in the user's timezone, but in UTC. + + :param ctx: The jinja context, passed automatically. + :type ctx: jinja2.runtime.Context + :param value: The value to format. + :type value: datetime.datetime + :return: The formatted date. + :rtype: Markup + """ + # If we get a naive object in, we assume that we got it from the database + # and we have to treat it like a UTC-aware object. This happens when we + # access object's DateTime columns directly, as SQLAlchemy only returns + # naive datetimes. + if value.tzinfo is None: + value = value.replace(tzinfo=datetime.timezone.utc) + else: + value = value.astimezone(datetime.timezone.utc) + + request = ctx.get('request') + locale = request.localizer.locale_name + fallback = Markup.escape(format_datetime(value, locale=locale)) + + # Forget about the fractional seconds + timestamp = int(value.timestamp()) + return Markup( + f'<span class="fietsboek-local-datetime" data-utc-timestamp="{timestamp}">{fallback}</span>' + ) diff --git a/fietsboek/static/fietsboek.js b/fietsboek/static/fietsboek.js index d27e21e..515f45e 100644 --- a/fietsboek/static/fietsboek.js +++ b/fietsboek/static/fietsboek.js @@ -150,4 +150,12 @@ document.addEventListener('DOMContentLoaded', function(event) { $("#archiveDownloadButton").disabled = (checked.length == 0); }); }); + + /* Format all datetimes to the local timezone */ + document.querySelectorAll(".fietsboek-local-datetime").forEach((obj) => { + let timestamp = obj.attributes["data-utc-timestamp"].value; + let date = new Date(timestamp * 1000); + let intl = new Intl.DateTimeFormat(LOCALE, {dateStyle: "medium", timeStyle: "medium"}); + obj.innerHTML = intl.format(date); + }); }); diff --git a/fietsboek/templates/details.jinja2 b/fietsboek/templates/details.jinja2 index 4d88224..e22efbc 100644 --- a/fietsboek/templates/details.jinja2 +++ b/fietsboek/templates/details.jinja2 @@ -150,7 +150,7 @@ {{ comment_md_to_html(comment.text) }} </div> <div class="card-footer text-muted"> - {{ comment.date | format_datetime }} + {{ comment.date | local_datetime }} </div> </div> {% endfor %} diff --git a/fietsboek/templates/layout.jinja2 b/fietsboek/templates/layout.jinja2 index a03b7c1..83a8f33 100644 --- a/fietsboek/templates/layout.jinja2 +++ b/fietsboek/templates/layout.jinja2 @@ -19,7 +19,8 @@ <link href="{{request.static_url('fietsboek:static/theme.css')}}" rel="stylesheet"> <script> -var FRIENDS_URL = "{{ request.route_url('json-friends') }}"; +const FRIENDS_URL = "{{ request.route_url('json-friends') }}"; +const LOCALE = "{{ request.localizer.locale_name.replace('_', '-') }}"; </script> </head> |