我试图将48位(8个八位字节)转换为使用python进行时间戳的小安全项目。我正在处理来自DNP3协议的一些网络数据包,并且我试图解码DNP3类对象的时间戳值。
根据DNP3标准," DNP3时间(以UINT48的形式):绝对时间值,表示为自1970年1月1日开始以来的毫秒数和#34;。
我有以下八位字节需要转换为日期时间:
# 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)
如果有人能帮助我完成这些步骤,我们将非常感激!
答案 0 :(得分:3)
您可以通过简单地组合字节来创建一个48位整数和一些bitwise operations。您可以将每个八位字节转换为带有ord()
的uint8,并将它们左移8个不同的倍数,以便它们在48位数字中占据不同的位置。
DNP3 encodes the bytes in a reverse order。为了使这个可视化,让你的八位字节从左到右称为A-F,A的位称为aaaaaaaa等。所以从你的八位字节到48位数字你想要实现这个顺序。
A B C D E F
ffffffff eeeeeeee dddddddd cccccccc bbbbbbbb aaaaaaaa
一旦你有毫秒,将它们除以1000得到一个以秒为单位的浮点数并将其传递给datetime.datetime.utcfromtimestamp()
。您可以使用strftime()
方法进一步格式化此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')
通过为每个DNP3时间调用此函数,您将获得以下结果。
Feb 19, 2016 14:27:07.628000 UTC
Feb 19, 2016 14:34:08.107000 UTC
Feb 19, 2016 14:42:40.460000 UTC
Feb 19, 2016 14:56:47.642000 UTC
Feb 19, 2016 14:56:48.295000 UTC
Feb 19, 2016 14:58:21.036000 UTC
Feb 19, 2016 15:17:09.147000 UTC
Feb 19, 2016 15:49:05.895000 UTC
Feb 19, 2016 15:58:30.648000 UTC
您会注意到这些结果完全滞后了8个小时。我无法弄清楚这种差异,但我不认为我的方法是错误的。
为了从日期时间到DNP3时间,请从converting the time to a timestamp of milliseconds开始。然后,通过右移和屏蔽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
,datetime(1970, 1, 1, 0, 0, 0, 1000)
(1970年代后一毫秒)将返回\x01\x00\x00\x00\x00\x00
。
注意,根据DNP3时间中的字节,如果您尝试打印它们,可能会得到奇怪的符号。不过不用担心,字节仍然存在,只是Python试图将它们编码为字符。如果您希望看到各个字节相互干扰,只需打印list(DNP3_ts[i])
即可。您可能会注意到它将'\x52'
打印为R
(类似于许多ASCII可打印字符),但它们是等效的。
答案 1 :(得分:1)
从Python 3中的输入字节中获取一个整数:
>>> millis = int.from_bytes(b'\xec\x58\xed\xf9\x52\x01', 'little')
>>> millis
1455892027628
将整数解释为“自纪元以来的毫秒数”:
>>> from datetime import datetime, timedelta
>>> utc_time = datetime(1970, 1, 1) + timedelta(milliseconds=millis)
>>> str(utc_time)
'2016-02-19 14:27:07.628000'
注意:结果与您的问题(Feb 20, 2016 00:27:07.628
)中的评论中提供的结果不同。
如果您需要支持较旧的Python版本,请将little-endian顺序的字节转换为无符号整数:
>>> import binascii
>>> int(binascii.hexlify(b'\xec\x58\xed\xf9\x52\x01'[::-1]), 16)
1455892027628