我有一个小的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)
我已经单独测试了这个Python代码,它给了我预期的结果。但是,在通过Django模型DateTimeField字段将这些对象插入MySQL后,它没有给出正确的结果。
这是我的MySQL查询,
SELECT id, `date`, UNIX_TIMESTAMP(`date`) FROM table
我通过将此查询结果中的unix timestamp列与MongoDB JSON转储进行比较来测试,以查看epoch是否匹配。
这到底发生了什么?为什么时区对纪元时代有影响?
仅供参考,我使用的是Django 1.5.1和MySQL-python 1.2.4。我还将Django USE_TZ标志设置为true
。
答案 0 :(得分:1)
我不是python或Django大师,所以也许有人可以比我更好地回答。但无论如何我会猜测它。
你说你把它存放在Django DateTimeField
中,根据the documents you referenced,它将它存储为Python datetime
。
关注the docs for datetime
,我认为关键是要理解“天真”和“意识”价值之间的区别。
然后进一步研究,我遇到了this excellent reference。请务必阅读第二部分“天真并了解日期时间对象”。这给出了Django控制其中多少内容的一些背景。基本上,通过设置USE_TZ = true
,您要求Django使用识别日期时间而不是天真。
然后我回头看了你的问题。你说你正在做以下事情:
dt = datetime.fromtimestamp(secs)
dt = dt.replace(tzinfo=utc)
查看fromtimestamp函数文档,我发现了这段文字:
如果可选参数
tz
为None
或未指定,则timestamp
将转换为平台的本地日期和时间,并且返回的datetime
对象是天真的。< / p>
所以我认为你可以这样做:
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
函数?或者那就是你在测试中所做的那样?