2013-05-29 11 views
67

:timezone属性を持つデータベースにfooというレコードがあります。Ruby/Rails - 値を変更せずに時間のタイムゾーンを変更してください

:start_timeは、たとえば、UTC-2001-01-01 14:20:00の時刻です。 :timezoneは、たとえば、文字列 - America/New_Yorkです。

:start_timeの値で、タイムゾーンが:timezoneで指定された新しいTimeオブジェクトを作成したいとします。 :start_timeをロードしてから:timezoneに変換することは望ましくありません。なぜなら、Railsは賢明になり、そのタイムゾーンと一貫するようにUTCから時刻を更新するからです。

現在、

t = foo.start_time 
=> 2000-01-01 14:20:00 UTC 
t.zone 
=> "UTC" 
t.in_time_zone("America/New_York") 
=> Sat, 01 Jan 2000 09:20:00 EST -05:00 

は代わりに、私は

=> Sat, 01 Jan 2000 14:20:00 EST -05:00 

すなわちを見てみたいです。私がやりたい:あなたはこれをUTCに、このローカルタイム(ゾーンを使用)に変換言う

ActiveSupport::TimeZone.new('America/New_York').local_to_utc(t) 

の線に沿って何かをしたいよう

t 
=> 2000-01-01 14:20:00 UTC 
t.zone = "America/New_York" 
=> "America/New_York" 
t 
=> 2000-01-01 14:20:00 EST 
+1

次のとおりです。http://api.rubyonrails。 org/classes/Time.html#method-c-use_zone – MrYoshiji

+2

タイムゾーンを正しく使用しているとは思いません。 ローカルからUTCとしてdbに保存すると、現地時間で解析し、相対utcで保存すると何が問題になりますか? – Trip

+0

ええと...私はあなたがこれをする必要がある理由を説明する必要があるかもしれない*あなたが助けを受けるのを助けてくれると思う?最初に間違った時間をデータベースに保存するのはなぜですか? – nzifnab

答えて

46

が鳴ります。あなたがTime.zoneセットを持っているなら、あなたはもちろん

Time.zone.local_to_utc(t) 

にこれはトンに添付​​タイムゾーンを使用することはありませんすることができます - それはそれはあなたから変換された時間帯にローカルだということを前提としています。

DSTトランジションは、指定したローカルタイムが存在しないか、あいまいである可能性があります。

+11

local_to_utcとTime.use_zoneの組み合わせは、私が必要としていたものです: 'Time.use_zone(self.timezone){Time.zone.local_to_utc(t)} .localtime' – rwb

+0

DSTの遷移に対して何をお勧めしますか? Time.nowが変更前である間に変換する目標時間がD/ST遷移後であるとします。それは働くだろうか? –

6

変換後に時間オフセットを追加する必要があります。おそらく最高のは、実際の時間で、彼らが構築されているように動作するようにカントー

これを行う最も簡単な方法は....これをしたいと思う理由

t = Foo.start_time.in_time_zone("America/New_York") 
t -= t.utc_offset 

ではないはずです。私はあなたが時間と時間帯をシフトする必要がある理由についてのいくつかの背景を推測すると役に立つでしょう。

+0

これは私のために働いた。使用時にオフセット値が設定されていれば、これは夏時間のシフト中に正しく維持されるはずです。 DateTimeオブジェクトを使用している場合は、「offset.seconds」を追加または削除できます。 – JosephK

4

実は、私はあなたがそれを変換後のオフセットを減算する必要があると思う、のように:

1.9.3p194 :042 > utc_time = Time.now.utc 
=> 2013-05-29 16:37:36 UTC 
1.9.3p194 :043 > local_time = utc_time.in_time_zone('America/New_York') 
=> Wed, 29 May 2013 12:37:36 EDT -04:00 
1.9.3p194 :044 > desired_time = local_time-local_time.utc_offset 
=> Wed, 29 May 2013 16:37:36 EDT -04:00 
3

することは、この時間を使用しようとしている場所によって異なります。

お時間が時間を属性として使用されている場合は、同じdate_time_attribute gemを使用することができます属性

です:

class Task 
    include DateTimeAttribute 
    date_time_attribute :due_at 
end 

task = Task.new 
task.due_at_time_zone = 'Moscow' 
task.due_at      # => Mon, 03 Feb 2013 22:00:00 MSK +04:00 
task.due_at_time_zone = 'London' 
task.due_at      # => Mon, 03 Feb 2013 22:00:00 GMT +00:00 

あなたが別の変数を設定

同じものを使用するdate_time_attribute gem

my_date_time = DateTimeAttribute::Container.new(Time.zone.now) 
my_date_time.date_time   # => 2001-02-03 22:00:00 KRAT +0700 
my_date_time.time_zone = 'Moscow' 
my_date_time.date_time   # => 2001-02-03 22:00:00 MSK +0400 
1
def relative_time_in_time_zone(time, zone) 
    DateTime.parse(time.strftime("%d %b %Y %H:%M:%S #{time.in_time_zone(zone).formatted_offset}")) 
end 

私は仕事を解決するために思いついた。誰かがこれを行うより効率的な方法を持っている場合は、投稿してください!

15

あなたがRailsのを使用している場合は、ここではエリック・ウォルシュの答えの線に沿って別の方法は次のとおりです。

def set_in_timezone(time, zone) 
    Time.use_zone(zone) { time.to_datetime.change(offset: Time.zone.now.strftime("%z")) } 
end 
+1

DateTimeオブジェクトをTimeWithZoneオブジェクトに変換するには、 '.in_time_zone'を最後までタックしてください。 – Sooie

+0

@Brian Murphy-Dyeこの関数を使って夏時間に問題がありました。 DSTで動作するソリューションを提供するために質問を編集できますか? 'Time.zone.now'を、あなたが変更したい時刻に最も近いものに置き換えてしまうのでしょうか? –

0

私は元に頼まれるとちょうど同じことを行いそのうちの一つ、いくつかのヘルパーメソッドを作成しました投稿の著者はRuby/Rails - Change the timezone of a Time, without changing the valueです。

また、私は私が観察し、いくつかの特殊性を文書化しており、また、これらのヘルパーは完全にアウトオブボックスのRailsフレームワークでは利用できない時間の変換中に該当する自動日-光貯蓄を無視するメソッドが含まれています。

def utc_offset_of_given_time(time, ignore_dst: false) 
    # Correcting the utc_offset below 
    utc_offset = time.utc_offset 

    if !!ignore_dst && time.dst? 
     utc_offset_ignoring_dst = utc_offset - 3600 # 3600 seconds = 1 hour 
     utc_offset = utc_offset_ignoring_dst 
    end 

    utc_offset 
    end 

    def utc_offset_of_given_time_ignoring_dst(time) 
    utc_offset_of_given_time(time, ignore_dst: true) 
    end 

    def change_offset_in_given_time_to_given_utc_offset(time, utc_offset) 
    formatted_utc_offset = ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, false) 

    # change method accepts :offset option only on DateTime instances. 
    # and also offset option works only when given formatted utc_offset 
    # like -0500. If giving it number of seconds like -18000 it is not 
    # taken into account. This is not mentioned clearly in the documentation 
    # , though. 
    # Hence the conversion to DateTime instance first using to_datetime. 
    datetime_with_changed_offset = time.to_datetime.change(offset: formatted_utc_offset) 

    Time.parse(datetime_with_changed_offset.to_s) 
    end 

    def ignore_dst_in_given_time(time) 
    return time unless time.dst? 

    utc_offset = time.utc_offset 

    if utc_offset < 0 
     dst_ignored_time = time - 1.hour 
    elsif utc_offset > 0 
     dst_ignored_time = time + 1.hour 
    end 

    utc_offset_ignoring_dst = utc_offset_of_given_time_ignoring_dst(time) 

    dst_ignored_time_with_corrected_offset = 
     change_offset_in_given_time_to_given_utc_offset(dst_ignored_time, utc_offset_ignoring_dst) 

    # A special case for time in timezones observing DST and which are 
    # ahead of UTC. For e.g. Tehran city whose timezone is Iran Standard Time 
    # and which observes DST and which is UTC +03:30. But when DST is active 
    # it becomes UTC +04:30. Thus when a IRDT (Iran Daylight Saving Time) 
    # is given to this method say '05-04-2016 4:00pm' then this will convert 
    # it to '05-04-2016 5:00pm' and update its offset to +0330 which is incorrect. 
    # The updated UTC offset is correct but the hour should retain as 4. 
    if utc_offset > 0 
     dst_ignored_time_with_corrected_offset -= 1.hour 
    end 

    dst_ignored_time_with_corrected_offset 
    end 
クラスやモジュールで上記の方法をラップした後、レールコンソールまたはRubyスクリプトに試すことができ

例:

dd1 = '05-04-2016 4:00pm' 
dd2 = '07-11-2016 4:00pm' 

utc_zone = ActiveSupport::TimeZone['UTC'] 
est_zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] 
tehran_zone = ActiveSupport::TimeZone['Tehran'] 

utc_dd1 = utc_zone.parse(dd1) 
est_dd1 = est_zone.parse(dd1) 
tehran_dd1 = tehran_zone.parse(dd1) 

utc_dd1.dst? 
est_dd1.dst? 
tehran_dd1.dst? 

ignore_dst = true 
utc_to_est_time = utc_dd1.in_time_zone(est_zone.name) 
if utc_to_est_time.dst? && !!ignore_dst 
    utc_to_est_time = ignore_dst_in_given_time(utc_to_est_time) 
end 

puts utc_to_est_time 

この情報がお役に立てば幸いです。

1

私もタイムゾーンに苦しんでかなりの時間を費やし、とRuby 1.9.3いじりの後にあなたが変換する前に、名前のタイムゾーンのシンボルに変換する必要がないことを実現:

my_time = Time.now 
west_coast_time = my_time.in_time_zone(-8) # Pacific Standard Time 
east_coast_time = my_time.in_time_zone(-5) # Eastern Standard Time 

これが意味する何されますあなたが望む地域で最初に適切な時間設定を得ることに集中することができます。あなたはそれについて考えるでしょう(少なくとも私の頭の中で私はこのようにパーティションを分割します)。最後に検証したいゾーンに変換しますあなたのビジネスロジックを。

これはRuby 2.3.1でも動作します。私はここに同じ問題に直面してきた

2

は私がするつもりです何です:

ここ
t = t.asctime.in_time_zone("America/New York") 

は多分これが役立ちますdocumentation on asctime

+0

それは私が期待したことだけです – Kaz

関連する問題