0

我有一个关于时区的黑魔法问题。

我有一个需要时间戳事件的应用程序。我将所有内容都存储在 UTC 中。但是,我想显示(客户端)日期,既不是在用户语言环境中也不是在 UTC 中,而是在给定的时区(即巴黎时区)中。

由于没有 javascript 方法可以轻松地做到这一点(我们必须使用类似 timezone.js 的东西来知道确切的夏令时事件,这似乎有点过分了),我想做以下技巧:

  • 获取 UTC 日期dutc=datetime(…,<UTC>)
  • 将其转换为巴黎日期dparis=datetime(…,<Paris>)
  • 强制 tzinfo 回到 UTC 以获取与巴黎日期和时间相对应的 UTC 时间戳(例如,相同的数字日期和时间,但使用 UTC 而不是巴黎,不添加或删除小时)
  • 将此 UTC 日期存储为时间戳,以便客户端可以将其解析为“假 UTC”时间戳,并显示“假时间”

但是,我没有取回原来的时间戳

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?
4

3 回答 3

2

你想:

  • 获取 UTC 日期 dutc=datetime(...,)
  • 将其转换为巴黎日期 dparis=datetime(...,)

好的,到目前为止,我和你在一起,但是...

  • 强制 tzinfo 回到 UTC 以获取与巴黎日期和时间相对应的 UTC 时间戳(例如,相同的数字日期和时间,但使用 UTC 而不是巴黎,不添加或删除小时)
  • 将此 UTC 日期存储为时间戳,以便客户端可以将其解析为“假 UTC”时间戳,并显示“假时间”

这可能是个坏主意。一般约定是,表示为整数的时间戳始终基于 UTC。这几乎适用于任何地方(某些 Microsoft .Net 类型除外)。

由于您的客户端使用的是 JavaScript,因此他们希望任何整数都是自 1970 年 1 月 1 日 UTC 以来的毫秒数。改变它会产生一些奇怪的后果。最有趣的是,您将错误地应用用户自己的本地时区关于夏令时的规则。在测试中很难找到这些。

Date根本不使用JavaScript 中的类型的最简单答案。完全在服务器上转换为巴黎时间,并将格式正确的字符串传回浏览器。

一个更有趣的解决方案是传递 UTC 时间,可以是整数,也可以是 ISO8601 字符串,如2013-08-21T17:29:00Z. 在客户端上,使用可以识别 UTC 时间并将其转换为Europe/Paris区域的库。您提到不想使用 timezone.js 执行此操作,但还有其他几个库可供选择。有些非常小,让您只包括您关心的区域的日期。

例如,尝试使用moment-timezone插件的moment.js。您可以专门构建一个仅包含区域数据的 moment-timezone-data.js 文件。(他们在那个网站上有一个很好的交互式数据构建器。)Europe/Paris

于 2013-08-21T17:38:13.457 回答
1

使用 timezone 的.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>)
于 2013-08-21T13:29:44.850 回答
1

因为time.mktime返回当地时间struct_time_ 您应该阅读文档并改用以UTC形式返回的文档。calendar.timegm()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
于 2013-08-21T13:45:48.923 回答