我在数据库中有一个记录foo
,其中包含:start_time
和:timezone
个属性。
例如,:start_time
是UTC时间 - 2001-01-01 14:20:00
。
:timezone
是一个字符串 - 例如America/New_York
。
我想创建一个新的Time对象,其值为:start_time
,但其时区由:timezone
指定。我不想加载: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
即。我想这样做:
t
=> 2000-01-01 14:20:00 UTC
t.zone = "America/New_York"
=> "America/New_York"
t
=> 2000-01-01 14:20:00 EST
答案 0 :(得分:61)
听起来你想要的东西
ActiveSupport::TimeZone.new('America/New_York').local_to_utc(t)
这表示将此本地时间(使用区域)转换为utc。如果您设置了Time.zone
,那么您当然可以
Time.zone.local_to_utc(t)
这不会使用附加到t的时区 - 它假定它是您要转换的时区的本地区域。
此处要防范的一个边缘案例是DST转换:您指定的本地时间可能不存在或可能不明确。
答案 1 :(得分:19)
如果你正在使用Rails,这是另一种方法,就像Eric Walsh的回答一样:
def set_in_timezone(time, zone)
Time.use_zone(zone) { time.to_datetime.change(offset: Time.zone.now.strftime("%z")) }
end
答案 2 :(得分:15)
答案 3 :(得分:7)
转换后,您需要将时间偏移量添加到您的时间。
最简单的方法是:
t = Foo.start_time.in_time_zone("America/New_York")
t -= t.utc_offset
我不确定你为什么要这样做,尽管实际上最好按照它们的构建方式进行工作。我想一些关于你需要转移时间和时区的背景会有所帮助。
答案 4 :(得分: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
答案 5 :(得分: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
答案 6 :(得分: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
快速的小功能我想出来解决这个问题。如果某人有更有效的方法,请发布它!
答案 7 :(得分:1)
我花了很多时间在TimeZones上苦苦挣扎,在修改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。
答案 8 :(得分: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
在类或模块中包装上述方法后,可以在rails控制台或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
希望这有帮助。
答案 9 :(得分:0)
t.change(zone: 'America/New_York')
OP的前提是不正确的:“我不想加载:start_time然后转换为:timezone,因为Rails会很聪明,并且将UTC的时间更新为与该时区一致。”这不是正如此处提供的答案所证明的那样,它一定是正确的。
答案 10 :(得分:0)
这是另一个对我来说比当前答案更好的版本:
now = Time.now
# => 2020-04-15 12:07:10 +0200
now.strftime("%F %T.%N").in_time_zone("Europe/London")
# => Wed, 15 Apr 2020 12:07:10 BST +01:00
使用“%N”来进行纳秒级的传输。如果需要其他精度,请see this strftime reference。
答案 11 :(得分:0)
这对我来说很好
date = '23/11/2020'
time = '08:00'
h, m = time.split(':')
timezone = 'Europe/London'
date.to_datetime.in_time_zone(timezone).change(hour: h, min: m)
答案 12 :(得分:0)
问题是关于 Rails 的,但似乎和我一样,并不是这里的每个人都在使用 ActiveSupport,所以还有一个选择:
irb(main):001:0> require "time"
=> true
irb(main):003:0> require "tzinfo"
=> true
irb(main):004:0> t = Time.parse("2000-01-01 14:20:00 UTC")
=> 2000-01-01 14:20:00 UTC
irb(main):005:0> tz = TZInfo::Timezone.get("America/New_York")
=> #<TZInfo::DataTimezone: America/New_York>
irb(main):008:0> utc = tz.local_to_utc(t)
=> 2000-01-01 19:20:00 UTC
irb(main):009:0> tz.utc_to_local(utc)
=> 2000-01-01 14:20:00 -0500
irb(main):010:0>
local_to_utc
不做与 utc_to_local
相反的事情可能看起来像一个错误,但至少有记录:https://github.com/tzinfo/tzinfo 说:
忽略时间的偏移量 - 它被视为时区的本地时间