有没有一种方法可以从大多数Linux发行版上分发的时区数据库中提取历史闰秒的瞬间?我正在寻找Python解决方案,但任何在命令行上运行的东西都可以。
我的用例是将GPS时间(基本上是自1980年第一颗GPS卫星启动以来的秒数)转换为UTC或本地时间。 UTC会不时进行闰秒调整,而GPS时间会线性增加。这相当于在UTC和TAI之间进行转换。 TAI也忽略闰秒,因此TAI和GPS时间应始终具有相同的偏移量。 在工作中,我们使用GPS时间作为全球天文观测同步的时间标准。
我有一些可用的功能,可以在GPS时间和UTC之间进行转换,但是我必须硬编码一个闰秒表,我从这里获取(文件tzdata2013xx.tar.gz
包含一个名为leapseconds
的文件)。每隔几年,当宣布新的闰秒时,我必须手动更新此文件。我希望能够从标准的tzdata中获取此信息,该信息会通过系统更新自动更新多次每年。
我相信这些信息隐藏在/usr/share/zoneinfo/
的某些二进制文件中。我已经使用struct.unpack
(man tzfile
提供了一些格式方面的信息)提取了其中的一些信息,但我从未完全使其正常工作。是否有任何标准软件包可以访问此信息?我知道pytz可以从相同的数据库中获取标准DST信息,但它无法访问闰秒。我还发现了tai64n,但查看其源代码后,发现它只包含一个硬编码的表格。
编辑
受steveha的回答和pytz/tzfile.py中的一些代码启发,我最终得到了一个可行的解决方案(在py2.5和py2.7上测试通过):
from struct import unpack, calcsize
from datetime import datetime
def print_leap(tzfile = '/usr/share/zoneinfo/right/UTC'):
with open(tzfile, 'rb') as f:
# read header
fmt = '>4s c 15x 6l'
(magic, format, ttisgmtcnt, ttisstdcnt,leapcnt, timecnt,
typecnt, charcnt) = unpack(fmt, f.read(calcsize(fmt)))
assert magic == 'TZif'.encode('US-ASCII'), 'Not a timezone file'
print 'Found %i leapseconds:' % leapcnt
# skip over some uninteresting data
fmt = '>%(timecnt)dl %(timecnt)dB %(ttinfo)s %(charcnt)ds' % dict(
timecnt=timecnt, ttinfo='lBB'*typecnt, charcnt=charcnt)
f.read(calcsize(fmt))
#read leap-seconds
fmt = '>2l'
for i in xrange(leapcnt):
tleap, nleap = unpack(fmt, f.read(calcsize(fmt)))
print datetime.utcfromtimestamp(tleap-nleap+1)
带有结果
In [2]: print_leap()
Found 25 leapseconds:
1972-07-01 00:00:00
1973-01-01 00:00:00
1974-01-01 00:00:00
...
2006-01-01 00:00:00
2009-01-01 00:00:00
2012-07-01 00:00:00
虽然这解决了我的问题,但我可能不会采用这种解决方案。相反,我将根据Matt Johnson的建议将leap-seconds.list与我的代码一起包含。这似乎是作为tzdata源使用的权威列表,并且可能每年由NIST更新两次。这意味着我必须手动更新,但是这个文件很容易解析并包括一个到期日期(tzdata似乎缺少此信息)。
zic
编译的,因此它们应该在tzdata更新中。正如您注意到的那样,在tzfile中显示为tzh_leapcnt
,因此您可能可以通过那种方式获得它。目前我没有更直接的答案给你。也许其他人会知道。 - Matt Johnson-Pintpytz/tzfile.py
直接复制/粘贴的)和一些随机的 tzfile.h,似乎你缺少了charcnt
字节(对于这个文件确实是 4)。 - Bas Swinckels