ほとんどの Linux ディストリビューションで配布されているタイムゾーン データベースから、歴史的な閏秒の瞬間を抽出する方法はありますか? Python でのソリューションを探していますが、コマンド ラインで機能するものであれば何でも構いません。
私の使用例は、GPS時間(基本的には1980年に最初のGPS衛星が起動されてからの秒数)とUTCまたは現地時間の間の変換です。UTCは時々うるう秒に合わせて調整されますが、GPS時間は直線的に増加します。これは、UTCとタイTAI はうるう秒も無視するため、TAI と gps-time は常に同じオフセットで進化するはずです。職場では、世界中の天文観測を同期するための時間基準として gps-time を使用しています。
GPS時間とUTCを変換する関数は動作していますが、うるう秒のテーブルをハードコードする必要がありました。ここ(ファイルtzdata2013xx.tar.gz
には というファイルが含まれていますleapseconds
)。新しい閏秒が発表されるたびに、数年ごとにこのファイルを手動で更新する必要があります。この情報は、年に数回システム アップデートによって自動的に更新される標準の tzdata から取得することを希望します。
情報は のどこかのバイナリファイルに隠されていると確信しています。 (はフォーマットに関する情報を提供します)/usr/share/zoneinfo/
を使用して一部を抽出することができましたが、完全には機能しませんでした。この情報にアクセスできる標準パッケージはありますか?struct.unpack
man tzfile
ピッツは、同じデータベースから標準のDST情報を取得しているようですが、うるう秒にはアクセスできません。また、たい64nしかし、ソース コードを見ると、ハードコードされたテーブルが含まれているだけです。
編集
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 の提案に従って、私のコードでこれを実行します。これは tzdata のソースとして使用される信頼できるリストのようで、おそらく NIST によって年に 2 回更新されます。つまり、手動で更新する必要がありますが、このファイルは解析が簡単で、有効期限が含まれています (tzdata には有効期限がないようです)。
ベストアンサー1
私はman 5 tzfile
、うるう秒情報を見つけるオフセットを計算し、うるう秒情報を読み取りました。
ファイル内で見つかった内容をさらに詳しく確認するには、「DEBUG:」印刷ステートメントのコメントを解除します。
編集: プログラムが更新され、正しく動作するようになりました。現在はファイルが使用され/usr/share/zoneinfo/right/UTC
、印刷するうるう秒が検出されます。
元のプログラムは、timezeone 省略文字をスキップしていませんでした。timezeone 省略文字はマニュアル ページに記載されていますが、ある意味隠されています (「...tt_abbrind は、ファイル内の ttinfo 構造に続くタイムゾーン省略文字の配列のインデックスとして機能します。」)。
import datetime
import struct
TZFILE_MAGIC = 'TZif'.encode('US-ASCII')
def leap_seconds(f):
"""
Return a list of tuples of this format: (timestamp, number_of_seconds)
timestamp: a 32-bit timestamp, seconds since the UNIX epoch
number_of_seconds: how many leap-seconds occur at timestamp
"""
fmt = ">4s c 15x 6l"
size = struct.calcsize(fmt)
(tzfile_magic, tzfile_format, ttisgmtcnt, ttisstdcnt, leapcnt, timecnt,
typecnt, charcnt) = struct.unpack(fmt, f.read(size))
#print("DEBUG: tzfile_magic: {} tzfile_format: {} ttisgmtcnt: {} ttisstdcnt: {} leapcnt: {} timecnt: {} typecnt: {} charcnt: {}".format(tzfile_magic, tzfile_format, ttisgmtcnt, ttisstdcnt, leapcnt, timecnt, typecnt, charcnt))
# Make sure it is a tzfile(5) file
assert tzfile_magic == TZFILE_MAGIC, (
"Not a tzfile; file magic was: '{}'".format(tzfile_magic))
# comments below show struct codes such as "l" for 32-bit long integer
offset = (timecnt*4 # transition times, each "l"
+ timecnt*1 # indices tying transition time to ttinfo values, each "B"
+ typecnt*6 # ttinfo structs, each stored as "lBB"
+ charcnt*1) # timezone abbreviation chars, each "c"
f.seek(offset, 1) # seek offset bytes from current position
fmt = '>{}l'.format(leapcnt*2)
#print("DEBUG: leapcnt: {} fmt: '{}'".format(leapcnt, fmt))
size = struct.calcsize(fmt)
data = struct.unpack(fmt, f.read(size))
lst = [(data[i], data[i+1]) for i in range(0, len(data), 2)]
assert all(lst[i][0] < lst[i+1][0] for i in range(len(lst)-1))
assert all(lst[i][1] == lst[i+1][1]-1 for i in range(len(lst)-1))
return lst
def print_leaps(leap_lst):
# leap_lst is tuples: (timestamp, num_leap_seconds)
for ts, num_secs in leap_lst:
print(datetime.datetime.utcfromtimestamp(ts - num_secs+1))
if __name__ == '__main__':
import os
zoneinfo_fname = '/usr/share/zoneinfo/right/UTC'
with open(zoneinfo_fname, 'rb') as f:
leap_lst = leap_seconds(f)
print_leaps(leap_lst)