Python
unixtime

(Tips)これから100年のunix時間は10桁数字。5桁の固定長文字列でも表現できる。

Unixtimeの時刻表現

多くのエンジニアがご存知であろうUNIX時間(ユニックスじかん、UNIX time)。
wikipediaには以下のように書かれている(抜粋)。

UNIX時間(UNIX time)とはコンピューターシステム上での時刻表現の一種。UNIXエポック、すなわち協定世界時 (UTC) での1970年1月1日午前0時0分0秒から形式的な経過秒数(すなわち、実質的な経過秒数から、その間に挿入された閏秒を引き、削除された閏秒を加えたもの)として表される。

UNIX timeは、うるう年、ローカルタイム、サマータイムなどの扱いが必要ないシンプルな時刻表現だ(うるう秒の扱いはちょっと面倒なのかもしれないが)。
時系列の時刻管理が必要なツールを作る際に、とりあえずUnix timeにしておけば秒単位での順序付けが行える。

21世紀ののUnix時間はほぼ10桁で表せる

普段目にするUnix時間の桁数は大抵10桁だ。

Unix時間 対応する時刻(JST)
1017378540 2002-03-29 14:09:00+09:00
1490418540 2017-03-25 14:09:00+09:00
4107906540 2100-03-05 14:09:00+09:00

python3では以下のようなコードでUnix時間と対応する時刻を計算できる。

現在時刻.py
from datetime import datetime, timezone, timedelta

JST = timezone(timedelta(hours=+9), 'JST')

now = int(datetime.now().timestamp())
now_loc = datetime.fromtimestamp(now, JST)
print("Unix時間 対応する時刻(JST)")
print(now, now_loc)

20世紀のUnix時間は9桁以下、21世紀や22世紀のUnix時間はだいたい10桁ということはTipsとして、知っておいてもいいだろう。

上1桁が5になるのは100年ちょっとあと(カンマ区切りを入れた)。

5,022,450,540 2129-02-26 14:09:00+09:00
2001年から計算してみると30年ちょっとに一回上1桁が大きくなっていくらしい。

BASE91に変換してみる。

さて、10桁固定で比較もできて便利なUnix時間であるが、可読性は低い。
どうせ可読性が低いならば、BASEエンコードしてしまって、桁数を減らしてデータベースに格納してしまっても良いのではと思ったので実験してみた。

元にしたのは以下の文字を使うBASE91:

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,./:;<=>?@[]^_`{|}~"

以前のエントリー『python+redisで、Base91のキーを扱う。』 で
redisがBase91の文字列を扱えることは確認済なので、redisのキーの一部にBASE91ベースのUnix時間を突っ込むイメージ。
例えば請求を種別に管理する場合

請求種別A-XXXXX

とキーの末尾が時刻表現となるイメージ

base91ではいろんな記号が使われちゃっているけれども、マイナス(-)は使われてないので、マイナスを区切り文字に使うと良い。(base91関連の情報は以下に)
http://base91.sourceforge.net/

ということで、以下のような書捨てコードで検証してみた:

unixtime2base91.py
from datetime import datetime, timezone, timedelta

base90 ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,./:;<=>?@[]^_`{|}~"
base91=base90 + '"'

LEN = len(base91)
#base91表現のUnixtimeを取得
def get91(time, LEN=91):
    if time < LEN:
        return base91[time]
    str=""
    q=time
    while (q >= LEN ):
        q, mod = divmod(q,LEN)
        str += base91[mod]
    #print(q,mod, end=':')
    return (str + base91[q])[::-1] #反転する

#base91表現のUnixtimeを数値に戻す
def melt91(str, LEN=91):
    time=0
    lst = [base91.find(char) for char in str][::-1] #反転する
    for i, L in enumerate(lst):
       power = pow(LEN,i)
       time += L*power
    return time

JST = timezone(timedelta(hours=+9), 'JST')
now = int(datetime.now().timestamp())
now_loc = datetime.fromtimestamp(now, JST)
ft = now
str ="XXXXX"

print(f"######## before {now_loc}###########")
while (len(str) > 4):
    ft -=(60*60 *24 * 365)
    loc = datetime.fromtimestamp(ft, JST)
    str=get91(ft)
    print(str,ft,melt91(str),loc) 
print(f"######## after {now_loc}###########")
ft = now
while (len(str) < 6):
    ft +=(60*60 *24 * 365)
    loc = datetime.fromtimestamp(ft, JST)
    str=get91(ft)
    print(str,ft,melt91(str),loc) 


base91表現のUnixtimeはけっこう長い期間5桁で表せる。

結果。どうやらbase91表現のUnixtimeは、1972年から2167年の間は5桁で表せる模様。
桁数を減らすことが目的で可読性は期待していなかったけど、上1桁の文字が2年ちょいは変わらないので
大体の年はひと目で分かりそうでちょっと良さげ
(例 2020年と2021年のbase91表現のUnixtimeは「X」ではじまる)。

######## before 2018-03-25 15:16:32+09:00###########
V&/"W 1490422592 1490422592 2017-03-25 15:16:32+09:00
VY|). 1458886592 1458886592 2016-03-25 15:16:32+09:00
U:Kvf 1427350592 1427350592 2015-03-26 15:16:32+09:00
UgYZ[ 1395814592 1395814592 2014-03-26 15:16:32+09:00
T[mEo 1364278592 1364278592 2013-03-26 15:16:32+09:00
(中略)
C@Bd+ 197446592 197446592 1976-04-04 15:16:32+09:00
CmPId 165910592 165910592 1975-04-05 15:16:32+09:00
B|c=? 134374592 134374592 1974-04-05 15:16:32+09:00
Btq4m 102838592 102838592 1973-04-05 15:16:32+09:00
BD4i} 71302592 71302592 1972-04-05 15:16:32+09:00
0+Nv 39766592 39766592 1971-04-06 15:16:32+09:00

######## after 2018-03-25 15:16:32+09:00###########
W7uqN 1553494592 1553494592 2019-03-25 15:16:32+09:00
XKg#2 1585030592 1585030592 2020-03-24 15:16:32+09:00
X0S`E 1616566592 1616566592 2021-03-24 15:16:32+09:00
YDFPt 1648102592 1648102592 2022-03-24 15:16:32+09:00
Ys]k{ 1679638592 1679638592 2023-03-24 15:16:32+09:00
Y{)6k 1711174592 1711174592 2024-03-23 15:16:32+09:00
Zl2?= 1742710592 1742710592 2025-03-23 15:16:32+09:00
Z?pKb 1774246592 1774246592 2026-03-23 15:16:32+09:00
aebf) 1805782592 1805782592 2027-03-23 15:16:32+09:00
a.N1S 1837318592 1837318592 2028-03-22 15:16:32+09:00
bW":7 1868854592 1868854592 2029-03-22 15:16:32+09:00
b$=FJ 1900390592 1900390592 2030-03-22 15:16:32+09:00
cP#ay 1931926592 1931926592 2031-03-22 15:16:32+09:00
c5xwA 1963462592 1963462592 2032-03-21 15:16:32+09:00
dIj*p 1994998592 1994998592 2033-03-21 15:16:32+09:00
dyV"] 2026534592 2026534592 2034-03-21 15:16:32+09:00
eBIVg 2058070592 2058070592 2035-03-21 15:16:32+09:00
eq`q/ 2089606592 2089606592 2036-03-20 15:16:32+09:00
e_,$X 2121142592 2121142592 2037-03-20 15:16:32+09:00
fj5`$ 2152678592 2152678592 2038-03-20 15:16:32+09:00
f=sQO 2184214592 2184214592 2039-03-20 15:16:32+09:00
gcel3 2215750592 2215750592 2040-03-19 15:16:32+09:00
g+Q7F 2247286592 2247286592 2041-03-19 15:16:32+09:00
hVC@u 2278822592 2278822592 2042-03-19 15:16:32+09:00
h!@K| 2310358592 2310358592 2043-03-19 15:16:32+09:00
iN&gl 2341894592 2341894592 2044-03-18 15:16:32+09:00
i301> 2373430592 2373430592 2045-03-18 15:16:32+09:00
  (中略)
}l@P0 6063142592 6063142592 2162-02-18 15:16:32+09:00
}?&lC 6094678592 6094678592 2163-02-18 15:16:32+09:00
~e06r 6126214592 6126214592 2164-02-18 15:16:32+09:00
~.m?_ 6157750592 6157750592 2165-02-17 15:16:32+09:00
"XZKi 6189286592 6189286592 2166-02-17 15:16:32+09:00
"%Lf; 6220822592 6220822592 2167-02-17 15:16:32+09:00
BAP}1Z 6252358592 6252358592 2168-02-17 15:16:32+09:00

以上、Tipsまで。