在python中将48位(6个八位字节)从DNP3时间转换为时间戳

在python中将48位(6个八位字节)从DNP3时间转换为时间戳,python,datetime,timestamp,dnp3,Python,Datetime,Timestamp,Dnp3,为了一个小的安全项目,我正在尝试使用python将48位(8个八位字节)转换为时间戳。我正在处理来自DNP3协议的一些网络数据包,并尝试对每个DNP3类对象的时间戳值进行解码 根据DNP3标准,“DNP3时间(以UINT48的形式):绝对时间值,表示为自1970年1月1日开始以来的毫秒数” 我有以下八位字节需要转换为日期时间: # List of DNP3 timestamps DNP3_ts = [] # Feb 20, 2016 00:27:07.628000000 UTC DNP3_ts

为了一个小的安全项目,我正在尝试使用python将48位(8个八位字节)转换为时间戳。我正在处理来自DNP3协议的一些网络数据包,并尝试对每个DNP3类对象的时间戳值进行解码

根据DNP3标准,“DNP3时间(以UINT48的形式):绝对时间值,表示为自1970年1月1日开始以来的毫秒数”

我有以下八位字节需要转换为日期时间:

# List of DNP3 timestamps
DNP3_ts = []

# Feb 20, 2016 00:27:07.628000000 UTC
DNP3_ts.append('\xec\x58\xed\xf9\x52\x01')

# Feb 20, 2016 00:34:08.107000000 UTC
DNP3_ts.append('\x6b\xc3\xf3\xf9\x52\x01')

# Feb 20, 2016 00:42:40.460000000 UTC
DNP3_ts.append('\xcc\x94\xfb\xf9\x52\x01')

# Feb 20, 2016 00:56:47.642000000 UTC
DNP3_ts.append('\x1a\x82\x08\xfa\x52\x01')

# Feb 20, 2016 00:56:48.295000000 UTC
DNP3_ts.append('\xa7\x84\x08\xfa\x52\x01')

# Feb 20, 2016 00:58:21.036000000 UTC
DNP3_ts.append('\xec\xee\x09\xfa\x52\x01')

# Feb 20, 2016 01:17:09.147000000 UTC
DNP3_ts.append('\x9b\x25\x1b\xfa\x52\x01')

# Feb 20, 2016 01:49:05.895000000 UTC
DNP3_ts.append('\xe7\x64\x38\xfa\x52\x01')

# Feb 20, 2016 01:58:30.648000000 UTC
DNP3_ts.append('\xf8\x02\x41\xfa\x52\x01')

for ts in DNP3_ts:
    print [ts]
因此,我需要找出以下步骤:

# 1. Converting the octets into a 48bit Integer (which can't be done in python)

# 2. Using datetime to calculate time from 01/01/1970

# 3. Convert current time to 48bits (6 octets)

如果有人能帮我完成这些步骤,我将不胜感激

您可以简单地将字节组合起来,创建一个48位整数,其中包含一些字节。您可以使用
ord()
将每个八位字节转换为uint8,并将它们左移8的不同倍数,以便它们在48位数字中占据不同的位置

。为了形象化这一点,让你的八位字节从左到右称为A-F,A的位称为aaaaaa,等等。所以从你的八位字节到48位的数字,你要达到这个顺序

       A        B        C        D        E        F
ffffffff eeeeeeee dddddddd cccccccc bbbbbbbb aaaaaaaa
获得毫秒数后,将其除以1000,得到以秒为单位的浮点数,并将其传递给
datetime.datetime.utcfromtimestamp()
。可以使用方法进一步格式化此datetime对象。实现这一切的代码是

from datetime import datetime

def dnp3_to_datetime(octets):
    milliseconds = 0
    for i, value in enumerate(octets):
        milliseconds = milliseconds | (ord(value) << (i*8))

    date = datetime.utcfromtimestamp(milliseconds/1000.)
    return date.strftime('%b %d, %Y %H:%M:%S.%f UTC')
你会注意到这些结果正好滞后8小时。我无法找出这种差异,但我不认为我的方法是错误的

要从日期时间转换为DNP3时间,请从开始。然后,通过一次右移和掩蔽8位,您可以构造DNP3八位字节

def datetime_to_dnp3(date=None):
    if date is None:
        date = datetime.utcnow()
    seconds = (date - datetime(1970, 1, 1)).total_seconds()
    milliseconds = int(seconds * 1000)
    return ''.join(chr((milliseconds >> (i*8)) & 0xff) for i in xrange(6))
如果不带参数调用它,它将为您提供当前时间,但您可以选择指定任何特定的日期时间。例如,
datetime(1970,1,1)
将返回
\x00\x00\x00\x00\x00\x00\x00
datetime(1970,1,1,0,0,0,0,1000)
(1970纪元后一毫秒)将返回
\x01\x00\x00\x00\x00\x00\x00\x00


注意,根据DNP3时间中的字节数,如果您试图打印它们,可能会得到奇怪的符号。不过别担心,字节仍然存在,只是Python试图将它们编码为字符。如果要查看相互干扰的各个字节,只需打印
列表(DNP3\u ts[i])
。您可能会注意到,它将
'\x52'
打印为
R
(类似于许多ASCII可打印字符),但它们是等效的。

要从Python 3中的输入字节中获取整数:

>>> millis = int.from_bytes(b'\xec\x58\xed\xf9\x52\x01', 'little')
>>> millis
1455892027628
要将整数解释为“自历元起的毫秒数”:

注意:结果与您问题中评论中提供的结果不同(
2016年2月20日00:27:07.628

如果需要支持较旧的Python版本,请将小尾数顺序的字节转换为无符号整数:

>>> import binascii
>>> int(binascii.hexlify(b'\xec\x58\xed\xf9\x52\x01'[::-1]), 16)
1455892027628

你是一个传奇!非常感谢您的详细解释!时间戳来自澳大利亚布里斯班(UTC+10)。我想这台设备的时钟可能配置错误。这里显示的是从布里斯班时区到UTC 0的转换,时间与我之前提供的时间一致。干杯@尼可罗兹,太棒了。我几乎可以肯定,这与我能够获得历元日期时间的正确结果这一事实有一定的偏差。
>>> from datetime import datetime, timedelta
>>> utc_time = datetime(1970, 1, 1) + timedelta(milliseconds=millis)
>>> str(utc_time)
'2016-02-19 14:27:07.628000'
>>> import binascii
>>> int(binascii.hexlify(b'\xec\x58\xed\xf9\x52\x01'[::-1]), 16)
1455892027628