Convertir l'heure d'un fuseau horaire à un autre dans Rails

105

Mes created_athorodatages sont stockés en UTC:

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

Comment puis-je convertir l'un d'entre eux en 'Heure de l'Est (États-Unis et Canada)' (en tenant compte de l'heure d'été)? Quelque chose comme:

Annotation.last.created_at.in_eastern_time
Tom Lehman
la source

Réponses:

191

Utilisez la méthode in_time_zone de la classe 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

Donc pour votre exemple particulier

Annotation.last.created_at.in_time_zone('Eastern Time (US & Canada)')
Steve Weet
la source
7
ou vous pouvez simplement utiliser l'heure identnow.in_time_zone(3)
fl00r
8
created_at.in_time_zone("EST")est plus courte
Orlando le
51
"EST" = "Heure standard de l'Est", donc ce sera faux pendant l'heure d'été. "Heure de l'Est (États-Unis et Canada)" détermine si c'est l'heure d'été ou non pour vous.
jhiro009
1
Notez que la méthode in_time_zone fait partie d'ActiveSupport, elle est donc intégrée à Rails, mais ne fait pas partie de Ruby stdlib. Si vous avez une application de rails, pas de problème. Si vous avez une application Ruby simple, vous devrez vous assurer d'avoir besoin d'une assistance active.
Gayle
Le lien vers la documentation de cette méthode est ici: apidock.com/rails/DateTime/in_time_zone
Naved Khan
17

Bien que ce soit une vieille question, cela vaut la peine de mentionner quelque chose. Dans une réponse précédente, il est suggéré d'utiliser un before_filter pour définir le fuseau horaire temporellement.

Vous ne devriez jamais, jamais faire cela car Time.zone stocke les informations dans le thread, et il sera probablement divulgué à la prochaine requête gérée par ce thread.

À la place, vous devez utiliser un around_filter pour vous assurer que Time.zone est réinitialisé une fois la requête terminée. Quelque chose comme:

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

En savoir plus à ce sujet ici

opsidao
la source
1
Il existe de nos jours une belle méthode Time.use_zone .
freemanoid le
9

Si vous ajoutez ceci à votre /config/application.rb

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

Alors tu peux cellule

Annotation.last.created_at.in_time_zone

pour obtenir l'heure dans le fuseau horaire spécifié.

Eifion
la source
si nous configurons le fuseau horaire dans application.rb, alors pas besoin d'appeler ".in_time_zone". les rails le font automatiquement pour nous, jyst doit appeler Annotation.last.created_at
Vishal
3

Si vous configurez votre /config/application.rb

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

Time.now.in_time_zone

DateTime.now.in_time_zone
Dorian
la source
2

Réglez votre fuseau horaire sur l'heure de l'Est.

Vous pouvez définir votre fuseau horaire par défaut dans config / environment.rb

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

Désormais, tous les enregistrements que vous retirez seront dans ce fuseau horaire. Si vous avez besoin de différents fuseaux horaires, par exemple en fonction d'un fuseau horaire utilisateur, vous pouvez le modifier avec un before_filter dans votre contrôleur.

class ApplicationController < ActionController::Base

  before_filter :set_timezone

  def set_timezone
    Time.zone = current_user.time_zone
  end
end

Assurez-vous simplement de stocker tous vos temps dans la base de données au format UTC et tout sera agréable.

nitecoder
la source
Cela a-t-il une importance si vous exécutez un serveur d'applications tel que bâtard, mince ou passager? Ils exécutent plus d'une instance, à un seul thread je crois? Je me trompe peut-être, j'aimerais le savoir!
nitecoder
Ce n'était peut-être pas en '09?
nitecoder
2
@jpwynn: d'après ceci: github.com/rails/rails/commit / ... c'est Thread safe depuis plus de 5 ans. Une source ou un exemple pour votre problème?
Pascal
1
Un fil de discussion peut servir plusieurs sessions. Si vous le définissez une fois lorsque l'utilisateur est connecté, par exemple, le DERNIER utilisateur à se connecter définira le fuseau horaire pour TOUS les utilisateurs dont la session utilise ce même fil. Vous DEVEZ donc utiliser un before_filter pour le définir pour chaque utilisateur à chaque chargement de page.
jpw