为什么在Django中将`timezone.now`应用为`DateField`的默认值时,显示未来日期?

huangapple go评论46阅读模式
英文:

Why is timezone.now showing a future date when being applied as a default value to DateField in django

问题

以下是我项目中相关的设置:

settings.py

TIME_ZONE = "US/Mountain"
USE_TZ = True

models.py(使用timezone.now

class Submission(models.Model):
    date_created = models.DateField(
        default=timezone.now,
        blank=True,
    )

    datetime_created = models.DateTimeField(
        default=timezone.now,
    )

如果我在2023年03月01日的下午5点MST创建一个Submission,那么以下是我得到的值:

  • date_created: "2023-03-02"
  • datetime_created: "2023-03-01 17:00:00-07:00"

为什么date_created会使用后续日期的日期?

英文:

Here are the relevant settings in my project:

settings.py

TIME_ZONE = "US/Mountain"
USE_TZ = True

models.py (using timezone.now)

class Submission(models.Model):
    date_created = models.DateField(
        default=timezone.now,
        blank=True,
    )

    datetime_created = models.DateTimeField(
        default=timezone.now,
    )

If I create a Submission at 5PM MST on 03/01/2023 these are the values I get:

  • date_created: "2023-03-02"
  • datetime_created: "2023-03-01 17:00:00-07:00"

Why would date_created be using the date of the following date?

答案1

得分: 1

我认为发生这种情况的原因是因为"日期"(没有时间)不是时区感知的。

您可以在DateField源代码中看到Django自动将日期时间转换为UTC的地方

    def _check_fix_default_value(self):
        """
        Warn that using an actual date or datetime value is probably wrong;
        it's only evaluated on server startup.
        """
        if not self.has_default():
            return []

        value = self.default
        if isinstance(value, datetime.datetime):
            value = _to_naive(value).date()  # <————— 在这里!
        elif isinstance(value, datetime.date):
            pass
        else:
            # 没有显式的日期/日期时间值--不需要检查
            return []
        # 在这一点上,value是一个日期对象。
        return self._check_if_value_fixed(value)

个人建议使用DateTimeField,以便保留时区信息,仅在需要时访问.date,但如果出于某种原因不想使用auto_now_add选项,您也可以使用datetime.date.today

import datetime

class Submission(models.Model):
    date_created = models.DateField(
        default=datetime.date.today,
        blank=True,
    )
英文:

I think the reason this happens is because the "date" (without the time) isn't timezone aware.

You can see in the DateField source where Django automatically converts a datetime back to UTC:

    def _check_fix_default_value(self):
        &quot;&quot;&quot;
        Warn that using an actual date or datetime value is probably wrong;
        it&#39;s only evaluated on server startup.
        &quot;&quot;&quot;
        if not self.has_default():
            return []

        value = self.default
        if isinstance(value, datetime.datetime):
            value = _to_naive(value).date()  # &lt;————— HERE!
        elif isinstance(value, datetime.date):
            pass
        else:
            # No explicit date / datetime value -- no checks necessary
            return []
        # At this point, value is a date object.
        return self._check_if_value_fixed(value)

Personally I would use a DateTimeField so you keep the timezone information, and just access .date when you need it, but you should be able to use datetime.date.today (assuming you don't want to use the auto_now_add option for some reason):

import datetime

class Submission(models.Model):
    date_created = models.DateField(
        default=datetime.date.today,
        blank=True,
    )

答案2

得分: 0

如果您希望date_created字段存储正确的创建日期,可以使用以下代码修改默认值:default=timezone.now().date()

.date()方法返回DateTime对象的日期部分,这将将date_created字段设置为"2023-03-01"(在您的情况下,需要创建新的数据库条目),这是正确的创建日期。

因此,您可以按照以下方式尝试代码:

class Submission(models.Model):
    date_created = models.DateField(
        default=timezone.now().date(),
        blank=True,
    )

    datetime_created = models.DateTimeField(
        default=timezone.now,
    )

date_created字段被设置为"2023-03-02"的原因是DateField类型仅存储日期,不包含时间信息,因此当您将默认值设置为timezone.now时,它将采用settings.py文件中指定的时区信息。

由于您在settings.py文件中将时区设置为"US/Mountain",它比UTC时间晚7小时。因此,当数据记录发生在2023年03月01日下午5点MST时,实际的UTC日期为2023年02月01日(MST和UTC之间的时差)。这就是为什么date_created的值被设置为"2023-03-02"的原因。

您可以查看时区转换器

英文:

If you want the date_created field to store the correct date of creation, you can modify the default value with this: default=timezone.now().date()

The .date() method returns the date portion of a DateTime object and this will set the date_created field to "2023-03-01" (In your case - New DB Entry will require), which is the correct date of creation.

So you can try the code as per below:

class Submission(models.Model):
    date_created = models.DateField(
        default=timezone.now().date(),
        blank=True,
    )

    datetime_created = models.DateTimeField(
        default=timezone.now,
    )

The reason of the date_created field is set to "2023-03-02" is that the DateField type only stores the date, without the time information so when you set the default value to timezone.now it will take the time zone information specified in your settings.py file.
As you have set the time zone to "US/Mountain" on settings.py file, which is 7 hours behind UTC. Since the TIME_ZONE setting was set to "US/Mountain" when the data entry happens at 5 PM MST on 03/01/2023, the actual date in UTC was 02/01/2023 (time difference between MST and UTC). This is the reason why the value for date_created was set to "2023-03-02" for you.
You can check timezone converter

huangapple
  • 本文由 发表于 2023年3月7日 06:58:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/75656591.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定