2010-12-17 12 views
19

Leer, escribir y serializar fechas y horas mientras se mantiene la zona horaria constante se está volviendo molesto. Estoy usando Ruby (y Rails 3.0) y estoy tratando de modificar la zona horaria de un DateTime. (a UTC) pero no el tiempo en sí.¿Cómo modifico la zona horaria de un DateTime en Ruby?

quiero esto:

t = DateTime.now 
t.hour 
-> 4 
t.offset = 0 
t.hour 
-> 4 
t.utc? 
-> true 

Lo más cerca que he llegado es esto, pero no es intuitiva.

t = DateTime.now 
t.hour 
-> 4 
t += t.offset 
t = t.utc 
t.hour 
-> 4 
t.utc? 
-> true 

¿Hay alguna otra manera mejor?

+2

Si en realidad está usando 'ahora' como una marca de tiempo, entonces almacenar eso en la base de datos como una versión marcada UTC de la misma hora es una mentira, ¿correcto? Personalmente experimento menos dolores de cabeza cuando todas las partes del sistema hablan UTC, y solo en las vistas puedo ajustarme para mostrar en la zona horaria del servidor o navegador. – Phrogz

+0

Es difícil porque estoy leyendo fechas de archivos CSV donde la zona horaria es desconocida o irrelevante, así que las almaceno como UTC en la base de datos. Pero cuando se consulta con fechas de la zona local (es decir, mayor que '2010/01/01 8:00 pm'), primero se convierte esa hora en UTC (el origen de mi problema) antes de usarlo en la consulta. Intento que toda la aplicación sea independiente de la zona horaria para que un usuario pueda cargar un archivo CSV con tiempos en el rango de 7:00 a 8:00, luego consultar de 7:00 a 8:00 y hacer que vuelva el mismo conjunto de registros. En cambio, Ruby supone que un tiempo analizado se encuentra en la zona local. –

Respuesta

35

El uso de Rails 3, que está buscando DateTime.change()

dt = DateTime.now 
=> Mon, 20 Dec 2010 18:59:43 +0100 
dt = dt.change(:offset => "+0000") 
=> Mon, 20 Dec 2010 18:59:43 +0000 
+2

Por lo que vale, .change parece estar disponible también en 2.3. – joshaidan

+7

Más útil sería vincular realmente a la respuesta. Creo que es este: http://stackoverflow.com/questions/5941613/are-the-date-time-and-datetime-classes-necessary/14007547#14007547 – micapam

+0

o simplemente DateTime.now.utc – courtsimas

7

Yo usaría un objeto Time en su lugar. Así que la hora local actual y luego se incrementará por el desplazamiento UTC y convertir a UTC, así:

t = Time.now # or Time.parse(myDateTime.asctime) 
t # => Thu Dec 16 21:07:48 -0800 2010 
(t + t.utc_offset).utC# => Thu Dec 16 21:07:48 UTC 2010 

Aunque, por comentario Phrogz, si sólo desea almacenar las marcas de tiempo de una manera independiente de la posición, luego sólo tiene que utilizar el hora UTC actual:

Time.now.utc 
+0

Eso es un gran golpe de rendimiento. Esto parece más lento y más complejo que la solución que ya tengo arriba. –

+0

Si realmente no te importan los objetos DateTime específicamente (solo el instante actual en el tiempo), salta el análisis y usa 'Time.now'. He editado mi respuesta un poco. – maerics

+0

Esto es exactamente lo mismo que la solución que tengo arriba, pero con un paso adicional de análisis de cadenas. –

8
d = DateTime.now 
puts [ d, d.zone ] 
#=> 2010-12-17T13:28:29-07:00 
#=> -07:00 

d2 = d.new_offset(3.0/24) 
puts d2, d2.zone 
#=> 2010-12-17T23:28:29+03:00 
#=> +03:00 

Editar: Esta respuesta no tiene en cuenta la información proporcionada por el comentario de otra respuesta que el deseo es tener las horas '' informó de la misma después de la hora cambio de zona.

+0

'new_offset' suena útil, y los documentos hacen que parezca que es lo que busco, pero simplemente se convierte de un desplazamiento a otro. Como dice la pregunta, estoy buscando cambiar la compensación, mientras dejo el tiempo solo. –

+1

Para mí, esta 'DateTime.now.new_offset (-8.0/24)' era lo único en esta página que funcionaba para mis propósitos: colocar una transacción bancaria aprox. desde la hora del Pacífico, DST insignificante. Probablemente ya que mi script simple no incluye frameworks mencionados ni Active anything. – Marcos

9

Usando un desplazamiento, por ejemplo, el número '1000 'no funcionará durante todo el año debido a los ahorros de luz diurna. Checkout ActiveSupport :: TimeZone. More info here

+2

Proporcionar un ejemplo de uso de 'ActiveSupport :: TimeZone' en lugar de simplemente vincular a los documentos es preferible. –

3

Como @Sam señalado, cambiar el desplazamiento no es suficiente y podría dar lugar a errores. Con el fin de ser resistente a los avances de reloj horario de verano, la conversión debe hacerse en un siguiente manera:

d = datetime_to_alter_time_zone 
time_zone = 'Alaska' 

DateTime.new 
    .in_time_zone(time_zone) 
    .change(year: d.year, month: d.month, day: d.day, hour: d.hour, min: d.min, sec: d.sec) 
1

Si alguien (como yo) tiene zona horaria en formato de cadena como "Hora del Pacífico (EE.UU. & Canadá)"

que esta es la mejor manera que he encontrado:

datetime.change(ActiveSupport::TimeZone[time_zone_string].formatted_offset(false) 
-1

puede especificar la zona horaria que desea utilizar en su aplicación mediante la adición de la siguiente línea en el fichero de configuración (config/application.rb) : config.time_zone = 'Bombay'

Puede encontrar la documentación oficial de la misma aquí: http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html

La otra opción es que "Mono Patch" la clase 'DateTime'.

Cuestiones relacionadas