在排查网络故障或做流量分析时,我们经常会拿到一份PCAP抓包文件。这类文件里的每个数据包都带有采集时刻的时间戳,但它并不是简单的Unix秒数,而是由秒和微秒两部分组合而成,并且采用UTC纪元记录。如果不了解它的编码方式,直接用Ruby读取出来的整数去当Unix时间处理,就会得到完全错误的时间点。下面我们具体看看怎么用Ruby正确地完成这个转换。

PCAP时间戳的底层结构
PCAP文件全局头之后,每个数据包前面都有一个16字节的包头,其中前8个字节就是时间戳。这8个字节分成两个部分:前4字节是ts_sec,表示从1970年1月1日UTC零点到抓包时刻的秒数;后4字节是ts_usec,表示不足一秒的微秒部分,取值范围是0到999999。这种拆分方式源于早期C语言struct timeval的定义,目的是在32位系统上也能保证足够的时间精度。
很多初学者会误以为ts_usec是独立的时间值,或者把它乘以1000当作毫秒直接加给秒,这都是不对的。正确的做法是将微秒除以一百万,得到一个小于1的小数,再与秒相加,构成带小数的Unix时间。例如ts_sec为1700000000、ts_usec为500000,实际Unix时间应为1700000000.5。如果忽略时区,PCAP本身记录的就是UTC,不需要再加本地时区偏移。
另外要注意字节序问题。标准PCAP文件使用大端序(big-endian)记录全局头中的魔术字,但数据包头的时间戳字段遵循文件自身声明的字节序。如果用Ruby的File#read配合unpack,必须根据魔术字判断应该用N(大端无符号)还是V(小端无符号)模板,否则在x86机器上抓的包会被解析成乱码时间。
用Ruby解析并转换的几种方案
最基础的办法是不借助第三方库,直接用Ruby内置的二进制字符串方法读取。我们打开文件跳过全局头,然后循环读取每个包头。下面这段代码演示了如何从一个已知大端格式的PCAP中提取时间戳并转成Unix浮点值:
file = File.open("demo.pcap", "rb")
global_header = file.read(24)
magic = global_header[0, 4].unpack("N").first
# 假设魔术字为0xa1b2c3d4,大端
loop do
pkt_header = file.read(16)
break if pkt_header.nil? || pkt_header.length < 16
ts_sec, ts_usec, incl_len, orig_len = pkt_header.unpack("NNNN")
unix_time = ts_sec + ts_usec / 1_000_000.0
puts unix_time
file.seek(incl_len, IO::SEEK_CUR)
end
file.close
上面的代码没有依赖任何gem,适合在受限环境里快速写脚本。它的优点是逻辑透明,你能清楚看到每一字节的去向;缺点是需要自己处理包长度跳转和字节序判断,遇到NANO秒精度(ts_usec实际存纳秒)的变体格式就容易出错。
如果你经常处理抓包文件,更推荐用pcaprub或者纯Ruby写的packetfu库。它们已经封装了文件格式差异,直接暴露出time或timestamp方法。不过即使使用库,理解底层拆分依然重要,因为某些库返回的是整数微秒,你需要自己除一百万才能得到浮点Unix时间,否则存入数据库时可能被当成纳秒而放大一千倍。
转换后的时间格式化与校验
得到浮点Unix时间后,Ruby的Time类可以接收小数秒。使用Time.at(unix_time, in: "UTC")能得到对应的UTC时间对象,再用strftime输出人类可读格式。注意Time.at第二个参数在老版本Ruby里只接受整数微秒,新版本才支持小数秒,因此稳妥写法是用Time.at(sec, usec)分开传参。
ts_sec = 1700000000
ts_usec = 500000
t = Time.at(ts_sec, ts_usec, :utc)
puts t.strftime("%Y-%m-%d %H:%M:%S.%6N UTC")
# 输出 2023-11-14 22:13:20.500000 UTC
校验环节常被遗忘。一个简单办法是把转换出的时间再减去已知抓包发生的大致时刻,如果偏差超过几分钟,多半是字节序搞反或者把纳秒当微秒。还可以在Wireshark里看同一包的显示时间,与Ruby脚本输出做交叉验证。对于批量处理,建议把转换结果写入CSV,用Excel或pandas快速画时间分布图,异常时间戳会立刻以离群点形式暴露。
此外,若你的业务系统使用本地时区存储日志,记得在格式化阶段显式调用getlocal而不是依赖服务器时区,否则容器环境里默认的UTC会让运维误判故障发生时刻。把PCAP时间转成Unix时间只是第一步,和应用日志对齐才是排查跨系统问题的关键。