假UTC时间戳在某些给定语言环境中的任何客户端上显示时间

时间:2013-08-21 13:22:28

标签: python datetime timezone utc pytz

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

我有一个需要时间戳事件的应用程序。我将所有内容存储在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?

3 个答案:

答案 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