Resta n horas de un DateTime en Ruby
-
04-07-2019 - |
Pregunta
Tengo un Ruby DateTime que se completa desde un formulario.Además, también tengo n horas del formulario.Me gustaría restar esas n horas del DateTime anterior.(Para obtener un rango de tiempo).
DateTime tiene dos métodos "-" y "<<" para restar día y mes, pero no hora.(API).¿Alguna sugerencia de cómo puedo hacer eso?
Solución
Podrías hacer esto.
adjusted_datetime = (datetime_from_form.to_time - n.hours).to_datetime
Otros consejos
Puede restar menos de un día entero:
two_hours_ago = DateTime.now - (2/24.0)
Esto funciona durante 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 las imprecisiones aritméticas de coma flotante son un problema, puede usar Rational
o alguna otra utilidad aritmética segura.
El método de avance es bueno si quieres ser más explícito sobre comportamientos como este.
adjusted = time_from_form.advance(:hours => -n)
Solo necesitas quitar fracciones de un día.
two_hours_ago = DateTime.now - (2.0/24)
- 1.0 = un día
- 1.0 / 24 = 1 hora
- 1.0 / (24 * 60) = 1 minuto
- 1.0 / (24 * 60 * 60) = 1 segundo
n/24.0
truco no funcionará correctamente ya que los flotadores se redondean eventualmente:
>> 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
Sin embargo, puede usar la clase Rational 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
No dijiste qué uso necesitas hacer del valor que obtienes, pero ¿qué hay de dividir tus horas entre 24 para restar una fracción de día?
mydatetime = DateTime.parse(formvalue)
nhoursbefore = mydatetime - n / 24.0
EDITAR:Echa un vistazo a esta pregunta antes de decidir utilizar el enfoque que he descrito aquí.Parece que puede que no sea una buena práctica modificar el comportamiento de una clase base en Ruby (lo cual puedo entender).Así que toma esta respuesta con cautela...
La respuesta de MattW Fue lo primero que pensé, pero tampoco me gustó mucho.
Supongo que podrías hacerlo menos feo parcheando DateTime
y Fixnum
para hacer lo que quieras:
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 puedes escribir tu código así:
some_date = some_date - n.hours
dónde n
es el número de horas que deseas restar some_date
DateTime
No puedo hacer esto, pero Time
poder:
t = Time.now
t = t-hours*60
Tenga en cuenta que Time
También almacena información de fechas, es todo un poco extraño.
Si tienes 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, así que ni siquiera estoy seguro de -
y <<
Si se me permite usar Time
en lugar de DateTime
(Hay varias formas de traducir uno a otro ):
# Just remove the number of seconds from the Time object
Time.now - (6 * 60 * 60) # 6 hours ago
Me gusta usar los ayudantes en active_support. Lo hace realmente limpio y fácil de leer.
Vea el ejemplo a continuación:
require 'active_support'
last_accessed = 2.hours.ago
last_accessed = 2.weeks.ago
last_accessed = 1.days.ago
Puede haber una forma de usar ese tipo de sintaxis para hacer lo que está buscando, si se usa la fecha actual.
Puedes usar esto:
Time.now.ago(n*60*60)
Por ejemplo, Time.now.ago(7200)
proporcionará la fecha y la hora antes de 2 horas a partir de ahora.