在Django中将Unix时间戳转换为带时区的日期时间

24
我有一个JavaScript日历,它向我发送一个Unix时间戳。我在新加坡。我希望将此时间戳解释为新加坡标准时间,然后转换为UTC以便与数据库进行比较。
但是我无论如何都不知道如何告诉Django这个时间戳来自当前时区,即新加坡。当我打印时间戳的输出时,它会将时间加8小时(这意味着Django认为我输入的时间是UTC,并将其本地化到新加坡上下文中)。
除了其他事情外,我尝试了以下代码: start=datetime.datetime.fromtimestamp(int(start_date)).replace(tzinfo=get_current_timezone()) 其中,start_date为1325376000(即2012年1月1日00:00:00)。
然而,当我打印此输出时,我得到的结果是2012年1月1日08:00:00+06:55。我甚至不知道+06:55是从哪里来的,因为新加坡是+08:00。我很迷茫。
感谢您的帮助。
settings.py: TIME_ZONE = 'Asia/Singapore' USE_TZ = True

POSIX时间戳1325376000'2012-01-01 00:00:00 UTC+0000''2012-01-01 08:00:00 SGT+0800'。你认为为什么在新加坡应该是00:00:00 - jfs
3个回答

38

上述所有方法都是有效的,但不符合“Django风格”。下面是一个简单的示例,展示了Django程序员将如何做到这一点:

from datetime import datetime

from django.utils.timezone import make_aware


# valid timestamp
value = 1531489250 
# you can pass the following obj to a DateTimeField, when your settings.USE_TZ == True
datetime_obj_with_tz = make_aware(datetime.fromtimestamp(value))

请查看Django github时区模块,以获取更多实用工具的完整概述...


3
注意:该解决方案引入了一个不必要的转换,通过使用naive本地时间(如果未传递tz参数,则由fromtimestamp()返回),这可能会以各种方式失败,例如在fromtimestamp()无法访问tz数据库或存在模糊的本地时间的系统上。 按照我的答案显式传递tz参数,在这种情况下,您不需要调用make_aware()(将tz传递给fromtimestamp()会导致调用tz.fromutc(),这比make_aware()调用的tz.localize()更健壮)。 - jfs

27

假设您已安装pytz

from datetime import datetime
import pytz
local_tz = pytz.timezone("Asia/Singapore") 
utc_dt = datetime.utcfromtimestamp(timestamp).replace(tzinfo=pytz.utc)
local_dt = local_tz.normalize(utc_dt.astimezone(local_tz))
例如:
>>> from datetime import datetime
>>> import pytz
>>> local_tz = pytz.timezone("Asia/Singapore")
>>> utc_dt = datetime.utcfromtimestamp(1325376000).replace(tzinfo=pytz.utc)
>>> utc_dt
datetime.datetime(2012, 1, 1, 0, 0, tzinfo=<UTC>)
>>> local_dt = local_tz.normalize(utc_dt.astimezone(local_tz))
>>> local_dt
datetime.datetime(2012, 1, 1, 8, 0, tzinfo=<DstTzInfo 'Asia/Singapore' SGT+8:00:00 STD>)
>>> local_dt.replace(tzinfo=None)
datetime.datetime(2012, 1, 1, 8, 0)

жҲ‘зҡ„еӣһзӯ”зҡ„е“ӘдёҖйғЁеҲҶеҸ–еҶідәҺжңҚеҠЎеҷЁзҡ„ж—¶еҢәпјҹжӯӨеӨ–пјҢpytzзҡ„tz.localize(dt)еңЁиҝҷз§Қжғ…еҶөдёӢдёҺtz.normalize(dt.replace(tzinfo=pytz.utc).astimezone(tz))жҳҜзӣёеҗҢзҡ„пјҡhttp://pytz.sourceforge.net/#localized-times-and-date-arithmetic - David Wolever
3
呃!啊,谢谢!你说得对。我是个白痴。我的答案已经更新,给出了实际的、正确的答案。 - David Wolever
假设您已经安装了 pytz,这太多了... :( - Jorge Leitao
为什么呢?pytz很容易安装(或者复制+粘贴,如果你喜欢的话)。如果你只涉及一个时区,并且该时区没有夏令时,并且UTC偏移量在应用程序处理的时间范围内没有改变(也不会改变),那么编写自己的时区处理可能不是太难...但否则几乎肯定会出现错误。 - David Wolever
它运行得很好,唯一的问题是我必须手动插入时区,我如何使其动态化,以便对来自任何地方的用户都有效?我从哪里获取时区信息? - Jesus Almaral - Hackaprende
@JesusAlmaral-Hackaprende:问题提到了Django-specific:timezone.get_current_timezone() - jfs

7
将pytz时区对象传递给fromtimestamp()方法:
#!/usr/bin/env python
from datetime import datetime
import pytz # $ pip install pytz

tz = pytz.timezone("Asia/Singapore")
print(datetime.fromtimestamp(1325376000, tz))
# -> 2012-01-01 08:00:00+08:00

注意:结果对象是有时区意识的:您可以将其与其他有意识的日期时间对象进行比较,即您无需将其转换为UTC进行比较,而是可以直接使用它。
您看到+06:55是由于无效的.replace()调用。函数get_current_timezone()返回pytz.timezone("Asia/Singapore"),该时区具有可变的UTC偏移量(在不同的日期可能有不同的UTC偏移量)。当您调用.replace()方法时会选取一些随机的(取决于实现)时区信息对象。问题在于.replace()方法不允许pytz.timezone("Asia/Singapore")为输入日期选择正确的时区信息。
>>> list(tz._tzinfos.values())
[<DstTzInfo 'Asia/Singapore' MALT+7:00:00 STD>,
 <DstTzInfo 'Asia/Singapore' MALT+7:20:00 STD>,
 <DstTzInfo 'Asia/Singapore' JST+9:00:00 STD>,
 <DstTzInfo 'Asia/Singapore' SMT+6:55:00 STD>,
 <DstTzInfo 'Asia/Singapore' SGT+7:30:00 STD>,
 <DstTzInfo 'Asia/Singapore' MALT+7:30:00 STD>,
 <DstTzInfo 'Asia/Singapore' MALST+7:20:00 DST>,
 <DstTzInfo 'Asia/Singapore' LMT+6:55:00 STD>,
 <DstTzInfo 'Asia/Singapore' SGT+8:00:00 STD>]

例如,对于新加坡来说,+06:55+0800都是有效的(在不同的日期)。这就是为什么你应该只将.replace()用于具有恒定utc偏移量的时区,例如utc时区本身(对于任何日期,偏移量始终为零)。 fromtimestamp(, tz)方法内部调用tz.fromutc(),允许tz选择给定utc时间的正确偏移量。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接