2018-05-30

タイムゾーン、実はRubyに関心があり、なんか気味が悪いので一応確認しておく。

昨夜Cのタイムゾーンについて有益なやりとりがあったわけだけれど、
https://twitter.com/e_toyoda/status/1001473595623665671
https://twitter.com/vaporoid/status/1001479712688881664

実のところ、大元の興味は「RubyでENV['TZ']='JST-9' とすればTime#strftimeの挙動を変えられると言ってよいか」に関心がありました。たまにはちゃんと確認してみよう。

まあまずもってrubyのソースコードはざっくりクラス別になっていて下のほうにメソッドテーブルの構築がある。
Time#strftimeは https://github.com/ruby/ruby/blob/trunk/time.c#L4926 で time_strftime() が実体と知れる。
time_strftime() は https://github.com/ruby/ruby/blob/trunk/time.c#L4467 にあり、中心的ロジックは rb_strftime_alloc() である。
rb_strftime_alloc() は https://github.com/ruby/ruby/blob/trunk/time.c#L4249 にあり、中心的ロジックはrb_strftime_timespec() である。
rb_strftime_timespec() は https://github.com/ruby/ruby/blob/trunk/strftime.c#L925 にあり、rb_strftime_with_timespec を呼んでいる。
rb_strftime_with_timespec() は https://github.com/ruby/ruby/blob/trunk/strftime.c#L228 にあり、その引数 vtm (型は struct vtm でそれは C 標準 struct tm に類似)が年月日時分をもっていることが知れる。

変数をつかんだのでこんどはスタックを逆トレース。
rb_strftime_with_timespec() の引数 vtm は第5引数であり、rb_strftime_timespec() の第四引数 vtm であり、rb_strftime_alloc() の第四引数vtmであり、それは time_strftime() の中で、
&tobj->vtm という値であり < https://github.com/ruby/ruby/blob/trunk/time.c#L4490
tobj を設定するのは MAKE_TM マクロであり < https://github.com/ruby/ruby/blob/trunk/time.c#L447
そのマクロ定義 https://github.com/ruby/ruby/blob/trunk/time.c#L1631 をみると、 time_get_tm() によって作られている。けっこう重い処理なので、一度呼ぶと (tobj)->tm_got != 1 になってスキップされる、キャッシュ的構造になっている。tobj 内部においては struct vtm 型の vtm メンバーはあくまで従だ。

さて time_get_tm() に戻ると、 https://github.com/ruby/ruby/blob/trunk/time.c#L4476 で time_gmtime(), time_fixoff(), time_localtime() のどれかが呼び出される。
たとえば time_localtime() ならば https://github.com/ruby/ruby/blob/trunk/time.c#L3367 にあり localtimew() が呼ばれる。
localtimew() は https://github.com/ruby/ruby/blob/trunk/time.c#L1552 に置かれていて、処理は分岐するが time_t 表現可能範囲内であれば、 localtime_with_gmtoff_zone() が呼ばれる。
localtime_with_gmtoff_zone() は https://github.com/ruby/ruby/blob/trunk/time.c#L1461 にあって、中心的ロジックは LOCALTIME() である。
LOCALTIME() はhttps://github.com/ruby/ruby/blob/trunk/time.c#L681 にある。

   それは、 tzset(3) を呼んでから、 rb_localtime_r() をしている。

その中で localtime_r(3), localtime(3), mktime(3) などがconfigure結果に応じて選択されるのだけれど、要は、毎回tzset(3)をしているということである。

あと、念のため、ENV のメソッドたちは hash.c にあって、 ENV.[]= は setenv(3) をちゃんと呼んでいる。
https://github.com/ruby/ruby/blob/trunk/hash.c#L3570

0 件のコメント:

コメントを投稿