使用datetime在python中获取UTC时间戳


82

有没有一种方法可以通过指定日期来获取UTC时间戳?我期望的是:

datetime(2008, 1, 1, 0, 0, 0, 0)

应该导致

 1199145600

创建朴素的datetime对象意味着没有时区信息。如果我查看datetime.utcfromtimestamp的文档,则创建UTC时间戳意味着遗漏时区信息。因此,我猜想,创建一个简单的datetime对象(如我所做的那样)会导致UTC时间戳。然而:

then = datetime(2008, 1, 1, 0, 0, 0, 0)
datetime.utcfromtimestamp(float(then.strftime('%s')))

结果是

2007-12-31 23:00:00

datetime对象中是否还存在任何隐藏的时区信息?我究竟做错了什么?


问题是then.strftime('%s')期望当地时间,但时间戳指示该时间datetime(2008, 1, 1)是UTC。
jfs 2014年

Answers:


90

天真datetime与意识datetime

默认datetime对象被称为“天真”的:它们保留时间信息而没有时区信息。可以将朴素的名字想datetime成一个+4没有明确来源的相对数字(即:)(实际上,您的来源在整个系统边界中都是常见的)。

相比之下,请考虑将意识datetime看作8是全世界共有的绝对数(即:)。

没有时区信息,您将无法将“原始”日期时间转换为任何非原始时间表示形式(+4如果我们不知道从哪里开始,目标会在哪里?)。这就是为什么您没有datetime.datetime.toutctimestamp()方法的原因。(参见:http : //bugs.python.org/issue1457227

要检查您datetime dt是否天真,请检查dt.tzinfo,如果是None,则表示天真:

datetime.now()        ## DANGER: returns naïve datetime pointing on local time
datetime(1970, 1, 1)  ## returns naïve datetime pointing on user given time

我的约会时间很晚,该怎么办?

您必须根据自己的具体情况做出假设:您必须问自己的问题是:您是否datetime在UTC上?还是当地时间?

  • 如果您使用的是UTC(没有麻烦):

    import calendar
    
    def dt2ts(dt):
        """Converts a datetime object to UTC timestamp
    
        naive datetime will be considered UTC.
    
        """
    
        return calendar.timegm(dt.utctimetuple())
    
  • 如果您不使用UTC,欢迎来到地狱。

    datetime在使用前一个功能之前,您必须先将其恢复为原先的时区,以使其天真。

    您将需要时区的名称,以及在生成目标原始日期时间时有关DST是否生效的信息(关于DST的最后一个信息对于角落情况是必需的):

    import pytz     ## pip install pytz
    
    mytz = pytz.timezone('Europe/Amsterdam')             ## Set your timezone
    
    dt = mytz.normalize(mytz.localize(dt, is_dst=True))  ## Set is_dst accordingly
    

    不提供的后果is_dst

    is_dst如果在设置了向后DST的同时生成了目标日期时间(例如,通过删除一小时来更改DST时间),则不使用将产生不正确的时间(以及UTC时间戳)。

    is_dst当然,仅提供错误的遗嘱只会在DST重叠或空洞时生成错误的时间(和UTC时间戳)。而且,如果还提供了不正确的时间,则出现在“空洞”中(由于DST向前移动而永远不存在的时间),is_dst将解释如何考虑该虚假时间,这是唯一在 .normalize(..)此处实际做些事情的情况,因为它将随后将其转换为实际有效时间(如果需要,请更改datetime和DST对象)。请注意,.normalize()不需要在末尾具有正确的UTC时间戳,但是如果您不喜欢在变量中使用虚假时间的想法,尤其是在其他地方重用此变量时,建议您这样做。

    避免使用以下内容:(参见:使用pytz进行Datetime时区转换

    dt = dt.replace(tzinfo=timezone('Europe/Amsterdam'))  ## BAD !!
    

    为什么?因为.replace()tzinfo不考虑目标时间的情况下盲目替换,将选择一个不良的DST对象。而.localize()使用目标时间和is_dst提示来选择正确的DST对象。

OLD错误答案(感谢@JFSebastien提出):

希望您可以很容易地在创建朴素datetime对象时猜测时区(您的本地来源),因为它与系统配置有关,希望您不会在朴素日期时间对象创建和获取日期之间进行更改。 UTC时间戳。这个技巧可以用来提出一个不完善的问题。

通过使用time.mktime我们可以创建一个utc_mktime

def utc_mktime(utc_tuple):
    """Returns number of seconds elapsed since epoch

    Note that no timezone are taken into consideration.

    utc tuple must be: (year, month, day, hour, minute, second)

    """

    if len(utc_tuple) == 6:
        utc_tuple += (0, 0, 0)
    return time.mktime(utc_tuple) - time.mktime((1970, 1, 1, 0, 0, 0, 0, 0, 0))

def datetime_to_timestamp(dt):
    """Converts a datetime object to UTC timestamp"""

    return int(utc_mktime(dt.timetuple()))

您必须确保datetime在与创建您的对象相同的时区创建对象datetime

最后一个解决方案是不正确的,因为它假设从现在开始的UTC偏移量与从EPOCH开始的UTC偏移量相同。对于许多时区(在夏时制(DST)偏移量的一年中的特定时间)而言,情况并非如此。


4
天真的日期时间对象应始终以UTC表示时间。其他时区应仅用于I / O(显示)。datetime.timestamp()Python 3.3中有方法。
jfs 2012年

2
time.mktime()应该仅用于当地时间。calendar.timegm()可用于将utc时间元组转换为posix时间戳。或者最好只使用datetime方法。看到我的答案
jfs 2012年

4
-1。您的代码假定utc_offset(now)和utc_offset(epoch)在本地时区相同。在116个时区(来自430个常见时区)中并非如此。
jfs 2014年

1
仍为-1:请勿.replace()与utc偏移量未固定的时区一起使用,例如'Europe/Amsterdam'。请参阅使用pytz进行Datetime时区转换
jfs

1
1-您知道为什么不应该使用.replace(tzinfo=get_localzone())吗?2-已经timegm()返回int。无需用来包装它int。同样,.timetuple()滴落几分之一秒。
jfs

29

另一种可能性是:

d = datetime.datetime.utcnow()
epoch = datetime.datetime(1970,1,1)
t = (d - epoch).total_seconds()

由于“ d”和“ epoch”都是幼稚的日期时间,因此使“-”运算符有效并返回间隔。total_seconds()将间隔变成秒。注意total_seconds()返回一个浮点数,即使d.microsecond == 0


12
好吧,不是真的,这个想法是一样的,但是这个想法更容易理解:)
Natim

3
您可能会认为时间库中将只有一个方法或类似的东西……sheesh
wordsforthewise,2017年


13

如果输入的datetime对象为UTC:

>>> dt = datetime(2008, 1, 1, 0, 0, 0, 0)
>>> timestamp = (dt - datetime(1970, 1, 1)).total_seconds()
1199145600.0

注意:它返回浮点数,即微秒表示为几分之一秒。

如果输入日期对象为UTC:

>>> from datetime import date
>>> utc_date = date(2008, 1, 1)
>>> timestamp = (utc_date.toordinal() - date(1970, 1, 1).toordinal()) * 24*60*60
1199145600

在Python中将datetime.date转换为UTC时间戳,请参见更多详细信息。


8

我觉得主要的答案还不是那么清楚,这是值得花时间了解时间时区

与时间打交道时要了解的最重要的事情是时间是相对的

  • 2017-08-30 13:23:00:(一个简单的日期时间)代表世界某个地方的当地时间,但请注意,2017-08-30 13:23:00伦敦的时间不像2017-08-30 13:23:00旧金山的时间

由于根据您所处的世界,同一时间字符串可以解释为不同的时间点,因此需要绝对的时间概念。

UTC时间戳是在秒(或毫秒)的数历元(定义为1 January 1970 00:00:00GMT时区00:00偏移)。

纪元固定在GMT时区,因此是绝对时间点。因此,UTC时间戳是相对于绝对时间的偏移量,它定义了绝对时间点

这使得可以及时订购事件。

如果没有时区信息,则时间是相对的,并且在没有提供天真日期时间应锚定到哪个时区的指示的情况下,不能转换为绝对时间概念。

计算机系统中使用的时间类型是什么?

  • 天真的日期时间:通常用于显示,以操作系统可以向程序提供时区信息的本地时间(即在浏览器中)显示。

  • UTC时间戳:如上所述,UTC时间戳是一个绝对时间点,但是它固定在给定的时区中,因此UTC时间戳可以转换为任何时区中的日期时间,但是其中不包含时区信息。那是什么意思?这意味着1504119325对应于2017-08-30T18:55:24Z2017-08-30T17:55:24-01002017-08-30T10:55:24-0800。它不会告诉您记录的日期时间是从哪里来的。通常在服务器端用于记录事件(日志等)或用于转换时区感知日期时间绝对的时间点和计算时间差

  • ISO-8601日期时间字符串:ISO-8601是一种标准格式,用于记录带时区的日期时间。(实际上是几种格式,请在此处阅读:https : //en.wikipedia.org/wiki/ISO_8601)它用于在系统之间以可序列化的方式传达时区感知日期时间信息。

什么时候使用?或者更确切地说,您何时需要关心时区?

  • 如果您需要以任何方式关心时间,则需要时区信息。日历或闹钟需要一天的时间才能为世界上的任何用户在一天的正确时间设置会议。如果此数据保存在服务器上,则服务器需要知道日期时间对应于哪个时区。

  • 要计算来自世界各地的事件之间的时间差,UTC时间戳已足够,但是您无法分析一天中的什么时间发生事件(例如,对于网络分析,您可能想知道用户何时来到您的网站上)。现场 以当地时间显示:您在早上或晚上看到更多用户吗?如果没有时间信息,您将无法解决这个问题。

日期字符串中的时区偏移量

另一点很重要,就是日期字符串中的时区偏移量不固定。就是说,因为2017-08-30T10:55:24-0800说补偿-0800或8个小时后退,并不意味着它将一直如此!

在夏季,很可能是夏令时, -0700

这意味着时区偏移量(+0100)时区名称(欧洲/法国)甚至是不同时区名称(CET)

America/Los_Angeles时区是世界上的一个地方,但PST在冬季变成(太平洋标准时间)时区偏移量表示法,并且PDT(太平洋夏令时间)。

因此,除了获取时区与日期字符串的偏移量之外,还应该获取准确的时区名称。

大多数程序包都可以自行将夏令时的数字偏移量转换为标准时间,但这不一定只是偏移量。例如WAT,西非的时区名称是UTC + 0100CET法国的时区,但是法国遵守夏令时,而西非则不这样做(因为它们靠近赤道)

简而言之,这很复杂。非常复杂,这就是为什么您不应该自己执行此操作,而是信任可以为您执行此操作的程序包,并保持最新状态!


请参阅我有关Python中日期和时间的博客文章,以了解不同软件包的陷阱media.com/@eleroy/…–
MrE

3

一个不使用外部模块的简单解决方案:

from datetime import datetime, timezone

dt = datetime(2008, 1, 1, 0, 0, 0, 0)
int(dt.replace(tzinfo=timezone.utc).timestamp())


1

我认为表达您的问题的正确方法是 Is there a way to get the timestamp by specifying the date in UTC?,因为时间戳只是一个绝对的数字,不是相对的。相对(或时区感知)片是日期。

我发现熊猫非常方便用于时间戳记,因此:

import pandas as pd
dt1 = datetime(2008, 1, 1, 0, 0, 0, 0)
ts1 = pd.Timestamp(dt1, tz='utc').timestamp()
# make sure you get back dt1
datetime.utcfromtimestamp(ts1)  

恕我直言,使用熊猫是正确的方法,对于当前时间,还有t = Timestamp.utcnow()可直接获得正确的时间:)
ntg

0

接受的答案似乎对我不起作用。我的解决方案:

import time
utc_0 = int(time.mktime(datetime(1970, 01, 01).timetuple()))
def datetime2ts(dt):
    """Converts a datetime object to UTC timestamp"""
    return int(time.mktime(dt.utctimetuple())) - utc_0

如果dt当地时区的当前()UTC偏移与1970年的UTC偏移不同,则失败。mktime()预计当地时间。
jfs 2014年

0

最简单的方法:

>>> from datetime import datetime
>>> dt = datetime(2008, 1, 1, 0, 0, 0, 0)
>>> dt.strftime("%s")
'1199163600'

编辑:@Daniel是正确的,这会将其转换为计算机的时区。这是一个修改后的答案:

>>> from datetime import datetime, timezone
>>> epoch = datetime(1970, 1, 1, 0, 0, 0, 0, timezone.utc)
>>> dt = datetime(2008, 1, 1, 0, 0, 0, 0, timezone.utc)
>>> int((dt-epoch).total_seconds())
'1199145600'

实际上,甚至不需要指定timezone.utc,因为时差相同,只要两者都datetime具有相同的时区(或没有时区)即可。

>>> from datetime import datetime
>>> epoch = datetime(1970, 1, 1, 0, 0, 0, 0)
>>> dt = datetime(2008, 1, 1, 0, 0, 0, 0)
>>> int((dt-epoch).total_seconds())
1199145600

此方法不返回utc,而是将datetime调整为当前时区。即。如果现在是12点(以tz +3表示),它将返回9点。
丹尼尔·杜波夫斯基

啊,你说得对。我的时区是UTC,这就是它起作用的原因。
Mike Furlender

如果要使用(Python 3.2及更高版本)timezone.utc对象,则只需将其与.timestamp():一起使用datetime(2008, 1, 1, tzinfo=timezone.utc).timestamp()。无需创建纪元对象并减去
。.–马丁·彼得斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.