Django's handling of datetimes in the admin interface can be greatly improved

Some background info first. Here are my Django settings:

USE_I18N = True
USE_L10N = True
USE_TZ = True
TIME_ZONE = "UTC"

And you should know that I am in the Amsterdam timezone, UTC+1.

I don’t really understand the way Django handles its DateTimeField in the admin forms. Like, the warning “Note: You are 1 hour ahead of server time.” is so incredibly unhelpful. It doesn’t make it clear if I should enter dates in my local time or in UTC. Why doesn’t it just say “Date and time must be entered as UTC”? That would make it super clear how I should enter the values, which the note does not.

https://media.hachyderm.io/media_attachments/files/113/560/954/884/415/973/original/2b3c138c50bb1b04.png

At the moment I am adding help_text=f"Date and time must be entered as {settings.TIME_ZONE}." to all my DateTimeField instances, and hiding Django’s note with the following css:

.timezonewarning { 
  display: none; 
}

Because our admins just don’t know what to enter in these fields with the default note.

And then when these values are shown in the admin interface, it shows them in UTC as well, rather than translated to the browser’s timezone. Which is fine for me, but why? Why even show that warning then? For example, I enter 2024-11-28 15:00:00 into one of the my DateTimeFields. This is entered as a UTC datetime, right? In the database it’s stored as 2024-11-28 16:00:00+01, which is indeed 15:00 in UTC. (Weird that it doesn’t store it that way, but ok.)

And then when displaying this value in the admin, it shows Nov. 28, 2024, 3 p.m.. Why does it not show 4 p.m., aka my local timezone? Why show that weird note about being ahead of server time? And why is the time not stored with +00 in the db?

Django doesn’t know my user’s timezone, since this is not something that’s stored in the User model. But, it can use the browser’s timezone to show these dates and times in the local timezone, right? Or another option: add a timezone picker to the top menu, next to the theme switcher for example.

The ​docs say this:

When support for time zones is enabled, Django stores datetime information in UTC in the database, uses time-zone-aware datetime objects internally, and translates them to the end user’s time zone in templates and forms.

So I guess I just don’t really understand the logic behind the timezone handling, and why it shows UTC everywhere in the admin.

I see two clear improvements here:

  1. Show the timezone that the date/time is supposed to be entered in, as supposed to showing “You are X hours ahead of server time." Seriously, nobody on my team knew what to enter in these fields until I changed the help_text and hid that warning.
  2. When showing the value of this field, for example as part of list_display, either show the date/time translated to the browser’s timezone, or show the timezone. So instead of showing Nov. 28, 2024, 3 p.m., show Nov. 28, 2024, 3 p.m. (UTC)
3 Likes

From Time zones | Django documentation | Django :

When support for time zones is enabled, Django stores datetime information in UTC in the database, uses time-zone-aware datetime objects internally, and translates them to the end user’s time zone in templates and forms.

Question: does it actually use the user’s time zone? Or does it just use the value of settings.TIME_ZONE? Which I wouldn’t call the end user. After all, I have admins in many parts of the world all using the same Django admin, so settings.TIME_ZONE does NOT reflect an “end user”.

I guess the big question is what settings.TIME_ZONE is used for. Obviously it’s not used for storing the datetimes (that’s always as UTC after all), so then it must be used for display purposes, which Settings | Django documentation | Django confirms:

When USE_TZ is True , this is the default time zone that Django will use to display datetimes in templates and to interpret datetimes entered in forms.

I can’t leave TIME_ZONE empty or Null, then I get an error. I have to choose a value, and the default value of America/Chicago is definitely not useful for us. But like I said, with users in different parts of the world this setting doesn’t really mean much. So why not use the browser’s timezone to display these values in the admin? Or at least show the timezone in the admin, I’d be OK with that as well (like Nov. 28, 2024, 3 p.m. (UTC)).

I also think the time zones documentation should be updated, to remove the text about “end user’s time zone” and instead link to settings.TIME_ZONE.

I agree that the message displayed in the admin page is not helpful and is confusing. I would much rather this message say exactly what is happening, i.e. “The dates and times entered and displayed on this page are using the server’s timezone and not your local timezone. The timezone on the server is ‘UTC’ and you are currently 13 hours ahead of UTC.” Obviously, this would need to be a bit more succinct, but the current message doesn’t really convey the true meaning of what’s going on.

I also agree, that it would be better to avoid all of this and to have the admin display and accept dates and times in the users’ local timezone instead. But after reading through the initial ticket that implemented the current message that is displayed, and clicking through to some of the linked issues, it’s apparent that there is a long history with timezone issues related to the admin.

I don’t know enough about the admin internals to comment on how easy it would be to properly fix the timezone issues, but I would advocate for (and would be willing to help) come up with a better message on the admin pages next to date and time fields.

That’s pretty long, yeah. Instead of writing out “and you are currently 13 hours ahead of UTC” I would personally link out to https://time.is/{{ timezone }} but I understand that that’s probably different for Django core.

Also had a look at the original ticket, and noticed that the same timezone dropdown in the header was suggested there.

And another suggestion to only show the note when the “now” button is pressed, which would’ve been an improvement as well.

I do think a timezone selection widget makes total sense, and then the note or even my version of the note wouldn’t be needed. You always enter everything in your selected timezone, and show it using that timezone as well.

Hmmm indeed, it’d be much more helpful for the field to say in what time zone dates/times are expected. Probably in all cases but definitely when the user’s current time zone isn’t the one the fields expect. I like your suggestion of new help text but think the current warning does help in some way in warning the user about a likely mismatch. So perhaps something like:

help_text=f"""
Date and time must be entered as {settings.TIME_ZONE}.
You are {x} hour {ahead_or_behind} of {settings.TIME_ZONE}.
"""

This shouldn’t be displayed only when pressing the “now” button. Like all help text, it’s more helpful if it’s visible before you interact with the field.


Re whether to display the dates in one time zone or another in the admin – there’s no way for users of the admin to guess, so definitely one way or another the admin needs to say in what time zone the dates are. Doesn’t necessarily need to be next to each and every date (though that’d be the simplest), but at least some way for the user to know.

Note this is a topic there are a thousand tickets and existing discussions about, particularly for the form widgets, particularly in the context of moving to the HTML5 date and datetime-local inputs. So if you want to pursue this you will need to do a fair bit of research of past discussions, as with all time zone code there’s a lot of considerations there.

1 Like

Changing the note from what it currently is to mentioning the actual timezone would be a huge improvement, and hopefully this can be done without going through all those past tickets and discussions.

And I 100% agree that it should always be visible. Only in the case of the current hard to understand note would it have been better to only show it when the user presses the now button :sweat_smile:

I do think something needs to be done about the displaying of dates and times, but that’s a separate issue to tackle. The note is the important bit.

1 Like

Thanks for bringing this up, there’s definitely room for improvement here.

One thing that might explain this weird Django behavior is that there’s no such thing as a “browser’s timezone” as far as Django is concerned. Browser do not send information about the end user’s timezone when talking to a server. This information is only available client-side via a javascript API. And even then, I believe the only information you get is not a time zone name, but the offset from UTC.

When the documentation mentions the “user’s timezone” as you point out, that’s incorrect. It means the server’s timezone.

Since these dates and times are shown in the browser, they can be modified using javascript, right? You know the UCT offset of the server’s timezone, you know the browser’s UTC offset (in JS), so you can translate between them? That’s how the current note does it too, I think (correct me if I’m wrong).

It should be possible to modify every shown date and time using javascript I would think. But yeah it’s not ideal. So there are two other options:

  1. Just show everything in the server’s timezone (like already happens) but at least show the timezone.
  2. Add a timezone picker in the admin header, so the user can pick their own timezone.

Still, this is separate from improving the form note, which I think is actually (by far) the most important part as it causes real confusion. And once that note is clear, people entering dates into the admin will also easier understand how they are shown in the admin.

1 Like

In Django’s current take on timezone, I personally wouldn’t want any Javascript solution to change the timezone in the browser.

I’m +1 on improving the message in the admin as a good start in this area.

If we wanted to support timezones in Django then I would suggest it get’s proposed ‘properly’.

You know the UTC offset for the server’s timezone right now, and the UTC for the user’s timezone right now. Using that difference to translate an arbitrary timestamp is not correct though, as it assumes that the gap between server UTC offset and user UTC offset is constant.

But it is far from guaranteed to be – and no, this isn’t a “technically, this could go wrong”. Even just the US and Europe switching to daylight savings time two weeks apart will break this assumption, and will make you select a time that is off by an hour (or, even more fun, just doesn’t exist). If you want to do datetime arithmetic, you’ll need to deal with timezones rather than offsets.

That said, with modern browsers, this is possible without resorting to library use: You can get the user’s timezone:

>>> Intl.DateTimeFormat().resolvedOptions().timeZone
"Europe/Berlin" 

Date is good enough to know not to generate invalid times – I tested this by adding an hour to the time of spring DST adjustment:

>>> d = new Date("2024-03-31T00:15:00Z")
>>> d.toLocaleString(undefined, {"timeZone": "Europe/Berlin"})
"31/03/2024, 01:15:00"
>>> d.setTime(d.getTime() + (60 * 60 * 1000))
>>> d.toLocaleString(undefined, {"timeZone": "Europe/Berlin"})
"31/03/2024, 03:15:00"

It can then also output a Date object in a chosen format and timezone:

>>> d.toLocaleString(undefined, {"timeZone": "Europe/Berlin"})
"31/03/2024, 03:15:00"
>>> d.toLocaleString(undefined, {"timeZone": "Australia/Adelaide"})
"31/03/2024, 11:45:00"

All this isn’t to say that this is what we should do, I just wanted to set the record straight re: current browser capabilities.

6 Likes