1

I have an important question. I understand Django saves data in "localtime", that is, UTC for my international app. So, if a user creates an "Event" with startdate (datetime object) 18:00, it will be 18:00 UTC. However, the user lives in Spain (UTC+1), so I store the timezone ("Europe/Madrid") in a different field. When I render it in the html, it will show the UTC time (but the user lives in UTC+1). Then, if I want to convert the time to a visiting user from Spain, I use the timezone tag to convert the datetime to Spain ... and now it will show 19:00.

    {% if logged_user_profile.timezone %}
        {% timezone logged_user_profile.timezone  %}
        <p class="text-muted font-weight-bold">
            {{ event.date_start|date:"h:i A" }} ({{ logged_user_profile.timezone }}
        </p>
        {% endtimezone %}
    {% else %}
        <p class="text-muted font-weight-bold">
         {{ event.date_start|date:"h:i A" }} ({{ owner_profile.timezone }})
        </p>
    {% endif %}

The Spain case is only an example. I guess my question is broad, which is the best way to deal with timezones in django. I need to store the datetime of an event and show it to visiting users in their localtime, if the stored datetime is in UTC, it will not reflect the datetime the user that created the event had in mind when I add a timezone to it.

Alejandro Veintimilla
  • 10,743
  • 23
  • 91
  • 180

2 Answers2

2

If you're handling timezones yourself, which you may need to in order to handle some corner cases (especially the times of future events in case the government of Spain changes the timezone between now and when the event takes place), you don't really want Django to also do it.

To handle it yourself, you want a "naive" date time field, which is not built in to Django; there are plug-ins that supply it (such as django-datetime-utc), which is probably easiest. Otherwise, if you need to use built-in field types only, you can use separate DateField and TimeField columns.

Note that handling timezones yourself will be annoying; if you don't need to take care of the rare corner cases, or have some other specialised requirement, it's probably easier to let Django do it, as explained by Antoine Pinsard's answer.

Jiří Baum
  • 6,697
  • 2
  • 17
  • 17
1

First thing, you have to set USE_TZ = True in your settings and TIME_ZONE to the default timezone (if you don't know the timezone of your user yet) (it's recommended to be the timezone of your server but you should be able to set a different one if needed). This will make django use timezone-aware datetimes.

Second thing, you should create a middleware that sets the correct timezone according to your user. In your case, it should be something like that:

import pytz

from django.utils import timezone

class TimezoneMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        tzname = None
        if request.user.is_authenticated:
            tzname = request.user.timezone  # (or request.user.user_profile.timezone)
        if tzname:
            timezone.activate(pytz.timezone(tzname))
        else:
            timezone.deactivate()
        return self.get_response(request)

Don't forget to add your middleware to your MIDDLEWARE setting.

And that's it. Django should handle everything correctly on its own with this. You might encounter some edge cases that you'll have troubleshoot and fix consequently, but basically, that's it.

However, note that the datetimes already stored in your database are wrong and can't be fixed as they were assumed in the wrong timezone (UTC instead of Europe/Spain for instance).

See full documentation on timezones.

Antoine Pinsard
  • 33,148
  • 8
  • 67
  • 87
  • Thanks for your answer. So, if the user wants to create an event that starts at 18:00 in Spain (UTC+1), should I store it as 17:00 (UTC)? And then change it when I show it to a visitor? – Alejandro Veintimilla Nov 23 '20 at 21:43
  • 1
    You can't reliably convert the times of future events to and from UTC, because the government of Spain could change the timezone between now and when the event takes place... – Jiří Baum Nov 24 '20 at 07:45
  • @AlejandroVeintimilla It depends on your database and settings. But in any case, you don't have to worry about it, django will handle conversion when needed. See https://docs.djangoproject.com/en/stable/ref/settings/#time-zone for more information. – Antoine Pinsard Nov 24 '20 at 12:49
  • Note that if your database supports timezones (like PostgreSQL), your datetimes will be stored in local time, which avoids the future datetimes issue highlighted by @sabik. – Antoine Pinsard Nov 24 '20 at 12:57
  • PostgreSQL supports both "with timezone" and "without timezone", so you need to check which one it is. And, possibly, whether it actually handles the future datetimes issue... – Jiří Baum Nov 24 '20 at 21:21
  • @AntoinePinsard Thanks for your comments. I'm not sure what do you mean by "not worry". If localtime is UTC and the user submits an event that starts at 19:00 UTC+1, should I save it as 18:00? – Alejandro Veintimilla Nov 24 '20 at 22:03
  • 1
    By "not worry", I mean "not care" about it. Once you set up the middleware to activate timezones, you don't have to bother about how you should store them, just let Django handle it. – Antoine Pinsard Nov 24 '20 at 22:25