【问题标题】:Python: Reading binary file with date/timePython:读取带有日期/时间的二进制文件
【发布时间】:2020-10-13 21:48:36
【问题描述】:

我有一个表示开始时间的二进制文件的一部分。

我正在尝试用 Python 读取文件中的时间/日期值,但遇到了问题。

>>>file = open('file1',"rb")
>>>data = file.read()
>>>data[16:24]
b'Q\xca\rk\x9c\xc6\xd7\x88'
>>>unpacked, = unpack('<Q', data[16:24])
9860568284264254033

我已经知道 16-24 位置包含 8 字节大小的 Int64。

在 C# 中,我可以通过这样做成功获得值 {3/12/2020 11:45:40 AM}

var value = _reader.ReadInt64();
sdatetime = DateTime.FromBinary(value);

有人知道如何在 Python 中正确阅读吗?

如果有帮助的话,

>>> unpack('q',data[16:24])
(-8586175789445297583,)
>>> unpack('Q',data[16:24])
(9860568284264254033,)

我试过了:

>>> unpacked
9860568284264254033
>>> secs = unpacked / 10.0 **7
>>> secs
986056828426.4253
>>> delta = datetime.timedelta(seconds = secs)
>>> delta
datetime.timedelta(days=11412694, seconds=66826, microseconds=425293)
>>> ts = datetime.datetime(1,1,1) + delta

这只是导致溢出错误:日期值超出范围

【问题讨论】:

  • 能否请您编辑问题并发布完整代码并展示您如何阅读文件?
  • 好吧,从DateTime.FromBinary() method 上的 C# 文档来看,看起来两位用于存储位置(Kind 属性),其余 62 位是从开头开始的滴答数21世纪。提取这些刻度数并执行一些奇怪的 python datetime.timedelta 数学运算,我相信你可以找出答案。
  • 我将发布我尝试过的数学运算,但它弹出错误消息说溢出错误:日期值超出范围
  • @akb515 timedelta 不正确,快速健全性检查表明您正在查看大约 31,267 年的时间增量。找出哪些 62 位用于存储刻度数,确定刻度和秒之间的时间转换,然后对表示 2001 年 1 月 1 日的日期时间对象执行 timedelta。
  • @Zircoz 感谢您的澄清!

标签: python binaryfiles


【解决方案1】:

我想我已经明白了

from datetime import datetime, timedelta

time_bytes = b'Q\xca\rk\x9c\xc6\xd7\x88'
time_int = int.from_bytes(time_bytes, 'little')
time_bin = bin(time_int)

# remove two unrelated bits and convert to integer number of ticks
n_ticks = int(time_bin[:2] + time_bin[4:], 2)
secs = n_ticks / 1e7

d1 = datetime(1, 1, 1)
t1 = timedelta(seconds=secs)
print(d1 + t1)

输出:

2020-03-12 15:45:40.947823

所以到时区为止都是正确的

编辑:

要获得实际时区,请再次使用timedelta

zone_delta = timedelta(hours=-4)
print(d1 + t1 + zone_delta)  

给予

2020-03-12 11:45:40.947830

【讨论】:

  • 我刚刚看到@akb515 提出了相同解决方案的评论。无论如何,也许以后有人会使用它。
  • 对于时区,既然我知道应该是上午 11:45,我将如何更改最后三行。它有 4 小时的差异,那么这个变化包括在哪里?
  • 刚刚添加到解决方案中
  • 简单一点:n_ticks = time_int &amp; 0x3fffffffffffffff,使用按位&amp; 清除前两位。十六进制值为(1&lt;&lt;63) - 1。 (可惜没有更好的方法来指定 64 位掩码。)
  • 我同意这有点干净。
【解决方案2】:

我们在 cmets 中查看了很多此类信息,但如果 cmets 被删除/删除,这里有一个答案。

DateTime.FromBinary() 通过将低 62 位编码为自 2001 年 1 月 1 日(21 世纪初)以来的刻度和高 2 位作为位置/种类信息,将 C# 日期时间对象转换为二进制。这意味着我们需要先提取低 62 位才能使用它们。然后我们只需将这些秒数转换为 timedelta 并执行算术以检索我们的 python 日期时间对象。

现在这里会有一个误差范围,因为我们忽略的 Kind 信息用于确定时区。您可以更深入地研究 Kind 位是什么,看看您是否可以检索时区以使您的结果更加一致,但对于这个答案,我没有。

unpacked = unpack('<Q', data[16:24])
# 64 bits with two most significant bits as 0 is decimal: 4611686018427387903
# Here I am performing a bitwise mask to keep only the 62 least significant bits.
# I prefer this method in stead of string manipulation from bin() for potential memory efficiency reasons
ticks = unpacked & 4611686018427387903
# There are 10,000,000 ticks in a second
seconds = ticks / 10000000
td = datetime.timedelta(seconds=seconds)
epoch = datetime.datetime(year=1, month=1, day=1)

resultDate = td + epoch

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-03-11
    • 2012-02-01
    • 2011-02-20
    • 1970-01-01
    • 2012-03-11
    • 1970-01-01
    相关资源
    最近更新 更多