2009-02-01 6 views
30

¿Hay alguna forma de iterar en un rango de tiempo en Ruby y establecer el delta?Iterar sobre el objeto Ruby Time con delta

Aquí es una idea de lo que me gustaría hacer:

for hour in (start_time..end_time, hour) 
    hour #=> Time object set to hour 
end 

Puede iterar sobre los objetos de tiempo, pero vuelve cada segundo entre los dos. Lo que realmente necesito es una forma de establecer el offset o delta (como minuto, hora, etc.)

¿Está esto incorporado a Ruby, o hay un plugin decente disponible?

Respuesta

55

Antes de 1,9, podría utilizar Range#step:

(start_time..end_time).step(3600) do |hour| 
    # ... 
end 

Sin embargo, esta estrategia es bastante lento, ya que llamaría Time#succ 3600 veces. En su lugar, como ha señalado dolzenko en his answer, una solución más eficaz es utilizar un bucle simple:

hour = start_time 
while hour < end_time 
    # ... 
    hour += 3600 
end 

Si está utilizando rieles se puede reemplazar con 1.hour 3600, que es significativamente más fácil de leer.

+4

Range # step no funciona para objetos Time en Ruby 1.9.2p180 y 1.9.3. Dice: TypeError: no puede iterar desde Time. – prekageo

+0

@prekageo Gracias, actualizado. –

+1

Es bastante fácil extender el método 'step' de' Range' para los pasos 'Time'. He escrito una respuesta para los intervalos dinámicos de paso como '2.months + 33.seconds' (_si_ estás usando' ActiveSupport'). Ver http://stackoverflow.com/questions/19093487/ruby-create-range-of-dates/19094504#19094504. Internamente, el método proxy utiliza este enfoque while-loop. – captainpete

36

Si su start_time y end_time son en realidad casos de Time clase, entonces la solución con el uso de la Range#step sería extremadamente ineficiente ya que sería iterar sobre cada segundo en este rango con Time#succ. Si convierte sus tiempos de números enteros de la simple suma será usada pero de esta manera va a terminar con algo como:

(start_time.to_i..end_time.to_i).step(3600) do |hour| 
    hour = Time.at(hour)  
    # ... 
end 

Pero esto también se puede hacer con simple y más eficiente (es decir, sin todas las conversiones de tipos) bucle:

hour = start_time 
begin 
    # ...  
end while (hour += 3600) < end_time 
+5

Solución de mucho mejor rendimiento que la respuesta aceptada. – dylanfm

+1

@dylanfm De hecho. Dado que no parece que el OP vaya a cambiar la respuesta aceptada a este, he actualizado mi respuesta para incluir esto. –

16

El método de rango de paso es muy lento en este caso. Use begin..end while, como dolzenko publicado aquí.

puede definir un nuevo método:

def time_iterate(start_time, end_time, step, &block) 
    begin 
     yield(start_time) 
    end while (start_time += step) <= end_time 
    end 

entonces,

start_time = Time.parse("2010/1/1") 
end_time = Time.parse("2010/1/31") 
time_iterate(start_time, end_time, 1.hour) do |t| 
    puts t 
end 

si en los carriles.