我有一个关于时区的黑魔法问题。
我有一个需要时间戳事件的应用程序。我将所有内容存储在UTC中。但是,我想显示(客户端)日期,既不在用户区域设置中也不在UTC中,而是在给定时区(即巴黎时区)中显示。
由于没有javascript方法可以轻松完成(我们必须知道使用像timezone.js这样看似矫枉过正的确切夏令时事件),我想做以下诀窍:
dutc=datetime(…,<UTC>)
dparis=datetime(…,<Paris>)
但是,我没有收回原来的时间戳
import datetime
import time
import pytz
utc = pytz.UTC
paris = pytz.timezone('Europe/Paris')
t = 1372982409 # timestamp in UTC
dutc = datetime.datetime.fromtimestamp(t, utc) # date in UTC
# datetime.datetime(2013, 7, 5, 0, 0, 9, tzinfo=<UTC>)
dparis = datetime.datetime.fromtimestamp(t, paris) # date in Paris time
# datetime.datetime(2013, 7, 5, 2, 0, 9, tzinfo=<DstTzInfo 'Europe/Paris' CEST+2:0
0:00 DST>)
dparisasutc = dparis.replace(tzinfo=utc) # force the date in UTC time
# datetime.datetime(2013, 7, 5, 2, 0, 9, tzinfo=<UTC>)
t0 = time.mktime(dparisasutc.timetuple())
# 1372986009.0
datetime.datetime.fromtimestamp(t0)
# datetime.datetime(2013, 7, 5, 3, 0, 9) # why not 02:00:09?
答案 0 :(得分:2)
你想:
- 获取UTC日期dutc = datetime(...,)
- 将其转换为巴黎日期dparis = datetime(...,)
好的,到目前为止我和你在一起,但接着......
- 强制tzinfo返回UTC以获取与日期和时间相对应的UTC时间戳。在巴黎的时间(例如相同的数字日期和时间,但使用UTC而不是巴黎,不添加或删除小时数)
- 将此UTC日期存储为时间戳,以便客户端可以将其解析为“假UTC”时间戳,并显示“假时间”
这可能是一个坏主意。一般惯例是,表示为整数的时间戳始终基于UTC。这实际上几乎无处不在(某些Microsoft .Net类型是例外)。
由于您的客户端是JavaScript,因此他们希望任何整数都是自1970年1月1日UTC以来的毫秒数。改变这将产生一些奇怪的后果。最有趣的一点是,您将获得用户自己的本地时区关于错误应用夏令时的规则。在测试中很难找到这些。
最简单的答案是不要在JavaScript中使用Date
类型。完全在服务器上转换到巴黎时间,并将格式良好的字符串传递回浏览器。
一个更有趣的解决方案是传递UTC时间,可以是整数,也可以是像2013-08-21T17:29:00Z
这样的ISO8601字符串。在客户端上,使用可识别UTC时间并将其转换为Europe/Paris
区域的库。您提到不想使用timezone.js执行此操作,但有several other libraries可供选择。有些非常小,让你只包括你关心的区域的日期。
例如,使用moment.js插件尝试moment-timezone。您可以专门构建一个仅包含Europe/Paris
区域数据的moment-timezone-data.js文件。 (他们在该网站上有一个很好的交互式数据构建器。)
答案 1 :(得分:1)
使用时区的.localize()
方法将UTC以外的时区应用于时间戳:
>>> import datetime
>>> import time
>>> import pytz
>>> utc = pytz.UTC
>>> paris = pytz.timezone('Europe/Paris')
>>> t = 1372982409 # timestamp in UTC
>>> dparis = paris.localize(datetime.datetime.fromtimestamp(t))
>>> dparis
datetime.datetime(2013, 7, 5, 1, 0, 9, tzinfo=<DstTzInfo 'Europe/Paris' CEST+2:00:00 DST>)
注意现在显示的一小时时差,因为正确的当前而非历史时区转换已应用。
接下来,您需要再次将生成的时间戳解释为UTC,以免Python再次将本地时区应用于时间戳:
>>> dparisasutc = dparis.replace(tzinfo=utc) # force the date in UTC time
>>> dparisasutc
datetime.datetime(2013, 7, 5, 1, 0, 9, tzinfo=<UTC>)
>>> t0 = time.mktime(dparisasutc.timetuple())
>>> t0
1372986009.0
>>> datetime.datetime.fromtimestamp(t0, utc)
datetime.datetime(2013, 7, 5, 1, 0, 9, tzinfo=<UTC>)
答案 2 :(得分:1)
因为time.mktime
在当地时间中返回struct_time
。您应该阅读docs并使用calendar.timegm()
而不是 UTC 返回struct_time
。
遗憾的是,这是古代遗留下来的一件旧事。并且永远不会改变,因为它可以制造人员代码:(
import datetime
import time
import pytz
import calendar
utc = pytz.UTC
paris = pytz.timezone('Europe/Paris')
t = 1372982409 # timestamp in UTC
dutc = datetime.datetime.fromtimestamp(t, utc) # date in UTC
# datetime.datetime(2013, 7, 5, 0, 0, 9, tzinfo=<UTC>)
dparis = datetime.datetime.fromtimestamp(t, paris) # date in Paris time
# datetime.datetime(2013, 7, 5, 2, 0, 9, tzinfo=<DstTzInfo 'Europe/Paris' CEST+2:00:00 DST>)
dparisasutc = dparis.replace(tzinfo=utc) # force the date in UTC time
# datetime.datetime(2013, 7, 5, 2, 0, 9, tzinfo=<UTC>)
t0 = calendar.timegm(dparis.timetuple())
print t0
# 1372989609.0
datetime.datetime.fromtimestamp(t0, utc)
# datetime.datetime(2013, 7, 5, 2, 0, 9, tzinfo=<UTC>) # now it is 02:00:09