tzdataから過去のうるう秒を抽出する 質問する

tzdataから過去のうるう秒を抽出する 質問する

ほとんどの 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.unpackman 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)

おすすめ記事