Руби / рельсы - изменить часовой пояс времени, без изменения стоимости

У меня есть запись foo в базе данных которого есть:значение start_timeи:атрибуты часовой пояс`.

В :значение start_time-время в UTC -2001-01-01 14:20:00, например. В:временнаястрока -Америка/Нью-Йорк`, например.

Я хочу создать новый объект времени со значением :значение start_time но чей часовой пояс определяется :часовой пояс. Я не хочу, чтобы загрузить :значение start_time, а затем преобразовать в `:часовой пояс, потому что рельсы будут умными и обновление времени от 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
Комментарии к вопросу (5)
Решение

Похоже, вы хотите что-то вдоль линий

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

Это говорит преобразовать в местное время (через зону) в UTC. Если у вас есть время.зоны установить то можно конечно

Time.zone.local_to_utc(t)

Это выиграл'т использовать пояс прикрепляется к т - предполагается, что это'с данным часовом поясе вы находитесь конверсии.

Один пограничный случай для того чтобы защитить против вот переход на летнее время: местное время указать не может существовать или может быть неоднозначным.

Комментарии (2)

Я'вэ просто столкнулся с той же проблемой и вот что я'м собираюсь сделать:

t = t.asctime.in_time_zone("America/New_York")

Вот документация на asctime

Комментарии (1)

Если вы're через рельсы, вот еще один способ вдоль линий Эрик Уолш'ы ответа:

def set_in_timezone(time, zone)
  Time.use_zone(zone) { time.to_datetime.change(offset: Time.zone.now.strftime("%z")) }
end
Комментарии (2)

Вам нужно добавить смещение времени в свое время после того, как вы преобразовать его.

Самый простой способ сделать это:

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

Я не уверен, почему вы хотели бы сделать это, хотя это, вероятно, лучше работать с времен, как они строятся. Я думаю, что некоторые предпосылки, почему вам необходимо перенести время и часовые пояса была бы полезна.

Комментарии (2)

На самом деле, я думаю, нужно вычесть смещение после преобразования его, например:

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 
Комментарии (0)

Зависит от того, где вы собираетесь использовать это время.

Если ваше время-это атрибут

Если время используется в качестве атрибута, вы можете использовать тот же date_time_attribute камень:

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 камень:

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
Комментарии (0)
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

Маленькая функция Быстрая я придумал, чтобы решить задание. Если у кого-то есть более эффективный способ сделать это, пожалуйста, задайте его!

Комментарии (0)

Я создал несколько вспомогательных методов, один из которых просто делает то же самое, как у автора поста на https://stackoverflow.com/q/16818180/936494.

Также я зафиксировал несколько отличительных особенностей, которые я заметил, и эти помощники содержит методы, чтобы полностью игнорировать автоматическое день-свет сбережения действующим во время перехода, который не доступен в среде 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

Примеры, которые можно попробовать на рельсы консоли или скрипта после обертывания вышеуказанных методов в классе или модуле:

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

Надеюсь, что это помогает.

Комментарии (0)

Я потратил значительное время борется также с часовых поясов, а после мастерить с 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.

Комментарии (1)