Soustraire n heures à un DateTime en Ruby
-
04-07-2019 - |
Question
J'ai un DateTime Ruby qui est rempli à partir d'un formulaire. De plus, j'ai n heures à partir du formulaire. Je voudrais soustraire ces n heures du DateTime précédent. (Pour obtenir une plage de temps).
DateTime a deux méthodes " - " et " < < " pour soustraire le jour et le mois, mais pas l'heure. ( API ). Avez-vous des suggestions à faire?
La solution
Vous pouvez le faire.
adjusted_datetime = (datetime_from_form.to_time - n.hours).to_datetime
Autres conseils
Vous pouvez simplement soustraire moins d'une journée entière:
two_hours_ago = DateTime.now - (2/24.0)
Ceci fonctionne pendant des minutes et toute autre chose aussi:
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 les imprécisions arithmétiques en virgule flottante posent problème, vous pouvez utiliser Rational
ou un autre utilitaire arithmétique sécurisé.
La méthode d'avance est intéressante si vous souhaitez être plus explicite à propos d'un comportement comme celui-ci.
adjusted = time_from_form.advance(:hours => -n)
Il vous suffit d’enlever des fractions de jour.
two_hours_ago = DateTime.now - (2.0/24)
- 1,0 = un jour
- 1.0 / 24 = 1 heure
- 1.0 / (24 * 60) = 1 minute
- 1.0 / (24 * 60 * 60) = 1 seconde
n/24.0
L’astuce ne fonctionnera pas correctement car les flottants sont finalement arrondis:
>> 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
Vous pouvez toutefois utiliser la classe Rational à la place:
>> 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
Vous n’avez pas précisé l’utilisation que vous devez faire de la valeur que vous obtenez, mais qu’en est-il de diviser vos heures par 24 afin de soustraire une fraction de jour?
mydatetime = DateTime.parse(formvalue)
nhoursbefore = mydatetime - n / 24.0
MODIFIER : consultez cette question avant de décider d'utiliser l'approche que j'ai décrite ici. Il semble que la modification du comportement d’une classe de base en Ruby ne soit pas recommandée (ce que je peux comprendre). Alors, prenez cette réponse avec un grain de sel ...
La réponse de MattW a été la première chose que j'ai pensé, mais je ne l’aimais pas beaucoup non plus.
Je suppose que vous pourriez le rendre moins laid en appliquant des correctifs à DateTime
et Fixnum
pour faire ce que vous voulez:
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
Ensuite, vous pouvez écrire votre code comme suit:
some_date = some_date - n.hours
où n
est le nombre d'heures que vous souhaitez soustraire some_date
DateTime
ne peut pas faire cela, mais Time
peut:
t = Time.now
t = t-hours*60
Notez que -
stocke également les informations relatives à la date, c'est un peu étrange.
Si vous devez travailler avec <<
DateTime.commercial(date.year,date.month,date.day,date.hour-x,date.minute,date.second)
pourrait fonctionner, mais est moche. La doc dit que <=> est immuable, je ne suis même pas sûr de <=> et de <=>
Si je suis autorisé à utiliser Time
au lieu de DateTime
( Plusieurs façons de traduire l'un vers l'autre ):
# Just remove the number of seconds from the Time object
Time.now - (6 * 60 * 60) # 6 hours ago
J'aime utiliser les helpers de active_support. Cela le rend vraiment propre et facile à lire.
Voir l'exemple ci-dessous:
require 'active_support'
last_accessed = 2.hours.ago
last_accessed = 2.weeks.ago
last_accessed = 1.days.ago
Il pourrait y avoir un moyen d'utiliser ce type de syntaxe pour faire ce que vous recherchez, si la date actuelle est utilisée.
Vous pouvez utiliser ceci:
Time.now.ago(n*60*60)
Par exemple, Time.now.ago(7200)
donnera la date et l'heure qui étaient antérieures à 2 heures.