13

有时您需要从用户那里收集时间而不收集相关日期。例如,如果用户正在配置每天同时运行的重复事件。不过, DjangoTimeField不使用时区。但是,在这种特殊情况下(并且可能在您单独记录时间的任何时候),时区是一个重要因素。那么,如何存储时区感知时间?

4

3 回答 3

8

答案是你没有。要使时间能够感知时区,它必须有一个与之关联的日期。想想夏令时......我的解决方案是DateTimeField在模型上使用 a 并像这样覆盖表单:

# 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()
于 2013-11-05T00:47:17.090 回答
0

这是未经测试且不完整的:

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)

这将使用 Postgres 的time with time zone数据类型。如果您将格式为 'HH:MM:SS.mmmmmm+HH:MM' 的字符串传递给它,它将会中断,并且使用 auto_now 将尝试节省一个幼稚的时间(不确定这是否会引发错误)。

编辑

在通用后端代码中,如果您尝试插入时区不是 UTC 的时间,则会引发异常。

编辑 2

我添加了一个更新get_db_prep_value以将提供time的时区转换为字符串,但它仅在提供的时区输出 utc 偏移量时才有效(如果没有日期,它可能不明确)。

这似乎time with time zone有点误导......据我所知,它实际上存储了一个带有 UTC 偏移量而不是时区的时间。因此,很难获取返回的值、添加日历日期并返回关于夏令时的正确时间。

于 2016-07-06T17:13:28.140 回答
0
  1. 在你的模型中time_field = models.TimeField(null=True)

  2. 创建将时间转换为日期时间的函数,然后将其转换为 UTC 日期时间,然后保存。Django 将仅从日期时间自动获取时间。

  3. 创建一个函数,将存储在数据库中的时间转换为当前时区

  • 注意:您应该在中间件中激活当前用户的时区。
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)

  1. 创建使用函数的自定义字段序列化程序以进行转换。

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

于 2021-08-27T10:35:52.497 回答