2008-10-26 62 views
39

Tengo un Ruby DateTime que se llena desde un formulario. Además, tengo n horas del formulario también. Me gustaría restar esas n horas del DateTime anterior. (Para obtener un rango de tiempo).Restar n horas de DateTime en Ruby

DateTime tiene dos métodos "-" y "< <" para restar el día y el mes, pero no la hora. (API). ¿Alguna sugerencia de cómo puedo hacer eso?

Respuesta

46

Puede hacer esto.

adjusted_datetime = (datetime_from_form.to_time - n.hours).to_datetime 
+42

Tenga en cuenta que esto solo funciona en el contexto de Rails. El método hours() proviene de uno de los ayudantes de fecha en Rails que no proviene de la lib estándar de Ruby. –

+43

Es frustrante cómo tanta gente piensa Rubí = rieles –

3

DateTime no puede hacer esto, pero puedo Time:

t = Time.now 
t = t-hours*60 

Tenga en cuenta que Time también almacena información actualizada, es todo un poco extraño.

Si tiene que trabajar con DateTime

DateTime.commercial(date.year,date.month,date.day,date.hour-x,date.minute,date.second) 

podría funcionar, pero es feo. El doctor dice DateTime es inmutable, por lo que ni siquiera estoy seguro de - y <<

+0

Sí, "los objetos DateTime son inmutables una vez creados". - Es como la clase String en Java. Sin embargo: ** método "-": ** "Si x es un valor numérico, crea un nuevo objeto Date x días antes que el actual.", Así podríamos trabajar con eso. Pensé en tu idea también, pero no estoy seguro de cómo el rubí convierte un valor negativo de "hora-x". ¿Eso es lo que lo hace feo para ti? Bueno, lo intento. Veamos qué hace la clase DateTime;) –

3

EDITAR: Tome un vistazo a this question antes de decidirse a utilizar el enfoque que he descrito aquí. Parece que no es una buena práctica modificar el comportamiento de una clase base en Ruby (que puedo entender). Por lo tanto, aprovechar esta respuesta con un grano de sal ...


MattW's answer fue lo primero que pensé, pero tampoco me gusta mucho.

supongo que podría hacerlo menos fea mediante parches DateTime y Fixnum a hacer lo que quiera:

require 'date' 

# A placeholder class for holding a set number of hours. 
# Used so we can know when to change the behavior 
# of DateTime#-() by recognizing when hours are explicitly passed in. 

class Hours 
    attr_reader :value 

    def initialize(value) 
     @value = value 
    end 
end 

# Patch the #-() method to handle subtracting hours 
# in addition to what it normally does 

class DateTime 

    alias old_subtract - 

    def -(x) 
     case x 
     when Hours; return DateTime.new(year, month, day, hour-x.value, min, sec) 
     else;  return self.old_subtract(x) 
     end 
    end 

end 

# Add an #hours attribute to Fixnum that returns an Hours object. 
# This is for syntactic sugar, allowing you to write "someDate - 4.hours" for example 

class Fixnum 
    def hours 
     Hours.new(self) 
    end 
end 

Entonces se puede escribir el código como el siguiente:

some_date = some_date - n.hours 

donde n es el número de horas que desea restar de some_date

+0

Esta es una solución completa. No sé si la pregunta realmente se hizo al respecto o si no descubrió que el nuevo operador es "Los valores negativos de h, min y sec se consideran contados hacia atrás desde el final de la siguiente unidad más grande" – Jonke

3

Usted no dijo qué uso necesita hacer de la va Si lo consigue, ¿qué pasa si dividimos sus horas entre 24 para que resta una fracción de un día?

mydatetime = DateTime.parse(formvalue) 
nhoursbefore = mydatetime - n/24.0 
2

Me gusta usar los ayudantes en active_support. Lo hace realmente limpio y fácil de leer.

Véase el siguiente ejemplo:

require 'active_support' 

last_accessed = 2.hours.ago 
last_accessed = 2.weeks.ago 
last_accessed = 1.days.ago 

Puede haber una manera de utilizar ese tipo de sintaxis para hacer lo que está buscando, si se utiliza la fecha actual.

26

Usted sólo puede restar todo menos de un día:

two_hours_ago = DateTime.now - (2/24.0) 

Esto funciona para los minutos y cualquier otra cosa también:

hours = 10 
minutes = 5 
seconds = 64 

hours = DateTime.now - (hours/24.0) #<DateTime: 2015-03-11T07:27:17+02:00 ((2457093j,19637s,608393383n),+7200s,2299161j)> 
minutes = DateTime.now - (minutes/1440.0) #<DateTime: 2015-03-11T17:22:17+02:00 ((2457093j,55337s,614303598n),+7200s,2299161j)> 
seconds = DateTime.now - (seconds/86400.0) #<DateTime: 2015-03-11T17:26:14+02:00 ((2457093j,55574s,785701811n),+7200s,2299161j)> 

Si coma flotante inexactitudes aritméticas son un problema, puede utilizar Rational o alguna otra utilidad aritmética segura.

+3

Cuidado, esto lleva a resultados inesperados a causa de las inexactitudes de punto flotante aritméticas ... – JtR

+2

En esa clase Racional caso es una solución ^^ – Smar

10

n/24.0 truco no funcionará correctamente como flotadores son eventualmente redondeados:

>> DateTime.parse('2009-06-04 02:00:00').step(DateTime.parse('2009-06-04 05:00:00'),1.0/24){|d| puts d} 
2009-06-04T02:00:00+00:00 
2009-06-04T03:00:00+00:00 
2009-06-04T03:59:59+00:00 
2009-06-04T04:59:59+00:00 

Puede, sin embargo, el uso racional de clase en su lugar:

>> DateTime.parse('2009-06-04 02:00:00').step(DateTime.parse('2009-06-04 05:00:00'),Rational(1,24)){|d| puts d} 
2009-06-04T02:00:00+00:00 
2009-06-04T03:00:00+00:00 
2009-06-04T04:00:00+00:00 
2009-06-04T05:00:00+00:00 
+5

Además, no es núcleo de Ruby. – dfrankow

+1

Quise decir: creo que "paso" no es el núcleo de Ruby. Sin embargo, puedes usar la estrategia racional con resta directa como en otras respuestas. – dfrankow

+3

1) 'step' estaba allí para ilustrar el punto 2)' step' está en lib estándar: http://rubydoc.info/stdlib/date/1.9.3/Date:step –

20

El método de avance es bueno si quieres ser más explícito acerca de un comportamiento como este.

adjusted = time_from_form.advance(:hours => -n) 
+21

Para cualquiera que intente esto: no está en stdlib, es provisto por ActiveSupport. Vea aquí: http://stackoverflow.com/questions/617284/where-is-time-advance-documented. – jkp

+2

Gracias jkp. Estaba frustrado con todos estos ejemplos que no funcionaban en el simple ole irb. – swilliams

-1

Puede utilizar esta:

Time.now.ago(n*60*60) 

Por ejemplo Time.now.ago(7200) dará la fecha y la hora en que estaba antes de 2 horas a partir de ahora.

+6

Esta pregunta es sobre Ruby, no sobre Ruby on Rails. – Mischa

10

Sólo tiene que despegar fracciones de un día.

two_hours_ago = DateTime.now - (2.0/24) 
  • 1,0 = un días
  • 1,0/24 = 1 hora
  • 1,0/(24 * 60) = 1 minuto
  • 1,0/(24 * 60 * 60) = 1 segundo
Cuestiones relacionadas