LoginSignup
0
0

More than 1 year has passed since last update.

任意のタイムゾーンを持ったActiveSupport::TimeWithZoneオブジェクトを生成する

Last updated at Posted at 2022-01-26

お題

Time.zone_default
# => #<ActiveSupport::TimeZone:0x00007fd94cdc6450 @name="UTC", @tzinfo=#<TZInfo::DataTimezone: Etc/UTC>, @utc_offset=nil>
Time.zone
# => #<ActiveSupport::TimeZone:0x00007fd94cdc6450 @name="UTC", @tzinfo=#<TZInfo::DataTimezone: Etc/UTC>, @utc_offset=nil>
Time.zone.now.zone
# => "UTC"

(戻すのが面倒だったり忘れたりするから)Time.zone_defaultTime.zone は変更せずに、なんか好きな ActiveSupport::TimeZone オブジェクトをしょった ActiveSupport::TimeWithZone オブジェクトがほしい。

activesupport-7.0.1/lib/active_support/core_ext/time/zones.rb この辺を参考にする

案1 Time.use_zone

block内だけタイムゾーンを変更してくれるので便利。

Time.use_zone('Tokyo') { Time.zone.now.zone }
# => "JST"
Time.zone.now.zone
# => "UTC"

blockなしで呼ぶと no block given (yield) (LocalJumpError) が発生するよ。

注意点

current = Time.zone.now
# => Wed, 26 Jan 2022 05:01:10.768064609 UTC +00:00
Time.use_zone('Tokyo') { current }
# => Wed, 26 Jan 2022 05:01:10.768064609 UTC +00:00

I18n.l current
# => "2022年01月26日(水) 05時01分10秒 +0000"
Time.use_zone('Tokyo') { I18n.l current }
# => "2022年01月26日(水) 05時01分10秒 +0000"

Time.use_zone('Tokyo') { I18n.l Time.zone.now }
# => "2022年01月26日(水) 14時02分08秒 +0900"

既に生成された ActiveSupport::TimeWithZone オブジェクトが書き変わるわけではない。
まぁよく考えればあたりまえではある気はするけど、うっかりミス注意。

案2 Time.find_zone

Time.find_zone('Tokyo').now
# => Wed, 26 Jan 2022 13:42:54.862489203 JST +09:00

ちょっと長いけど、まぁ素直かな。
気持ち的にはTime.zone('Tokyo').now って書きたい気もする。

ちなみに Time.find_zoneTime.find_zone! の違いは

Time.find_zone('Saitama')
# => nil
Time.find_zone!('Saitama')
# => activesupport-7.0.1/lib/active_support/core_ext/time/zones.rb:85:in `find_zone!': Invalid Timezone: Saitama (ArgumentError)

こんな感じで、知らないタイムゾーン名を渡された時にnilを返す例外を発生させるかの違い。(よくあるやつ)

案3 ActiveSupport::TimeWithZone#in_time_zone

一旦適当に生成して、後で作りなおすという逆転の発想

Time.zone.now.in_time_zone('Tokyo')
# => Wed, 26 Jan 2022 14:38:11.790049006 JST +09:00

嫌いじゃないけど、ちょっとチェインが長い気もする。

案4 ActiveSupport::TimeZone.new

ActiveSupport::TimeZone.new('Tokyo').now
# => Wed, 26 Jan 2022 14:29:50.811455162 JST +09:00

ActiveSupport::TimeZone.new.now はないわ。newnowの字面が似すぎて見にくいわ。

実は.new.[]を呼んでるだけなので次に紹介する ActiveSupport::TimeZone.[] の方がいいかもね。

案5 ActiveSupport::TimeZone.[]

ActiveSupport::TimeZone['Tokyo'].now
# => Wed, 26 Jan 2022 14:28:14.118789817 JST +09:00

うん。これはとても素直。
だけど、見くらべると Time.find_zone('Tokyo').now の方が好きかなぁ。
まぁでも文脈に応じてこちらの方がいい事もあるかもね。

その他

案4や案5の様にActiveSupport::TimeZone オブジェクトを生成する方法は色々あるので activesupport-7.0.1/lib/active_support/values/time_zone.rb あたりを見てみるとおもしろいね。

番外編: String を parse するんだったら。。。

String#in_time_zone という手もある

'2022-01-26'.in_time_zone
# => Wed, 26 Jan 2022 00:00:00.000000000 UTC +00:00
'2022-01-26'.in_time_zone('Tokyo')
# => Wed, 26 Jan 2022 00:00:00.000000000 JST +09:00
'2022-01-26'.in_time_zone('Saitama')
# activesupport-7.0.1/lib/active_support/core_ext/time/zones.rb:85:in `find_zone!': Invalid Timezone: Saitama (ArgumentError)

:point_down: これは残念な結果になる。

'2022年01月26日'.in_time_zone('Tokyo')
# => <internal:timev>:310:in `initialize': argument out of range (ArgumentError)

でもまぁ、内部では Date._parse が使われているのでしょうがない。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0