There are occasions when you need to collect a time from a user without collecting an associated date. For example, if the user is configuring a repeating event that runs every day at the same time. Django's TimeField
doesn't play with timezones though. However, in this particular case (and probably any time you record a time by itself), timezone is an important factor. So, how do you store a timezone-aware time?

- 12,896
- 4
- 48
- 49
3 Answers
The answer is you don't. For a time to be timezone aware, it has to have a date associated with it. Think of daylight savings... My solution for this was to use a DateTimeField
on the model and to override the form like so:
# models.py
class MyModel(models.Model):
time_of_day = models.DateTimeField()
# form_fields.py
from django.forms.util import from_current_timezone, to_current_timezone
from django.utils import timezone
class TzAwareTimeField(forms.fields.TimeField):
def prepare_value(self, value):
if isinstance(value, datetime.datetime):
value = to_current_timezone(value).time()
return super(TzAwareTimeField, self).prepare_value(value)
def clean(self, value):
value = super(TzAwareTimeField, self).to_python(value)
dt = to_current_timezone(timezone.now())
return dt.replace(
hour=value.hour, minute=value.minute,
second=value.second, microsecond=value.microsecond)
# forms.py
class MyForm(forms.ModelForm):
time_of_day = TzAwareTimeField()
-
9Your opening statement (and Django’s behaviour) isn’t consistent with how Python handles `time` objects. A Python `time` object may have a `tzinfo` field, allowing for a timezone-aware `time` object with no associated date, which _is_ useful in some circumstances, like when the date is stored elsewhere. I just came across this issue with `open_time` and `close_time` `TimeField`s on a `Day` model in Django. Those times would ideally be timezone-aware, but can’t be because Django doesn’t support that. – jbg Mar 24 '14 at 21:32
-
This is old, but still useful, so I want to point out that I think you need to wrap the return value of the `clean` method in `from_current_timezone`. – John Rork Jul 10 '17 at 19:37
-
1@josh I know it's late, but other people may read this. Your assert that a timezone needs a date is false. There is a difference between a point in time (datetime) and a time of day(time). 11:00, as in "Every Friday at 11:00", is represented by a time. That time may be joined with many different dates to make a point in time, whereupon you can consider DST, if you need to convert to another time zone. Try scheduling an international meeting for Fridays at 11:00, and you'll quickly see you need to know the time zone, but not the date. – ThoughtfulHacking Jul 20 '21 at 07:54
-
How to do the same for DateField? – Ali Husham Aug 25 '21 at 09:46
-
@josh is correct, while a python `time` object can have `tzinfo` associated with it, that value is informational only. It is *not possible* to convert from one timezone to another without knowing a date as well. For example, it's currently 3:45PM PDT as I type this, which is 22:00 UTC in the fall but during the summer the same time of day here would be 23:00 UTC. – James Emerton Oct 07 '22 at 22:46
This is untested and incomplete:
class TimeFieldWithZone(TimeField):
def db_type(self, connection):
if (connection.settings_dict['ENGINE'] ==
'django.db.backends.postgresql_psycopg2'):
return 'time with time zone'
raise Exception('Unsupported database type')
def get_db_prep_value(self, value, *args, **kwargs):
try:
return super(TimeFieldWithZone, self).get_db_prep_value(
value, *args, **kwargs)
except ValueError:
return six.text_type(value)
This will use Postgres' time with time zone
datatype. It will break if you pass it a string in the format 'HH:MM:SS.mmmmmm+HH:MM' and using auto_now will try to save a naive time (not sure if that throws an error).
edit
In the generic backend code an exception is thrown if you try inserting a time with a timezone other than UTC.
edit 2
I added an updated get_db_prep_value
to convert a provided time
with timezone into a string, but it only works if the provided timezone outputs a utc offset (which may be ambiguous without a date).
It seems time with time zone
is a little misleading... As far as I can tell, it actually stores a time with a UTC offset and NOT a timezone. So it'd be difficult to take the returned value, add a calendar date, and get back the proper time with regard to daylight savings time.

- 9,914
- 3
- 52
- 82
in your model
time_field = models.TimeField(null=True)
create functions that convert the time to datetime then convert it to UTC datetime then save it. Django will automatically take the time only from the datetime.
Create a function that take the time stored in the database and convert it to the current timezone
- Note: You should activate the timezone of the current user in the middleware.
import datetime
import pytz
from django.utils import timezone
def ConvertToUTC(parsed):
date_time = timezone.get_current_timezone().localize(parsed)
utc_date_time = date_time.astimezone(pytz.utc)
return utc_date_time
def ConvertToTimeZone(value, format):
value = value.strftime(format)
value = datetime.datetime.strptime(value, format)
value = value.astimezone(timezone.get_current_timezone())
return value.strftime(format)
- create custom field serializer that use the functions in order to make the conversions.
class TimeSer(Field):
default_error_messages = {
'invalid': 'Time has wrong format, expecting %H:%M:%S%z.',
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def to_internal_value(self, value):
try:
parsed = datetime.datetime.strptime(value, '%H:%M:%S')
except (ValueError, TypeError) as e:
pass
else:
return ConvertToUTC(parsed)
self.fail('invalid')
def to_representation(self, value):
if not value:
return None
if isinstance(value, str):
return value
if isinstance(value, datetime.time):
return ConvertToTimeZone(value,'%H:%M:%S')
return None

- 816
- 10
- 31