お題
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_default
や Time.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_zone
と Time.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
はないわ。new
とnow
の字面が似すぎて見にくいわ。
実は.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)
これは残念な結果になる。
'2022年01月26日'.in_time_zone('Tokyo')
# => <internal:timev>:310:in `initialize': argument out of range (ArgumentError)
でもまぁ、内部では Date._parse
が使われているのでしょうがない。