AWK: フリーゲルの公式に基づく Unix time 変換処理
gawk が使える場合を除き、AWKスクリプトにおけるグレゴリオ暦とUnix time(POSIX time)の相互変換処理は自前で準備する必要がある。
そこで今回はフリーゲルの公式を使って実装してみる。
・unix_time.awk ※テスト用コードも実装済み
#!/usr/bin/nawk -f
# Based on Fliegel-Van Flandern algorithm.
# Inputs must be Epoch (1970/01/01 00:00:00) and over.
# Time zone will not be considered.
function to_unix_time(yyyy, mm, dd, hh, mi, ss) {
if (mm - 2 <= 0) { --yyyy; mm += 12}
days = int(365.25 * yyyy) + int(yyyy / 400) - int(yyyy / 100) \
+ int(30.59 * (mm - 2)) + dd - 719499 # 1721089 - 2440588(Epoch)
return ((days * 24 + hh) * 60 + mi) * 60 + ss
}
function to_string(unix_time) {
ss = unix_time % 60; unix_time = (unix_time - ss) / 60
mi = unix_time % 60; unix_time = (unix_time - mi) / 60
hh = unix_time % 24; unix_time = (unix_time - hh) / 24
j = unix_time + 2472632 # 2440588(Epoch) + 32044
g = int(j / 146097); dg = j % 146097
c = int((int(dg / 36524) + 1) * 3 / 4); dc = dg - c * 36524
b = int(dc / 1461); db = dc % 1461
a = int((int(db / 365) + 1) * 3 / 4); da = db - a * 365
y = g * 400 + c * 100 + b * 4 + a
m = int((da * 5 + 308) / 153) - 2
d = da - int((m + 4) * 153 / 5) + 122
yyyy = y - 4800 + int((m + 2) / 12)
mm = (m + 2) % 12 + 1
dd = d + 1
return sprintf("%04d-%02d-%02d %02d:%02d:%02d", yyyy, mm, dd, hh, mi, ss)
}
# test code
{
x = to_unix_time(substr($1, 1, 4), substr($1, 6, 2), substr($1, 9, 2))
expected = (NR - 1) * 60 * 60 * 24
if (x != expected) {
print "Assertion failed. line " NR " - Expected: " expected ", Received: " x
exit
}
print to_string(x)
}
Python の datetime モジュールの処理結果と比較することで、1970/1/1~9999/12/31 の期間の全日付について連続性および妥当性を検証する。
・unix_time_test.py
#!/usr/bin/env python import datetime s = datetime.date(1970, 1, 1) t = datetime.date(9999, 12, 31) while True: print '%s 00:00:00' % s if s >= t: break s += datetime.timedelta(days=1)
datetime.date では西暦10000年は表現できないので注意。(Exception が発生)
テスト用のシェルを作るならこのような感じである。
・unix_time_test.sh
#!/usr/bin/ksh ./unix_time_test.py > ./test_in.txt ./unix_time.awk ./test_in.txt > ./test_out.txt diff ./test_in.txt ./test_out.txt > /dev/null if [ $? -eq 0 ]; then print 'OK'; else print 'NG'; fi
改行コードがLFの場合、58657940 bytes(≒56MB) のファイルが2個生成される。(test_in.txt, test_out.txt)
Windows 環境なら diff のところを comp で検査。
・出力結果
OK
ただ実際のところ、もう少しわかりやすいコーディングのほうが周囲の理解を得やすいかもしれない。
Perl や Python、Ruby が使える環境であれば尚更である。
参考:
http://www5d.biglobe.ne.jp/~noocyte/Programming/GregorianAndJulianCalendars.html#DateToDayNumber
http://en.wikipedia.org/wiki/Julian_day
0 件のコメント:
コメントを投稿