【问题标题】:Convert JSON timestamp string into python date in pandas dataframe将 JSON 时间戳字符串转换为 pandas 数据框中的 python 日期
【发布时间】:2014-12-18 06:21:36
【问题描述】:

我有一个从 JSON 读取的 pandas 数据框,一个日期列是一种奇怪的时间戳格式,如下所示

"/日期(1405961743000+0100)/"

。如何将整列转换为 python 日期?

我已经能够通过在前 10 位数字(即datetime.datetime.fromtimestamp(1405961743))上使用 datetime fromtimestamp 函数将该日期手动转换为 python 日期,但我正在努力转换整个列。

我猜我需要从每个条目中选择适当的数字,转换为整数,然后使用 fromtimestamp 函数,但我是 python(和 pandas)的新手,所以很难做到这一点。

任何帮助将不胜感激。

谢谢

【问题讨论】:

    标签: python json datetime pandas


    【解决方案1】:

    我最近不得不解决同样的问题。幸运的是,我找到了这篇文章并设法调整解决方案以格式化 JSON 文件中的所有日期,该文件来自 MS Project Online 的基于 OData 的 API。非常感谢@abarnert 和@jfs!这是我使用的代码:

        from datetime import datetime, timedelta, timezone
        import re
    
    
        def ms_to_timestamp(
            milliseconds: 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(milliseconds))
            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(milliseconds=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}")
    
        # Output
        # ------
        # Before: example 1: /Date(1615885200000)/ ; example 2: /Date(1405961743000+0100)/
        # After: example 1: 2021-03-16 09:00:00 ; example 2: 2014-07-21 17:55:43
    

    【讨论】:

      【解决方案2】:

      时间字符串为OData version 2 JSON verbose format for Datetime:

      “/Date(&lt;ticks&gt;[“+” | “-” &lt;offset&gt;])/”
      &lt;ticks&gt; = 毫秒数 自 1970 年 1 月 1 日午夜起
      &lt;offset&gt; = 添加的分钟数或 减法

      @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'))
      

      Output

      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 格式。

      【讨论】:

        【解决方案3】:

        显然,如果您知道 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我拍打在一起作为概念证明;你也许可以让它变得更好一点,但是如果他们不提供钩子,你能做到的干净程度是有限的……


        PS,如果您曾经自己扩展 JSON,您可能需要考虑一种更标准化和自我记录的方式来执行此操作。 json 模块文档中显示的 dict 格式,您可以在其中有效地指定要调用的构造函数和传递它的参数,人们更容易理解(并为其添加挂钩)。或者,有一种准标准的方式将 YAML 格式编码为 JSON 格式,并且 YAML 是可扩展的(并且已经具有标准的时间戳扩展)。

        【讨论】:

        • 格式来自旧版本的 ASP.NET 或 WCF 应用程序。这不是最好的格式,但它是众所周知的。在大多数情况下它已被 ISO8601 取代,但仍被大量代码使用。
        • @MattJohnson:谢谢!方便的信息。知道这应该可以更容易地搜索解析这些东西的现有代码,而不必猜测它并重新发明轮子......
        • fromtimestamp() 如果本地 UTC 偏移与时间字符串中的偏移不对应,则未指定时区可能是错误的。这是how to get an aware datetime object given this time format。看起来我的代码也错了,偏移量是分钟数(即,它是 not HHMM)。
        • 另一方面,the docs 可以撒谎,我看到的所有示例都使用 HHMM 格式而不是“分钟”。
        • @J.F.Sebastian:这些是这种格式的正确文档吗?如果不是,那么通过特殊字符串格式将 JSON 扩展为新类型的两种不同标准会提出非常相似但不完全相同的方法,这并不奇怪。例如,jsonpickle 和 YAML-in-JSON 使用的 dict 格式通常是相同的,但有时并不相同。 (这就是为什么与文档或人交谈或编写代码总是比试图从格式的小样本中猜测更好的原因……)
        猜你喜欢
        • 2016-09-15
        • 1970-01-01
        • 1970-01-01
        • 2012-03-27
        • 2012-06-04
        • 2019-03-14
        • 2011-05-05
        • 1970-01-01
        • 2014-10-07
        相关资源
        最近更新 更多