p_chinのおっぱいブログ

UnityとPerlなど

perlで時間を固定するの処理で色々ハマった時のメモ

クライアントサイド(Unity)の時間依存の処理(2013-07-10〜2013-07-20までにログインしたら報酬プレゼントとか)のテストを書きたかったので、サーバの時間を固定する処理が欲しくて書いた時に悩む事が多かったのでメモ


BEGINEブロックでCORE::GLOBAL::timeなどをoverrideする

以下の様な空気感で、自作の関数を定義してコードリファレンスを代入する

package Fugu::FixedTime;

sub time() {
    # 固定した時間のEpochTimeを返す処理
}

sub gmtime(;$) {
    my $time = shift;
    # 引数に何も無かったら上のtime関数から参照する
    # $time = $time // Fugu..と同じ
    $time //= Fugu::FixedTime::time();
    # 上のtime関数の返り値をbuilt-inのオリジナルのgmtime関数で計算する
    CORE::gmtime($time);
}

sub localtime(;$) {
    # ここも上のgmtime関数と同じノリで書く
}

BEGINE {
    *CORE::GLOBAL::time = \&Fugu::FIxedTime::time;
    # gmtime, localtimeも同じノリで代入しちゃう
}

 

この処理の肝はEpochTimeの計算

  • overrideしたFugu::FixedTime::gmtime, Fugu::FixedTime::localtimeは,Fugu::FixedTime::timeからEpochTimeを取得して値を計算しているので、Fugu::FixedTime::timeで適切な処理を行う必要が有る.
sub time() {
    # 固定したい時間の文字列を取得する処理を呼ぶ
    my $time_str = get_global_time();
    # 固定時間を指定してなかったら素のtimeを返す
    return CORE::time unless $time_str;

    # TimeZoneを日本に指定
    my $time_zone = '+0900';

    # EpochTimeの計算だけなのでHttp::Date使う
    # (Time::PIece::Plus使いたかったけど、プロジェクト内のどこかで先に読み込まれてしまっていたので)
    return str2time($time_str . $time_zone);
}

 

TIme系モジュールの挙動や読み込むタイミング

  • Time系のモジュールは、built-inのtimeから時間を読んで、それをモジュール内のlocaltime関数に独自の処理を書いて、useされた時にExportしてるので、CORE::Globalをoverrideしてる訳じゃ無いらしい
  • 自分の名前空間上にある関数をbuilt-inよりも先にperlは見てくれるらしいので、そのような事が出来る

 

Time::Piece::Plusでのtimezoneの修正

  • strptime関数はGMTで計算してしまうので、「2013-07-07 10:00:00」をただ単に引数に渡しても、GMTの10時は日本の19時と解釈されるので、「2013-07-07 19:00:00」が返ってきてしまう。
  • Time::Piece::Plus->strptime('2013-07-07 10:00:00 +0900', '%Y-%m-%d %H:%M:%S %z')->epoch;のようにやれば、timezone指定でstrptimeが出来る。

 

Http::DateでEpochTimeを計算した理由

  • Time::Piece::Plusを使ってepoch timeを出していた
  • もっと早いタイミングでTime::Piece::Plusが呼ばれてlocal timeが置き換わってしまったらしい。

  

その他メモ

COREとCORE::Global
  • CORE::localtimeが元々のlocaltime
  • CORE::Global::local timeが書き換えられるbuilt-in関数(local time呼ぶとこっちが参照される)
日付の比較
  • Date::Time, Time::Piece:Plusでは日付のオブジェクト同士で"="などの比較が使える
  • 実際のDBデータと比較する時はstrftimeで文字列に戻して比較してやる必要があった
built-in関数のtime(epochタイムの計算)にはHttp::Dateを使った
  • "1994-02-03 14:15:29 -0100" -- ISO 8601 formatと、丁度使いたいフォーマットにも対応してた