Konwertuj czas z jednej strefy czasowej na inną w Railsach

105

Moje created_atsygnatury czasowe są przechowywane w UTC:

>> Annotation.last.created_at
=> Sat, 29 Aug 2009 23:30:09 UTC +00:00

Jak zamienić jeden z nich na „czas wschodni (USA i Kanada)” (biorąc pod uwagę czas letni)? Coś jak:

Annotation.last.created_at.in_eastern_time
Tom Lehman
źródło

Odpowiedzi:

191

Użyj metody in_time_zone klasy DateTime

Loading development environment (Rails 2.3.2)
>> now = DateTime.now.utc
=> Sun, 06 Sep 2009 22:27:45 +0000
>> now.in_time_zone('Eastern Time (US & Canada)')
=> Sun, 06 Sep 2009 18:27:45 EDT -04:00
>> quit

Więc dla twojego konkretnego przykładu

Annotation.last.created_at.in_time_zone('Eastern Time (US & Canada)')
Steve Weet
źródło
7
lub możesz po prostu użyć identyfikatora godzinynow.in_time_zone(3)
fl00r
8
created_at.in_time_zone("EST")jest krótszy
Orlando,
51
„EST” = „Eastern Standard Time”, więc będzie nieprawidłowy w czasie letnim. „Czas wschodni (Stany Zjednoczone i Kanada)” oblicza, czy jest to czas letni, czy nie.
jhiro009
1
Zauważ, że metoda in_time_zone jest częścią ActiveSupport, dlatego jest wbudowana w Railsy, ​​ale nie jest częścią standardowego biblioteki Ruby. Jeśli masz aplikację rails, nie ma problemu. Jeśli masz prostą aplikację Ruby, musisz upewnić się, że potrzebujesz aktywnego wsparcia.
Gayle,
Link do dokumentacji tej metody znajduje się tutaj: apidock.com/rails/DateTime/in_time_zone
Naved Khan
17

Chociaż to stare pytanie, warto o czymś wspomnieć. W poprzedniej odpowiedzi sugerowano użycie before_filter do tymczasowego ustawienia strefy czasowej.

Nigdy, przenigdy nie powinieneś tego robić, ponieważ Time.zone przechowuje informacje w wątku i prawdopodobnie wycieknie do następnego żądania obsługiwanego przez ten wątek.

Zamiast tego powinieneś użyć around_filter, aby upewnić się, że Time.zone zostanie zresetowany po zakończeniu żądania. Coś jak:

around_filter :set_time_zone

private

def set_time_zone
  old_time_zone = Time.zone
  Time.zone = current_user.time_zone if logged_in?
  yield
ensure
  Time.zone = old_time_zone
end

Przeczytaj więcej na ten temat tutaj

opsidao
źródło
1
Obecnie istnieje ładna metoda Time.use_zone .
freemanoid
9

Jeśli dodasz to do swojego /config/application.rb

config.time_zone = 'Eastern Time (US & Canada)'

Wtedy możesz dzwonić do komórki

Annotation.last.created_at.in_time_zone

aby uzyskać czas w określonej strefie czasowej.

Eifion
źródło
jeśli konfigurujemy strefę czasową w application.rb, nie ma potrzeby wywoływania ".in_time_zone". railsy automatycznie robią to za nas jyst trzeba zadzwonić do Annotation.last.created_at
Vishal
3

Jeśli skonfigurujesz /config/application.rb

config.time_zone = 'Eastern Time (US & Canada)'

Time.now.in_time_zone

DateTime.now.in_time_zone
dorycki
źródło
2

Ustaw strefę czasową na czas wschodni.

Możesz ustawić domyślną strefę czasową w config / environment.rb

config.time_zone = "Eastern Time (US & Canada)"

Teraz wszystkie wyciągane rekordy będą w tej strefie czasowej. Jeśli potrzebujesz różnych stref czasowych, powiedzmy w oparciu o strefę czasową użytkownika, możesz ją zmienić za pomocą filtru before_filter w kontrolerze.

class ApplicationController < ActionController::Base

  before_filter :set_timezone

  def set_timezone
    Time.zone = current_user.time_zone
  end
end

Tylko upewnij się, że przechowujesz wszystkie swoje czasy w bazie danych jako UTC i wszystko będzie słodkie.

nitecoder
źródło
Czy to ma znaczenie, jeśli używasz serwera aplikacji, takiego jak kundel, cienki lub pasażerski? Uruchamiają więcej niż jedną instancję, jak sądzę, jednowątkową? Może się mylę, chciałbym wiedzieć!
nitecoder
4
Time.zone=jest bezpieczny dla wątków github.com/rails/rails/blob/master/activesupport/lib/… .
bynajmniej nie
Może to nie było w '09?
nitecoder
2
@jpwynn: zgodnie z tym: github.com/rails/rails/commit/ ... jest bezpieczny dla wątków od ponad 5 lat. Jakieś źródło lub przykład Twojego problemu?
Pascal
1
Wątek może obsługiwać wiele sesji. Jeśli ustawisz ją raz, gdy użytkownik jest zalogowany, na przykład OSTATNI użytkownik, który będzie się logował, ustawi strefę czasową dla WSZYSTKICH użytkowników, których sesja używa tego samego wątku. Dlatego MUSISZ użyć filtru before_filter, aby ustawić go dla każdego użytkownika przy każdym ładowaniu strony.
jpw