【问题标题】:Convert a UTC time to epoch将 UTC 时间转换为纪元
【发布时间】:2014-06-04 02:04:33
【问题描述】:

我希望分析与天气数据相关的交通流量。交通数据有一个 UNIX 时间戳(又名纪元),但我在将时间戳(在天气数据中) 转换为纪元时遇到了麻烦。问题是我在挪威,天气数据中的 UTC 时间戳与我不在同一个时区 (GMT+1)。

我的初步做法

我首先尝试将其转换为纪元并将数据视为 GMT+1 时区。然后我通过减去 UTC 和 GMT+1 之间的秒数差来补偿。

方法的问题

我首先意识到这种方法非常原始而且不是很优雅(事实上,它可能充其量只是一个丑陋的hack)。然而,这里最大的问题是 UTC 和 GMT+1 之间的差异不是恒定的(由于夏令时)。

问题

是否有任何可靠方法可以将 UTC 时间转换为 python 中的 UNIX 时间戳(考虑到我的机器处于 GMT+1 状态)?时间戳格式如下:

Y-m-d HH:MM:SS

编辑: 试过 rmunns 的解决方案:

def convert_UTC_to_epoch(timestamp):
  tz_UTC = pytz.timezone('UTC')
  time_format = "%Y-%m-%d %H:%M:%S"
  naive_timestamp = datetime.datetime.strptime(timestamp, time_format)
  aware_timestamp = tz_UTC.localize(naive_timestamp)
  epoch = aware_timestamp.strftime("%s")
  return (int) (epoch)

不能正常工作,如下所示:

#Current time at time of the edit is 15:55:00 UTC on June 9th 2014.
>>> diff = time.time() - convert_UTC_to_epoch("2014-06-09 15:55:00")
>>> diff
3663.25887799263
>>> #This is about an hour off.

【问题讨论】:

  • 我不明白你的第一段。您说流量数据采用“UNIX 时间戳(又名纪元)”格式,但您正试图将其转换为纪元,它已经采用的格式?请澄清。我将使用我对您的意思的最佳猜测来回答您的问题,但对输入格式和输出格式进行更多解释会有所帮助。
  • @rmunn 这是一个小错字。流量数据采用纪元格式。天气数据采用字符串格式 Y-m-d HH:MM:SS (UTC)。
  • 我已经更新了我的答案,以说明您遇到问题的原因以及正确的解决方案。 (有两种正确的解决方案,取决于你是否需要关心毫秒。)
  • 更新:对不起,我以前的 cmets 错了。问题不是你使用time.time(),问题是你使用strftime("%s")NEVER use strftime("%s"):“它不受支持,它不可移植,它可能会默默地为感知的日期时间对象产生错误的结果,如果输入是 UTC(如问题所示)但本地时区不是 UTC,它会失败”。

标签: python date datetime


【解决方案1】:

解决方案是使用日历模块(灵感来自here

>>>#Quick and dirty demo
>>>print calendar.timegm(datetime.datetime.utcnow().utctimetuple()) - time.time()
>>>-0.6182510852813721

这里是转换函数:

import calendar, datetime, time

#Timestamp is a datetime object in UTC time
def UTC_time_to_epoch(timestamp):
  epoch = calendar.timegm(timestamp.utctimetuple())
  return epoch

【讨论】:

  • 如果您的时间戳是 UTC,这将工作得很好,但这让我感到困惑。为什么您在关于您的时区和 UTC 之间的一小时差异的问题中提出了这么大的问题?如果您的时间戳采用 UTC,则首先不需要进行任何时区转换。
  • 我想我要问的是:您传入数据的哪一部分是当地时间?如果都不是当地时间,那么您可以忽略我关于pytz 的全部答案,因为您不需要进行任何时区转换。
  • 正如我之前所说,一部分(天气数据)是 UTC 时间。另一部分有 UNIX 时间戳。
  • 我的问题没什么大不了的。如果你注意到了,我明确地问了这个问题:Is there any reliable way of turning UTC time to a UNIX time stamp in python (taking into account that my machine is in GMT+1)? 当我尝试你的方法时,我无法解释这里有 1 小时的时差。它显示差异为 3600~ 秒,而应该是 7200。
  • "另一部分有 UNIX 时间戳。"由于 UNIX 时间戳被定义为“自纪元以来的秒数,以 UTC 表示”,这让我想知道为什么您甚至在问题正文中提到时区。您有 UTC 中的 UNIX 时间戳,以及 UTC 中的天气数据——所以您的本地时间是什么时区根本不重要。将所有内容保持在 UTC 中,永远不要转换它,你就完成了。我花了很多时间来回答我认为您所问的问题,而实际上您所需要的只是calendar.timegm() 函数(谢天谢地,您找到了)。
【解决方案2】:

另一种选择,datetime 有它自己的 .strptime() 方法。

http://en.wikipedia.org/wiki/Unix_time

Unix 纪元是 1970 年 1 月 1 日 00:00:00 UTC 时间(或 1970-01-01T00:00:00Z ISO 8601)。

import datetime
unix_epoch = datetime.datetime(1970, 1, 1)
log_dt = datetime.datetime.strptime("14-05-07 12:14:16", "%y-%m-%d %H:%M:%S")
seconds_from_epoch = (log_dt - unix_epoch).total_seconds()
>>> 1399490056.0

【讨论】:

  • 这种方法的问题是它仍然不会考虑我的时区 (GMT+1) 和 UTC 之间的时差。这是我问题的症结所在。是的,如果我错了,请纠正我(即这种方法确实考虑了时区差异)。
  • 如果你必须处理夏令时,你应该使用pytz,正如 rmunn 提到的那样。
  • @ArnabDatta: 如果log_dt 是UTC(正如您使用calendar.timegm() 所暗示的那样),那么您的本地时区是什么并不重要:posix_timestamp_seconds_since_epoch = (log_dt - unix_epoch).total_seconds() 给出正确的结果(在浮点精度内)
【解决方案3】:

pytz module 可能会对您有所帮助。它允许您编写如下代码:

import pytz
import datetime
tz_oslo = pytz.timezone('Europe/Oslo')
time_format = "%Y-%m-%d %H:%M:%S"
naive_timestamp = datetime.datetime(2014, 6, 4, 12, 34, 56)
# Or:
naive_timestamp = datetime.datetime.strptime("2014-06-04 12:34:56", time_format)
aware_timestamp = tz_oslo.localize(naive_timestamp)
print(aware_timestamp.strftime(time_format + " %Z%z"))

这应该打印“2014-06-04 14:34:56 CEST+0200”。

请注意 pytz 手册中的以下内容:

处理时间的首选方式是始终以 UTC 工作,仅在生成供人类阅读的输出时转换为本地时间。

因此,在编写代码时请牢记这一点:仅将时间转换为本地时间一次,这样您就可以更轻松地正确比较两个时间戳。

更新:以下是一些您可能会觉得有用的视频:

  • What you need to know about datetimes,Taavi Burns 的 PyCon 2012 演讲(30 分钟)
  • Drive-in Double Header: Datetimes and Log Analysis,由两部分组成的演示文稿。 (注意:视频中有烦人的嗡嗡声,但我找不到声音更好的副本)。第一部分是我在上面链接的“您需要了解的有关日期时间的知识”演示文稿,第二部分有一些实用技巧,用于解析日志文件并使用它们做有用的事情。 (50 分钟)

更新 2:您在更新后的问题中提到的 convert_UTC_to_epoch() 函数(我已在下面复制)正在返回当地时间,而不是 UTC:

def convert_UTC_to_epoch(timestamp):
  tz_UTC = pytz.timezone('UTC')
  time_format = "%Y-%m-%d %H:%M:%S"
  naive_timestamp = datetime.datetime.strptime(timestamp, time_format)
  aware_timestamp = tz_UTC.localize(naive_timestamp)
  epoch = aware_timestamp.strftime("%s")
  return (int) (epoch)

问题是您使用的是strftime("%s"),它没有记录并且返回错误的结果。 Python 不支持%s 参数,但它似乎可以工作,因为它被传递给系统的strftime() 函数,该函数支持%s 参数——但它返回local强>时间!您正在获取 UTC 时间戳并将其解析为当地时间,这就是为什么要休息一个小时。 (谜团在于为什么不休息两个小时——挪威现在不是夏令时吗?你不应该在 UTC+2 吗?)

正如您从下面的交互式 Python 会话中看到的那样,我在 UTC+7 时区,您的 convert_UTC_to_epoch() 函数对我来说是七个小时。

# Current time is 02:42 UTC on June 10th 2014, 09:42 local time
>>> time.timezone
-25200
>>> time.time() - convert_UTC_to_epoch("2014-06-10 02:42:00")
25204.16531395912
>>> time.time() + time.timezone - convert_UTC_to_epoch("2014-06-10 02:42:00")
6.813306093215942

strftime("%s") 调用将 6 月 10 日的 02:42 解释为当地时间,即 6 月 9 日的 19:42 UTC。从 6 月 10 日 02:42 UTC 减去 6 月 9 日 19:42 UTC(这是 time.time() 返回的值)会产生 7 小时的差异。请参阅Convert python datetime to epoch with strftime,了解更多关于为什么不应该使用strftime("%s")的详细信息。

(顺便说一句,如果你看到我之前在标题“更新 2”下写的内容,我声称 time.time() 正在返回当地时间,请忽略它——我错了。一开始我被愚弄了strftime("%s") bug 就像你一样。)

【讨论】:

  • 请注意,我的代码假设您的输入时间戳是字符串。如果您输入的是纪元数字(UTC 时间自 1970 年 1 月 1 日午夜以来的秒数),那么您将希望使用 datetime.datetime.fromtimestamp(some_number) 而不是 datetime.datetime.strptime(some_str, parse_fmt)
  • 如果不清楚:pytz 模块提供的时区定义了解每个国家/地区的夏令时规则。因此,您不必自己重新实现 DST 转换规则(正如您所说,这将是一个丑陋的 hack);您可以使用 pytz 提供的转换。
  • 我尝试了你的方法,不幸的是,正如你在上面看到的,它是一个小时的休息时间。
  • 这就是为什么始终以 UTC 进行所有计算并始终了解传入数据是 UTC 还是本地时间很重要的原因。在这种情况下,您从本地时间中减去了 UTC 时间,当然这是一个小时的时间。 (实际上,令我感到困惑的是,为什么它没有休息 两个 小时。挪威现在不是夏令时吗,这会使您当前的时区 UTC+2?)
  • 哎呀——我以前的 cmets(我刚刚删除)是错误的。我错误地声称time.time() 正在返回当地时间,但事实并非如此。问题是您使用了未记录的、做错事的strftime("%s")。我已经更新了答案以反映这一点。
【解决方案4】:

您可以使用timedatetime 模块:

import time, datetime
date = "14-05-07 12:14:16" #Change to whatever date you want
date = time.strptime(date, "%y-%m-%d %H:%M:%S")
epoch = datetime.datetime.fromtimestamp(time.mktime(date)).strftime('%s')

运行如下:

>>> import time, datetime
>>> date = "14-05-07 12:14:16"
>>> date = time.strptime(date, "%y-%m-%d %H:%M:%S")
>>> epoch = datetime.datetime.fromtimestamp(time.mktime(date)).strftime('%s')
>>> epoch
'1399490056'
>>> 

【讨论】:

  • time.mktime(date) 就够了。
  • mktime() 需要本地时间(作为时间元组),而不是问题中的 UTC 时间。 mktime() 已经返回时间戳(自 Unix 上的 Epoch 以来的秒数);你不应该打电话给fromtimestamp().strftime()strftime('%s') 不可移植(它适用于 Unix 上的本地时间)。
猜你喜欢
  • 2021-12-31
  • 2019-09-26
  • 2016-02-06
  • 2020-09-14
  • 2019-04-19
  • 2011-08-06
  • 2020-05-19
  • 2023-03-27
  • 2015-12-19
相关资源
最近更新 更多