将SNMP八位字节字符串转换为可读日期格式

8 投票
3 回答
24407 浏览
提问于 2025-04-16 09:15

我在使用pysnmp这个框架时,进行SNMP遍历时获取了一些值。不过,对于这个OID:

1.3.6.1.21.69.1.5.8.1.2 (DOCS-CABLE-DEVICE-MIB)

我得到了一个奇怪的结果,无法正确打印出来,因为它包含了一些像BELACK这样的ASCII字符。

当我用repr函数查看时,得到的是:

OctetString('\x07\xd8\t\x17\x03\x184\x00')

但我期望的输出应该是:

2008-9-23,3:24:52.0

这个格式叫做“日期和时间”。我该如何将OctetString的输出转换成“人类可读”的日期和时间呢?

3 个回答

2

在这里我想推荐一下:Pycopia的SNMP和SMI模块可以正确处理这个对象,还有其他的对象也可以。你可以从源代码安装Pycopia,如果你试用的话,别忘了下载mibs文件

4

@Paulo Scardine:这是我在网上找到的最好的答案,当时我在解决一个非常相似的问题。即使有了这个答案,我还是花了一些时间才解决我的问题,所以我想发一个后续的回答,可能会让事情更清楚一些。(特别是关于日期有不同长度选项的问题)。

下面这段代码连接到一个服务器,获取系统时间,然后将其作为字符串输出,以展示这个方法。

import netsnmp
import struct
oid = netsnmp.Varbind('hrSystemDate.0')
resp = netsnmp.snmpget(oid, Version=1, DestHost='<ip>', Community='public')
oct = str(resp[0])
# hrSystemDate can be either 8 or 11 units in length.
oct_len = len(oct)
fmt_mapping = dict({8:'>HBBBBBB', 11:'>HBBBBBBcBB'})
if oct_len == 8 or oct_len == 11:
    t = struct.unpack(fmt_mapping[oct_len], oct)
    print 'date tuple: %s' % (repr(t))
else:
    print 'invalid date format'

希望这能帮助到其他遇到类似问题的人,特别是在处理这种数据时。

17

你可以在这里找到格式说明。

A date-time specification. 
            field  octets  contents                  range
            -----  ------  --------                  -----
              1      1-2   year*                     0..65536
              2       3    month                     1..12
              3       4    day                       1..31
              4       5    hour                      0..23
              5       6    minutes                   0..59
              6       7    seconds                   0..60
                           (use 60 for leap-second)
              7       8    deci-seconds              0..9
              8       9    direction from UTC        '+' / '-'
              9      10    hours from UTC*           0..13
             10      11    minutes from UTC          0..59
* Notes:
            - the value of year is in network-byte order
            - daylight saving time in New Zealand is +13 For example, 
              Tuesday May 26, 1992 at 1:30:15 PM EDT would be displayed as:
                 1992-5-26,13:30:15.0,-4:0 
              Note that if only local time is known, then timezone
              information (fields 8-10) is not present.

为了处理你的示例数据,你可以用这个简单粗暴的一行代码:

>>> import struct, datetime
>>> s = '\x07\xd8\t\x17\x03\x184\x00'
>>> datetime.datetime(*struct.unpack('>HBBBBBB', s))
datetime.datetime(2008, 9, 23, 3, 24, 52)

上面的例子并不完美,它没有考虑到大小(这个对象的大小是可变的),而且缺少时区信息。另外要注意,第7个字段是十分之一秒(0到9),而timetuple[6]是微秒(0 <= x < 1000000);正确的实现留给读者自己去做

[更新]

8年后,让我们来试着修正这个答案(我是不是太懒了?):

import struct, pytz, datetime

def decode_snmp_date(octetstr: bytes) -> datetime.datetime:
    size = len(octetstr)
    if size == 8:
        (year, month, day, hour, minutes, 
         seconds, deci_seconds,
        ) = struct.unpack('>HBBBBBB', octetstr)
        return datetime.datetime(
            year, month, day, hour, minutes, seconds, 
            deci_seconds * 100_000, tzinfo=pytz.utc)
    elif size == 11:
        (year, month, day, hour, minutes, 
         seconds, deci_seconds, direction, 
         hours_from_utc, minutes_from_utc,
        ) = struct.unpack('>HBBBBBBcBB', octetstr)
        offset = datetime.timedelta(
            hours=hours_from_utc, minutes=minutes_from_utc)
        if direction == b'-':
            offset = -offset 
        return datetime.datetime(
            year, month, day, hour, minutes, seconds, 
            deci_seconds * 100_000, tzinfo=pytz.utc) + offset
    raise ValueError("The provided OCTETSTR is not a valid SNMP date")

我不确定我算的时区偏移是否正确,但我没有样本数据来测试,如果你有更好的方法,欢迎修改这个答案或者在评论中提醒我。

撰写回答