皆さんはアプリケーション開発中に、タイムゾーンを意識することはどれくらいあるでしょうか?
普段の開発だと、一度設定してしまえば中々意識しにくいものですが、たまたま今回こちらに関して触る機会があったので記事にすることにしました。
今回やりたかったこと
UTCからの時差を+09:00
のような、±xx:yy
(xxが時間、yyが分)の形式で取得することです。
というのも、Elasticsearchに時差を考慮した日程の絞り込み検索を行いたい、と考えていました。
Elasticsearchでcreated_atが2022年4月のドキュメントを探したい、という場合以下のような絞り込みを行います(下記はクエリの一部を抜粋)。
{"filter":{"range":{"created_at":{"gte":"2022-04-01","lt":"2022-05-01"}}}
しかし、これではタイムゾーンが何も配慮していないため、時間の絞り込みはUTC基準で行われ、日本時間だと9時間ずれてしまいます。
この場合、そもそもcreated_atを9時間ずらして入れるか、time_zoneを下記のように指定する形を取ります。
{"filter":{"range":{"created_at":{"gte":"2022-04-01","lt":"2022-05-01","time_zone":"+09:00"}}}
今回は後者の、time_zoneを指定する方法を取ることにしました。
現在、rubyでパラメーターを作成し、それをFaradayで投げることで検索結果を受け取っているので、rubyでタイムゾーンの取得が必要になったというわけです。
方法1. strftimeを使って現在時間からタイムゾーンのみをパースする
パッと思いついたのは、strftimeメソッドを使ったもので、以下のようなものです。
Time.zone.now.strftime("%:z")
# => "+09:00"
Time.zoneでアプリケーションのタイムゾーンを取得(今回はTokyo
)し、今の時間をタイムゾーンのみパースすることで時差を取得します。
フォーマットも、%:z
をつかうことで、±xx:yy
の形式で獲得することができます。
しかし、ここで一つ思いました。思ってしまいました。
現在時間取得する意味なくない? と。
Time.zoneまではいいとして、問題はnowメソッド。実行時間としては大してかからないものの、現在時刻は取得する必要はないので、削れるなら削ってしまいたい。
これくらいなら自作してもいいけど、今回は作る以外でいい感じに取得する方法はないか調査してみることにしました。
方法2. seconds_to_utc_offsetメソッドを使う
source_locationなどを使って、メソッドを調べていたところ、ちょうど求めていたメソッドを見つけました。
それが、ActiveSupport::TimeZone.seconds_to_utc_offset
です。
# Time.zone.utc_offset => 32400
ActiveSupport::TimeZone.seconds_to_utc_offset(Time.zone.utc_offset)
# => "+09:00"
上記ではTime.zone.offset
メソッドで、UTCからの時差を秒数で取得し、それを元にUTCからの時差を整形して出力しています。
実装を見ればわかるのですが、かなり素朴な実装なので、どう処理をしてるのか理解してしまえば自分で実装してしまうのも良さそうです。
探してみて
今回の用途であれば自前で作るのも簡単なので、エイッと作っても良かったのですが、調べてみて良かったことが2つほどあります。
一つはTimeオブジェクトへの理解が深まったこと。普段何気なしに使っているものですが、あえて調べてみることで知らない関数なども知ることができ、今後の実装にも行かせそうです。
もう一つは多くのユーザーが使っている、洗練された実装を見ることで あっ、ここでこのメソッドを使うのか! という学びを得られたことです。
今回のActiveSupport::TimeZone.seconds_to_utc_offset
でも、abs(実数の絶対値を取るメソッド)を使った実装で記述を短く、そして分かりやすくできており、何も見ずに自前で作った場合はめんどくさい分岐を書いていたような気がします。
普段、中々公式ドキュメントを読むことは多くありますが、実際の実装を見に行くのは不具合でもないと中々ないような気がします。
しかし、たまにはこの実装どうなってるんだろう?と興味を持って見に行くのも良さそうですね。