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?
问题:
回答1:
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:
# Model
class MyModel(models.Model):
time_of_day = models.DateTimeField()
# Form Fields
from django.forms.util import from_current_timezone
from django.forms.util import 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
class MyForm(forms.ModelForm):
time_of_day = TzAwareTimeField()
回答2:
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.