为啥Django时区设置会影响纪元时间?
Posted
技术标签:
【中文标题】为啥Django时区设置会影响纪元时间?【英文标题】:Why does the Django time zone setting effect epoch time?为什么Django时区设置会影响纪元时间? 【发布时间】:2013-06-12 05:11:33 【问题描述】:我有一个小型 Django 项目,可以将 MongoDB 中的数据转储导入 mysql。在这些 Mongo 转储中是存储在纪元时间中的日期。无论时区如何,我都希望纪元时间相同,但我看到的是 Django TIME_ZONE 设置对 MySQL 中创建的数据有影响。
我一直在使用 MySQL UNIX_TIMESTAMP 函数测试我的数据库输出。如果我插入一个纪元为1371131402880
(包括毫秒)的日期,我将我的时区设置为'America/New_York'
,UNIX_TIMESTAMP 给我1371131402
,这是相同的纪元时间,不包括毫秒。但是,如果我将时区设置为 'America/Chicago'
,我会得到 1371127802
。
这是我将纪元时间转换为 Python datetime
对象的代码,
from datetime import datetime
from django.utils.timezone import utc
secs = float(epochtime) / 1000.0
dt = datetime.fromtimestamp(secs)
我试图通过在 datetime
对象上放置一个明确的时区来解决此问题,
# epoch time is in UTC by default
dt = dt.replace(tzinfo=utc)
PythonFiddle for the code
我已经单独测试了这个 Python 代码,它给了我预期的结果。但是,通过 Django 模型 DateTimeField 字段将这些对象插入 MySQL 后,它并没有给出正确的结果。
这是我的 MySQL 查询,
SELECT id, `date`, UNIX_TIMESTAMP(`date`) FROM table
我通过将此查询结果中的 unix 时间戳列与 MongoDB JSON 转储进行比较来测试这一点,以查看时代是否匹配。
这里到底发生了什么?为什么时区会对纪元时间产生影响?
仅供参考,我使用的是 Django 1.5.1 和 MySQL-python 1.2.4。我还将 Django USE_TZ 标志设置为 true
。
【问题讨论】:
可能 MySQL 服务器的时区与您的应用程序的时区不匹配。问题在于 MySQL 将UNIX_TIMESTAMP
的参数解释为其私有时区中的本地时间。
为获得最佳结果,请始终在所有 MySQL 连接上设置 SET TIME_ZONE="+00:00"
,并将每个日期解释为 UTC。
术语 - “纪元”只是我们设置零的参考日期。大多数时间是 1970 年 1 月 1 日 UTC,但这并不意味着基于此日期的整数应称为“纪元时间”。其他系统中使用了许多其他纪元日期。
@Celada - 这对我来说似乎不对,但我不是 MySQL 专家。你能用一些参考链接备份它吗?如果是这样,请输入答案。我所知道的关于这种格式的日期的一切都表明 UTC 始终是参考时区。它似乎更有可能在某处更改类型或调用引入本地时区的函数。
好的,我刚刚通读了these mysql docs,现在我明白了。 Celada 确实对该函数说“参数”,这是一个包含格式化日期时间值的字符串。是的 - 它将TIME_ZONE
设置应用于此值是有道理的。我的观点是基础整数然后转换为UTC。因此,当表示为整数时,无论本地时区如何,UTC 始终是参考点。我只是陷入了措辞。应用。
【参考方案1】:
我不是 python 或 Django 大师,所以也许有人能比我回答得更好。但无论如何我都会猜测一下。
您说您将其存储在 Django DateTimeField
中,根据 the documents you referenced,将其存储为 Python datetime
。
看the docs for datetime
,我认为关键是理解“naive”和“aware”值的区别。
然后进一步研究,我遇到了this excellent reference。请务必阅读第二部分“朴素且有意识的日期时间对象”。这为 Django 控制了多少提供了一些背景信息。基本上,通过设置USE_TZ = true
,您要求 Django 使用 aware 日期时间而不是 naive 日期时间。
然后我回头看了你的问题。您说您正在执行以下操作:
dt = datetime.fromtimestamp(secs)
dt = dt.replace(tzinfo=utc)
查看fromtimestamp函数文档,发现了这段文字:
如果可选参数
tz
为None
或未指定,则timestamp
转换为平台的本地日期和时间,返回的datetime
对象是naive。
所以我认为你可以这样做:
dt = datetime.fromtimestamp(secs, tz=utc)
然后,在该函数的正下方,文档显示utcfromtimestamp
函数,所以它可能应该是:
dt = datetime.utcfromtimestamp(secs)
我对 python 的了解还不够,无法知道它们是否等效,但您可以尝试看看是否有区别。
希望其中之一会有所作为。如果没有,请告诉我。我非常熟悉 javascript 和 .Net 中的日期/时间,但我总是对这些细微差别如何在 Python 等其他平台中发挥不同的作用感兴趣。
更新
关于问题的 MySQL 部分,请查看this fiddle。
CREATE TABLE foo (`date` DATETIME);
INSERT INTO foo (`date`) VALUES (FROM_UNIXTIME(1371131402));
SET TIME_ZONE="+00:00";
select `date`, UNIX_TIMESTAMP(`date`) from foo;
SET TIME_ZONE="+01:00";
select `date`, UNIX_TIMESTAMP(`date`) from foo;
结果:
DATE UNIX_TIMESTAMP(`DATE`)
June, 13 2013 13:50:02+0000 1371131402
June, 13 2013 13:50:02+0000 1371127802
UNIX_TIMESTAMP
函数的行为似乎确实受到 MySQL TIME_ZONE
设置的影响。这并不奇怪,因为它在文档中。令人惊讶的是,无论设置如何,datetime
的字符串输出都具有相同的 UTC 值。
这就是我认为正在发生的事情。在UNIX_TIMESTAMP
函数的文档中,它说:
date
可以是DATE
字符串、DATETIME
字符串、TIMESTAMP
或格式为YYMMDD
或YYYYMMDD
的数字。
请注意,它并不是说它可以是DATETIME
- 它说它可以是DATETIME
字符串。所以我认为实际值在传递给函数之前被隐式转换为字符串。
所以现在看看this updated fiddle 显式转换。
SET TIME_ZONE="+00:00";
select `date`, convert(`date`, char), UNIX_TIMESTAMP(convert(`date`, char)) from foo;
SET TIME_ZONE="+01:00";
select `date`, convert(`date`, char), UNIX_TIMESTAMP(convert(`date`, char)) from foo;
结果:
DATE CONVERT(`DATE`, CHAR) UNIX_TIMESTAMP(CONVERT(`DATE`, CHAR))
June, 13 2013 13:50:02+0000 2013-06-13 13:50:02 1371131402
June, 13 2013 13:50:02+0000 2013-06-13 13:50:02 1371127802
您可以看到,当它转换为字符数据时,它会去除偏移量。因此,当然,现在当UNIX_TIMESTAMP
将此值作为输入时,它假设本地时区设置并因此获得不同的 UTC 时间戳是有意义的。
不确定这是否对您有帮助。您需要更深入地了解 Django 是如何调用 MySQL 进行读取和写入的。它真的使用UNIX_TIMESTAMP
函数吗?或者这正是你在测试中所做的?
【讨论】:
utcfromtimestamp()
是创建表示 UTC 时间的天真日期时间对象的正确函数。仅仅fromtimestamp()
会在本地时区返回一个幼稚的日期时间(在大多数情况下是错误的,除非您想立即在本地系统上显示它)。
感谢马特。我之前实际上是在玩utcfromtimestamp
,但运气不佳。它正在将 UTC 时间插入数据库,而 unix 时间戳甚至更远。我稍后会尝试发布更多信息。另外,我将不得不尝试一下您对secs, tz=utc
的推荐,至少这将为我节省一行代码:)
是的,请显示您使用UNIX_TIMESTAMP
函数测试的代码。如 Celada 所述,该代码可能受时区影响。
UNIX_TIMESTAMP 是一个 MySQL 函数,但我将添加查询。我还添加了一个 PythonFiddle @pythonfiddle.com/tests-for-epoch-issue
呃,我错过了关于字符串转换的那部分,这真的很不直观。写得很好,这实际上是如何工作的,我的确认测试可能有缺陷。以上是关于为啥Django时区设置会影响纪元时间?的主要内容,如果未能解决你的问题,请参考以下文章