对于我的大多数应用程序,使用TIME_ZONE和USE_TZ设置保存日期时间都很好。我的问题是,如何保存一个日期时间为UTC的模型,但是设置日期时间以便转换回用户输入的时区是正确的?下面给出的模型,视图,表单和html。如果settings.py文件中的USE_TZ = False,此代码将起作用,但我想保留项目中其他所有内容的时区。
型号:
class TZTestModel(models.Model):
timezone = TimeZoneField()
dt = models.DateTimeField()
查看:
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())
形式:
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
模板:
<html>
<body>
<form method="post">
{% csrf_token %}
{{ form }}
<input type="submit">
</form>
</body>
</html>
示例:
我希望能够输入'US / Alaska'的时区和今天13:00的日期时间,除了它的UTC值,然后能够转换回'US / Alaska'并获得正确的价值。
基本上我试图将一个模型的日期时间保存在与我的应用程序不同的时区中,其中时区由用户以指定日期时间的相同形式指定。
答案 0 :(得分:2)
我遇到了与对象级时区相同的问题。
我找到了this blog entry。它并不完美,但有效!并且不太复杂。加上管理员的处理。
将片段粘贴在这里:
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)
答案 1 :(得分:0)
编辑:表单字段的pastebin源:http://pastebin.com/j4TnnHTS 进一步讨论:https://code.djangoproject.com/ticket/21300
执行此操作的方法似乎是创建一个自定义表单字段,该字段返回一个天真的日期时间,然后将其转换为用户指定的时区,然后将其转换为UTC。
自定义字段:
class DateTimeNoTimeZoneField(forms.DateTimeField):
def to_python(self, value):
"""
Validates that the input can be converted to a datetime. Returns a
Python datetime.datetime object.
"""
if value in validators.EMPTY_VALUES:
return None
if isinstance(value, datetime.datetime):
return value
if isinstance(value, datetime.date):
return datetime.datetime(value.year, value.month, value.day)
if isinstance(value, list):
# Input comes from a SplitDateTimeWidget, for example. So, it's two
# components: date and time.
if len(value) != 2:
raise ValidationError(self.error_messages['invalid'])
if value[0] in validators.EMPTY_VALUES and value[1] in validators.EMPTY_VALUES:
return None
value = '%s %s' % tuple(value)
# Try to coerce the value to unicode.
unicode_value = force_text(value, strings_only=True)
if isinstance(unicode_value, six.text_type):
value = unicode_value.strip()
# If unicode, try to strptime against each input format.
if isinstance(value, six.text_type):
for format in self.input_formats:
try:
return self.strptime(value, format)
except (ValueError, TypeError):
continue
raise ValidationError(self.error_messages['invalid'])
形式:
class TZTestForm(forms.ModelForm):
dt = DateTimeNoTimeZoneField()
class Meta:
model = TZTestModel
def clean(self):
tz = self.cleaned_data['timezone']
dt = self.cleaned_data['dt']
dt = pytz.UTC.normalize(tz.localize(dt).astimezone(pytz.UTC))
self.cleaned_data['dt'] = dt
return self.cleaned_data