【问题标题】:Django saving datetime based on per object timezoneDjango根据每个对象时区保存日期时间
【发布时间】:2013-10-30 14:26:12
【问题描述】:

对于我的大多数应用程序,使用 TIME_ZONE 和 USE_TZ 设置保存日期时间都很好。我的问题是,我怎样才能完成保存具有 UTC 日期时间的模型,但是设置了日期时间以便转换回用户输入的时区是正确的?下面给出了模型、视图、表单和 html。如果 USE_TZ = False 在 settings.py 文件中,此代码将起作用,但我想为项目中的其他所有内容保留时区。

型号:

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>

例子:

我希望能够输入“美国/阿拉斯加”的时区和今天 13:00 的日期时间,将其保存为 UTC 值,然后能够转换回“美国/阿拉斯加”并获得正确的值。

基本上我试图将一个模型的日期时间保存在与我的应用程序不同的时区,其中时区由用户以与指定日期时间相同的形式指定。

【问题讨论】:

  • 你看过time zones吗?
  • kroolik,是的,我已经研究过时区并正在使用它们。例如,在我的 settings.py 文件中,我将 TIME_ZONE 设置为使用“太平洋/檀香山”,并且所有保存为 UTC 的日期都可以正常工作。我的问题不在于显示时区信息,而是使用与我的服务器不同的用户指定时区保存对象的日期时间。我希望 django 使用在同一模型上指定的时区保存对象的日期时间。我可以用我想要的时区替换 tzinfo,但这会导致结果不正确,因为在保存之前没有计算 DST。
  • 在会话中保留用户的时区不够吗?或者您需要更精细的时区选择方式?
  • 同样,这与用户时区无关。这是在模型上计算日期时间,其中模型还包含时区。日期时间需要独立于用户或 django 的时区设置来计算。

标签: python django datetime timezone pytz


【解决方案1】:

我在对象级时区方面遇到了同样的问题。

我找到了this blog entry。它并不完美,但可以工作!而且不太复杂。加上处理管理员。

在此处粘贴 sn-ps:

确保在所需时区处理表单:

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)

【讨论】:

    【解决方案2】:

    编辑:表单域的 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-07-25
      • 2017-06-26
      • 1970-01-01
      • 2020-12-03
      • 2020-06-02
      • 1970-01-01
      • 1970-01-01
      • 2018-01-03
      相关资源
      最近更新 更多