我有一个我从JSON读过的pandas数据帧,一个日期列是一个奇怪的时间戳格式,如下所示
" /日期(1405961743000 + 0100)/"
。如何将整个列转换为python日期?
我已经能够通过在前10个数字fromtimestamp
上使用日期时间datetime.datetime.fromtimestamp(1405961743)
函数手动将该日期转换为python日期,但我很难转换整个列。
我猜我需要从每个条目中选择适当的数字,转换为整数,然后使用fromtimestamp函数,但我是python(和pandas)的新手,所以我很难做到这一点。
任何帮助都将不胜感激。
由于
答案 0 :(得分:1)
显然,如果你知道JSON的来源会更好,并且可以查看文档/询问作者/等。知道该日期格式背后的实际意图。 (它甚至可能由Python代码生成,使用您可以自己使用的库...)
但是看看这些数字,我可以很好地猜测这意味着什么:1405961743000
是自Unix时代以来的毫秒(这解释了为什么你可以使用它的前10位数字作为自Unix以来的秒数时代,至少在2014年左右的相当宽的范围内),+0100
是GMT的时区偏移,格式为+HHMM
。
因此,您不想提取前10个数字,转换为int,并调用fromtimestamp
,而是要将所有内容提取到+
或-
,转换为int除以1000,然后调用fromtimestamp
。虽然你给我们的唯一例子碰巧有0毫秒的事实意味着他们都很有可能,在这种情况下,这种差异无关紧要......
无论如何,由您决定如何处理时区偏移。你想知道当地的日期时间吗? GMT日期时间?天真的当地日期?它们很容易从时间戳和偏移量中获得(虽然“意识到”意味着使用像GMT-05:00这样的假时区,当然没有任何历史或DST信息),但你必须决定你想要哪一个。
无论您最终做什么,您可能需要考虑扩展JSON解码器以使其自动化,如the docs中的示例所示。 (与正则表达式r'/Date\((\d+)([+-]\d{4})\)/'
匹配的任何字符串,第一组是时间戳,第二组是偏移量。)
但也许不是。特别是因为parse_string
似乎没有被覆盖,至少从3.4开始,所以看起来你必须对其进行单一操作。见this code我拍了一下作为概念证明;你可能能够做得更好一点,但是如果他们没有为它提供一个钩子,那么你可以做得多清洁是有限制的。
json
模块文档中显示的dict格式,您可以有效地指定要调用的构造函数和传递它的参数,这对于人们弄清楚(以及添加钩子)来说要容易得多。或者,或者,有一种准标准的方式将YAML格式编码为JSON格式,YAML是可扩展的(并且已经具有标准时间戳扩展名)。
答案 1 :(得分:1)
我最近不得不解决同样的问题。幸运的是,我找到了这篇文章,并设法调整了该解决方案以格式化来自 MS Project Online 的基于 OData 的 API 的 JSON 文件中的所有日期。非常感谢@abarnert和@jfs!这是我使用的代码:
from datetime import datetime, timedelta, timezone
import re
def ms_to_timestamp(ticks: str, offset: str, date_format: str = "%Y-%m-%d %H:%M:%S") -> str:
epoch = datetime(1970, 1, 1, tzinfo=timezone.utc)
utc_dt = epoch + timedelta(milliseconds=int(ticks))
timestamp = utc_dt.strftime(date_format)
if offset:
offset = int(offset)
hours, minutes = divmod(abs(offset), 100)
if offset < 0:
hours, minutes = -hours, -minutes
dt = utc_dt.astimezone(timezone(timedelta(hours=hours, minutes=minutes)))
timestamp = dt.strftime(date_format)
return timestamp
def format_datetime(string: str) -> str:
dates = re.findall(r'Date\((\d+)([+-]\d{4})?\)', string)
for d in dates:
timestamp = ms_to_timestamp(ticks=d[0], offset=d[1])
string = string.replace(f"/Date({d[0]}{d[1]})/", timestamp)
return string
if __name__ == '__main__':
text = "example 1: /Date(1615885200000)/ ; example 2: /Date(1405961743000+0100)/"
new_text = format_datetime(text)
print(f"Before: {text}\nAfter: {new_text}")
答案 2 :(得分:0)
时间字符串为OData version 2 JSON verbose format for Datetime:
“/Date(<ticks>[“+” | “-” <offset>])/”
<ticks>
=毫秒数 自1970年1月1日午夜起<offset>
=要添加的分钟数或 减去
作为@Matt Johnson mentions格式可以在ASP.NET或WCF应用程序中看到。
#!/usr/bin/env python3
import re
from datetime import datetime, timedelta, timezone
time_string = "/Date(1405961743000+0100)/"
epoch = datetime(1970, 1, 1, tzinfo=timezone.utc)
ticks, offset = re.match(r'/Date\((\d+)([+-]\d{4})?\)/$', time_string).groups()
utc_dt = epoch + timedelta(milliseconds=int(ticks))
print(utc_dt, utc_dt.strftime('%Z'))
if offset:
offset = int(offset)
# http://www.odata.org/documentation/odata-version-2-0/json-format
# says offset is minutes (an error?)
dt = utc_dt.astimezone(timezone(timedelta(minutes=offset)))
print(dt, dt.strftime('%Z'))
# but it looks like it could be HHMM
hours, minutes = divmod(abs(offset), 100)
if offset < 0:
hours, minutes = -hours, -minutes
dt = utc_dt.astimezone(timezone(timedelta(hours=hours, minutes=minutes)))
print(dt, dt.strftime('%Z'))
2014-07-21 16:55:43+00:00 UTC+00:00
2014-07-21 18:35:43+01:40 UTC+01:40
2014-07-21 17:55:43+01:00 UTC+01:00
看起来应该忽略odata.org文档,并且应将偏移量视为HHMM格式。