Saya memiliki catatan foo
dalam database yang telah :start_time
dan :timezone
atribut.
The :start_time
adalah Waktu dalam UTC - 2001-01-01 14:20:00
, misalnya.
The :timezone
ini adalah string America/New_York
, misalnya.
Saya ingin membuat yang baru Kali objek dengan nilai :start_time
tapi yang timezone ditentukan oleh :timezone
. Saya tidak ingin memuat :start_time
dan kemudian dikonversi ke :timezone
, karena Rel akan menjadi pintar dan memperbarui waktu dari UTC harus konsisten dengan timezone.
Saat ini,
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
Sebaliknya, saya ingin melihat
=> Sat, 01 Jan 2000 14:20:00 EST -05:00
ie. Yang ingin saya lakukan:
t
=> 2000-01-01 14:20:00 UTC
t.zone = "America/New_York"
=> "America/New_York"
t
=> 2000-01-01 14:20:00 EST
Kedengarannya seperti anda ingin sesuatu di sepanjang baris
ActiveSupport::TimeZone.new('America/New_York').local_to_utc(t)
Ini kata mengkonversi ini waktu setempat (menggunakan zone) untuk utc. Jika anda memiliki Waktu.zona` set maka anda bisa saja untuk
Time.zone.local_to_utc(t)
Ini won't menggunakan timezone yang melekat pada t - ini mengasumsikan bahwa itu's lokal untuk zona waktu anda mengkonversi dari.
Salah satu kasus tepi untuk menjaga terhadap berikut ini adalah DST transisi: lokal waktu yang anda tentukan tidak mungkin ada atau mungkin ambigu.
I've hanya menghadapi masalah yang sama dan di sini adalah apa yang saya'm akan lakukan:
t = t.asctime.in_time_zone("America/New_York")
Berikut ini adalah dokumentasi pada asctime
Jika anda're menggunakan Rel, di sini adalah metode lain sepanjang garis Eric Walsh's jawaban:
def set_in_timezone(time, zone)
Time.use_zone(zone) { time.to_datetime.change(offset: Time.zone.now.strftime("%z")) }
end
Anda perlu menambahkan offset waktu ke waktu anda setelah anda mengubah itu.
Cara termudah untuk melakukan ini adalah:
t = Foo.start_time.in_time_zone("America/New_York")
t -= t.utc_offset
Saya tidak yakin mengapa anda ingin melakukan ini, meskipun itu mungkin yang terbaik untuk benar-benar bekerja dengan cara mereka dibangun. Saya kira beberapa latar belakang mengapa anda perlu menggeser waktu dan zona waktu akan membantu.
Sebenarnya, saya pikir anda perlu mengurangi offset setelah anda mengubah hal itu, seperti dalam:
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
Tergantung pada mana anda akan menggunakan Waktu ini.
Bila waktu anda adalah atribut
Jika waktu yang digunakan sebagai atribut, anda dapat menggunakan yang sama 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
Ketika anda mengatur sebuah variabel terpisah
Sama-sama menggunakan 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
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
Cepat sedikit fungsi saya datang dengan untuk menyelesaikan pekerjaan. Jika seseorang memiliki cara yang lebih efisien untuk melakukan hal ini silahkan posting!
Saya telah menciptakan beberapa metode pembantu yang hanya melakukan hal yang sama seperti yang diajukan oleh penulis asli dari posting di https://stackoverflow.com/q/16818180/936494.
Juga saya telah mendokumentasikan beberapa keanehan yang saya amati dan juga pembantu ini berisi metode untuk benar-benar mengabaikan otomatis hari-cahaya tabungan yang berlaku sementara waktu konversi yang tidak tersedia out-of-the-box dalam Rails framework:
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
Contoh-contoh yang dapat dicoba pada rel konsol atau ruby script setelah membungkus metode di atas dalam sebuah class atau module:
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
Semoga ini bisa membantu.
Saya menghabiskan waktu yang signifikan berjuang dengan Zona waktu juga, dan setelah bermain-main dengan Ruby 1.9.3 menyadari bahwa anda don't perlu mengkonversi ke nama timezone simbol sebelum konversi:
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
Apa ini berarti adalah bahwa anda dapat fokus pada mendapatkan waktu yang tepat setup pertama di wilayah anda inginkan, seperti yang anda akan berpikir tentang hal itu (setidaknya di kepala saya, saya partisi dengan cara ini), dan kemudian dikonversi pada akhir untuk zona yang ingin anda memverifikasi bisnis anda dengan logika.
Hal ini juga bekerja untuk Ruby 2.3.1.