Ruby で時刻のタイムゾーンを変更するにはどうすればよいですか?
概要
タイムゾーン (UTC など) を持つ Time オブジェクトがあるとします。 「時間」などの時間の他のパラメータを変更せずに、タイムゾーンを任意のタイムゾーン(別名、ローカルタイムゾーンではない)にリセットしたいと考えています。 バージョン 3.3 の Ruby では、それを行う単一の方法が提供されていないようです (Time の公式ドキュメントと Stackoverflow の回答を参照)。では、どうすればこれを達成できるでしょうか?
DateTime クラスの場合、change メソッドは (一見) この回答のように機能することに注意してください [編集: これは Rails メソッドです]。しかし、
Ruby 3と同様なので、Timeクラスでそれを行う方法を知りたいです。
これらの回答で述べたように、Rails はいくつかの便利なメソッドを実装していることにも注意してください。しかし、それらは純粋な Ruby では利用できません (実際、Rails の in_time_zone は別の時間パラメーターである時間を変更します)。
純粋な Ruby でそれを行うにはどうすればよいでしょうか?
解決策
Time インスタンスは不変です。つまり、既存の Time オブジェクトを変更することはできません。あるいは、純粋な Ruby で行うのは少し面倒ですが (公式ドキュメントを参照)、既存のものに基づいて新しい Time インスタンスを作成することも可能です。その場合、他のタイムゾーンを変更せずに、新しい任意の「タイムゾーン […]」を設定します。時間のパラメータ」、つまり既存の時間から新しい時間にすべてコピーします。
ここではいくつかの方法をリストします (注: シングルトン メソッドやインスタンス変数は継承されません)。
require "time"
t = Time.now(in: "+00:00")
# => <2024-02-24 01:23:45 +0000>
t1= Time.new(*(t.to_a.reverse[4..-1]), in: "+09:00")
# => <2024-02-24 01:23:45 +0900>
t2= Time.new(t.year, t.month, t.day, t.hour, t.min, t.sec+t.subsec, in: "+09:00")
# => <2024-02-24 01:23:45 ... +0900>
ar = t.strftime("%Y %m %d %H %M %S %N").split.map(&:to_i)
t3= Time.new(*(ar[0..4]+[ar[5]+ar[6].quo(1_000_000_000), "+09:00"]))
# => <2024-02-24 01:23:45 ... +0900>
t4= Time.at(t, in: "+09:00") - 9*3600
# => <2024-02-24 01:23:45.... +0900>
ここで、t1 は 1 秒までしか考慮しませんが、他のものはナノ秒まで考慮します。
t2 は最も直感的ですが、最も面倒です。ここで、Time#subsec は Rational を返すため、数式には浮動小数点の丸め誤差が含まれません。
t3 には t2 に比べて利点がない可能性があります。 t2 のような浮動小数点の丸め誤差は含まれません。
t4 は長さはそれほど長くありませんが、DRY 原則に反していることと、パリティ (プラスかマイナスか?) を正確に指定する必要があるという事実は、重大な欠点です。