For the majority of my application saving datetimes with the TIME_ZONE and USE_TZ settings are fine. My question is, how can I accomplish saving a model with a datetime that is in UTC, but the datetime is set so that converting back to the users inputted timezone is correct? Model, view, form and html given below. This code will work if USE_TZ = False in the settings.py file, but I would like to keep the timezone for everything else in the project.
Model:
class TZTestModel(models.Model):
timezone = TimeZoneField()
dt = models.DateTimeField()
View:
class TZTestView(LoginRequiredMixin, TemplateView):
template_name = "tz_test.html"
def get_context_data(self, **kwargs):
return {
'form': self.form
}
def dispatch(self, request, *args, **kwargs):
self.form = TZTestForm(self.request.POST or None)
return super(TZTestView, self).dispatch(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
if self.form.is_valid():
self.form.save()
return self.render_to_response(self.get_context_data())
Form:
class TZTestForm(forms.ModelForm):
class Meta:
model = TZTestModel
def clean(self):
timezone = self.cleaned_data['timezone']
dt = self.cleaned_data['dt']
dt = timezone.localize(dt)
self.cleaned_data['dt'] = pytz.UTC.normalize(dt.astimezone(pytz.UTC))
return self.cleaned_data
Template:
<html>
<body>
<form method="post">
{% csrf_token %}
{{ form }}
<input type="submit">
</form>
</body>
</html>
Example:
I would like to be able to enter a timezone of 'US/Alaska' and a datetime of today at 13:00, save that as it's UTC value, then be able to convert back to 'US/Alaska' and get the correct value.
Essentially I am trying to save one model's datetime in a different timezone than my application, where the timezone is specified by the user in the same form that the datetime is specified in.
I have had the same issue with object-level timezones.
I found this blog entry. It's not perfect but it works! and is not too complex. Plus handling the Admin is dealt with.
Pasting the snippets here:
def view(request):
if request.method == 'POST':
tz_form = TimeZoneForm(request.POST)
if tz_form.is_valid():
tz = tz_form.cleaned_data['event_time_zone']
timezone.activate(tz)
# Process the full form now
else:
# assuming we have an event object already
timezone.activate(event.event_time_zone)
# Continue to create form for display on the web page
class EventAdmin(admin.ModelAdmin):
list_display = [..., 'event_datetime_in_timezone', ...]
def event_datetime_in_timezone(self, event):
"""Display each event time on the changelist in its own timezone"""
fmt = '%Y-%m-%d %H:%M:%S %Z'
dt = event.event_datetime.astimezone(pytz_timezone(event.event_time_zone))
return dt.strftime(fmt)
event_datetime_in_timezone.short_description = _('Event time')
class EventAdmin(admin.ModelAdmin):
# ...
# Override add view so we can peek at the timezone they've entered and
# set the current time zone accordingly before the form is processed
def add_view(self, request, form_url='', extra_context=None):
if request.method == 'POST':
tz_form = TimeZoneForm(request.POST)
if tz_form.is_valid():
timezone.activate(tz_form.cleaned_data['event_time_zone'])
return super(EventAdmin, self).add_view(request, form_url, extra_context)
class EventAdmin(admin.ModelAdmin):
# ...
# Override change view so we can peek at the timezone they've entered and
# set the current time zone accordingly before the form is processed
def change_view(self, request, object_id, form_url='', extra_context=None):
if request.method == 'POST':
tz_form = TimeZoneForm(request.POST)
if tz_form.is_valid():
timezone.activate(tz_form.cleaned_data['event_time_zone'])
else:
obj = self.get_object(request, unquote(object_id))
timezone.activate(obj.event_time_zone)
return super(EventAdmin, self).change_view(request, object_id, form_url, extra_context)