にしし ふぁくとりー:西村文宏 個人サイト

Presented by Nishishi via Movable Type. Last Updated: 2023/03/01. 00:13:29.

timelocal関数に渡す年の値は、西暦から1900を引かない方が良い

timelocal関数へ渡す「年」の値は、西暦を-1900せずに、そのままの4桁を渡す!

エポック秒(UNIX時間)から日時を得るPerlの組み込み関数 localtime では、「年」の値が1900年からの相対値で得られる仕様なので、返ってきた値から西暦を知るには 1900 を足してやる必要があります。

で、そのlocaltime関数と逆のこと(=日時をエポック秒に変換)をやってくれる Time::Localモジュールの timelocal関数では、当然「年」の値を「西暦-1900」で渡さねばならないのだろう……と思ってそうしていたのですけども、そうするのは望ましくないんですね!
timelocal関数に「年」の情報を渡すときには、普通に西暦の4桁をそのまま渡せば良いのであって、1900を引いてしまうと(たまたま意図通りの結果が得られるケースもあるのですけども)場合によっては意図しない結果が得られることもあるのだと知りました。

▼過去の日付のつもりなのに、未来の日付だと解釈されるケースがある

具体的には、西暦から1900を引いた結果が2桁の数値になる場合、その2桁の数値を年の値としてtimelocal関数へ渡してしまうと、「現在から50年以上前の日付」に対しては未来の日付だと解釈されてしまう仕様なのだそうです。

  • 西暦から1900を引いた値が2桁の場合、それは「今年から見た前後50年間の西暦の下2桁」だと解釈される
  • 西暦から1900を引いた値が3桁の場合は、1900年からの経過年数だと解釈される。(なので2899年までは問題ない)

……という仕様なのだとか。

わざわざ -1900 する手間をかけると(変換可能な年の幅に制約ができて)不便になってしまい、
そのまま西暦を渡せば何の問題もなく変換できる仕様だったとは……。

なんてこったい。┌(:3」└)┐

▼気付いたのは50年以上前の日付を扱ってみたとき

なんか1973年あたり以前の日付を処理するときにおかしなことになるなあ……と思って調べてみたところ、下記の分かりやすい解説記事を見かけて初めて知りました。^^;
Perl : [ timegm / timelocal ] で「年」引数の仕様の正しい理解と使い方について

今年が2023年なので、50年前の1973年あたり以前の日付を示そうとして例えば 72 を渡すと、それは 1972年のことではなく 2072年のことだと解釈されるわけですね。
1972年を示したいのなら、単に 1972 をそのまま(timelocal関数へ)渡せば良かったのでした。

localtime関数でエポック秒(UNIX時間)から日時を得る場合 → 年には1900を足し、月には1を足す

例えば、変数 $epoch に任意のエポック秒が入っているとき、

my ($sec, $min, $hour, $mday, $month, $year, $wday) = localtime($epoch);
$year += 1900;	# 1900を足すと西暦になる(=西暦1900年からの相対値で得られるため)
$month += 1;	# 1を足すと月になる(=Januaryが0でDecemberが11なので)

……のような感じでPerlソースを書くと、変数 $year には西暦、$month には月、$mday には日、$hour には時、$min には分、$sec には秒が入ります。
なお、$wday には曜日が数値で入ります。(0=日曜日、1=月曜日、……、6=土曜日)

このように、localtime関数では、西暦を得るには返ってきた年の値に 1900 を足さないといけませんし、月を得るには返ってきた月の値に 1 を足さないといけません。

timelocal関数で日時からエポック秒(UNIX時間)を得る場合 → 年には西暦そのまま、月は1を引く

上記の逆で、各変数に格納された任意の年・月・日・時・分・秒の値(=下記2行目)からエポック秒を求める場合は、

use Time::Local;
my ($year, $month, $mday, $hour, $min, $sec) = (2023, 2, 23, 12, 34, 56);  # 年・月・日・時・分・秒の値
$year -= 1900;	# ←この処理は要らない……!
$month -= 1;	# 月は0~11でないといけないので1を引く
my $epoch = timelocal($sec,$min,$hour,$mday,$month,$year);

変に西暦から1900を引いたりせずに西暦4桁はそのまま渡して、上記のような感じでPerlソースを書くと、変数 $epoch に指定した日時のエポック秒(UNIX時間)が数値で入ります。

というわけで

localtime関数とは違って、timelocal関数の年の値は最初から西暦4桁をそのまま渡せる仕様なので、何も加工せずに西暦4桁の数値を引数に与えれば良いのだった、という話でした。

コメント

コメント数: 0件

コメント投稿欄 この日記に対するコメント投稿を歓迎します。



※本文中にURLは書けません。(書くと投稿が拒否されますのでご注意下さい。)

※ご投稿頂いた内容は、掲載前に管理者が確認する設定にしている場合があります。たいていは数日以内には表示されるはずですので、気長にお待ち願います。m(_ _)m

著者紹介


にしし(西村文宏)

にししでございます。本書いたり記事書いたりしてます。あと萌えたり。著書5冊発売中です(Web製作系4冊+小説1冊)。著書や記事は「西村文宏」名義。記事は主にAll Aboutで連載。本の最新刊は2011年3月に発売されたライトノベルでございますよ。

Twitter:にしし/西村文宏
にしし/西村文宏 on facebook にしし/西村文宏 on mixi フォローはお気軽に!

にしし(西村文宏)連絡先
☕ コーヒーをおごる

著書一覧と詳細

にししふぁくとりー Sakura scope内限定での主要なカテゴリ

--- 当サイト内を検索 ---