web-dev-qa-db-fra.com

Rails: Comment analyser une chaîne de date/heure dans un fuseau horaire spécifique

J'utilise Rails 3.2 et Ruby 1.9.3 sur Debian. J'ai une application qui collecte une date, une heure et un fuseau horaire sous forme de chaînes via un formulaire HTML. Quelque chose comme ça:

start_date: "04-15-2010",
start_time: "10:00:00",
timezone: "Central Time (US & Canada)"

Ce que je voudrais faire, c’est d’analyser ces 3 éléments dans une seule date enregistrée dans ma base de données au format UTC, ce qui dans ce cas ajouterait 7 heures à l’heure de début, une fois que l’heure est dans le fuseau horaire UTC. l’heure mémorisée serait 17h00 une fois qu’elle est dans la base de données au format UTC au lieu de l’heure centrale reçue.

J'ai essayé quelque chose comme ça pour analyser la date:

ActiveSupport::TimeZone[timezone].at DateTime.strptime("{ 2012-04-09 20:00:00 }", "{ %Y-%m-%d %H:%M:%S }").to_i

Cependant, je ne suis pas en mesure d'intégrer le fuseau horaire dans le temps résultant avec% Z. Il ne soit pas analysé ou l'heure est interprétée comme UTC et non comme heure centrale. Ma question est donc la suivante: comment contraindre une chaîne de date dans un certain fuseau horaire sans modifier la valeur de la date/heure réelle stockée. Je voudrais pouvoir analyser la chaîne dans un objet date/heure qui inclut le fuseau horaire correct avec lui à ce moment-là afin que les conversions de fuseaux horaires soient exactes. J'ai regardé partout et je ne trouve pas le moyen de faire ça. C'est étrange, car cela semble être quelque chose de commun avec les dates saisies à partir de formulaires HTML. Merci pour toute aide.

35
Matt Schwartz

%Z est le bon moyen de spécifier un nom de fuseau horaire. Avez-vous essayé le suivant?

date_and_time = '%m-%d-%Y %H:%M:%S %Z'
DateTime.strptime("04-15-2010 10:00:00 Central Time (US & Canada)",date_and_time)
17
0x4a6f4672

Essaye ça:

zone = "Central Time (US & Canada)"  

ActiveSupport::TimeZone[zone].parse("2013-04-03 17:47:00")
98
ryancheung

Utilisez String#in_time_zone (Rails 4+)

Personnellement, je préfère utiliser String#in_time_zone:

>> '22.09.1986 10:30'.in_time_zone('Central Time (US & Canada)')
# => Mon, 22 Sep 1986 10:30:00 CDT -05:00

Cela analyse la date et l'heure de la chaîne dans le fuseau horaire fourni.

30
Artur Beljajev

J'ai enfin trouvé la façon la plus sale mais la plus définitive de le faire.

Commencez par analyser la chaîne en utilisant Plain Ruby Time.strptime comme ceci:

time = Time.strptime('12 : 00 : PM', '%I : %M : %p')

De cette façon, vous obtenez l'heure analysée, mais pas encore dans le fuseau horaire correct. Pour résoudre ce problème, convertissons l'heure en forme de chaîne et analysons-la avec l'analyse standard ActiveSupport :: TimeZone #.

Time.zone.parse(time.to_s)

Le résultat est ActiveSupport :: TimeWithZone avec notre temps analysé dans le fuseau horaire correct.

La raison pour laquelle nous devons procéder ainsi est que ni ActiveSupport :: TimeZone ni ActiveSupport :: TimeWithZone ne prennent en charge la méthode strptime. Nous devons donc analyser Time avec le noyau Ruby strptime qui ne contient pas d’information de fuseau horaire, le convertir au format acceptable dans les objets ActiveSupport, puis le réanalyser.

1
Phitherek_

Ceci est la méthode que je suis venu avec. Pas la plus jolie, mais ça marche. Permet d'analyser la chaîne en utilisant un format spécifié, puis de la convertir dans le format requis par Time.zone.parse.

class ActiveSupport::TimeZone
  def strptime(time, format='%m/%d/%Y')
    formatted = Time.strptime(time, format).strftime('%Y-%m-%d %T')
    parse(formatted)
  end
end

Ensuite, vous pouvez faire quelque chose comme ce qui a été mentionné dans une autre question, mais avec un format spécifié:

zone = "Central Time (US & Canada)"  
ActiveSupport::TimeZone[zone].strptime('2013-04-03', '%Y-%m-%d')

Ou si vous avez déjà un fuseau horaire défini:

Time.zone = "Central Time (US & Canada)" 
Time.zone.strptime('01/13/2006')

J'ai utilisé un format par défaut de %m/%d/%Y parce que c'est ce que mon utilisateur saisit le plus souvent. Vous pouvez le personnaliser selon vos besoins ou utiliser le format par défaut utilisé par DateTime qui est, à mon avis, iso8601 (%FT%T%z).

1
ifightcrime

Convertir un format de date spécifique en UTC.

ActiveSupport::TimeZone['UTC'].parse(Time.strptime('01/24/2019T16:10:16', "%m/%d/%YT%H:%M:%S").asctime)
0
DenisKo

Pour que DateTime prenne la chaîne de date et attache un fuseau horaire autre que UTC sans changer les valeurs de la chaîne de date, utilisez this, c’est facile, il ne se casse pas le jour de référence :) 

xx = DateTime.strptime("9/1/15 #{object.time_zone}", "%m/%d/%Y %Z")
0
Jon