web-dev-qa-db-fra.com

Soustrayez n heures d'un DateTime en Ruby

J'ai un Ruby DateTime qui est rempli à partir d'un formulaire. De plus, j'ai aussi n heures du formulaire. Je voudrais soustraire ces n heures du DateTime précédent. (Pour obtenir un intervalle de temps).

DateTime a deux méthodes "-" et "<<" pour soustraire le jour et le mois, mais pas l'heure. ( API ). Des suggestions sur la façon dont je peux faire cela?

Tu pourrais faire ça.

adjusted_datetime = (datetime_from_form.to_time - n.hours).to_datetime
54
Daniel Beardsley

Vous pouvez simplement soustraire moins d'une journée entière:

two_hours_ago = DateTime.now - (2/24.0)

Cela fonctionne pendant quelques minutes et tout le reste:

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 inexactitudes arithmétiques en virgule flottante sont un problème, vous pouvez utiliser Rational ou un autre utilitaire arithmétique sûr.

34
Somebee

La méthode avancée est Nice si vous voulez être plus explicite sur un comportement comme celui-ci.

adjusted = time_from_form.advance(:hours => -n)
20
Joe Kelley

Vous avez juste besoin d'enlever des fractions de journée.

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
16
Nigel Thorne

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 cependant 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
9
Mladen Jablanović

Si vous travaillez dans Rails, la possibilité super-intuitive suivante existe:

> Time.now - 12.hours
=> 2019-08-19 05:50:43 +0200

(Cela fonctionne également avec secondes, minutes, jours et années)

si vous utilisez uniquement Ruby, DateTime ne peut pas le faire, mais Time peut:

t = Time.now
t = t - (hours*60**2)

Notez que Time stocke également les informations de date, c'est un peu étrange.

Si vous devez travailler avec DateTime

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

pourrait fonctionner, mais est moche. Le doc dit que DateTime est immuable, donc je ne suis même pas sûr de - et <<

4
MattW.

[~ # ~] modifier [~ # ~] : Jetez un œil à cette question avant de décider d'utiliser l'approche J'ai décrit ici. Il semble qu'il ne soit pas recommandé de modifier le comportement d'une classe de base dans Ruby (que je peux comprendre). Alors, prenez cette réponse avec un grain de sel ...


réponse de MattW a été la première chose à laquelle j'ai pensé, mais je n'ai pas beaucoup aimé non plus.

Je suppose que vous pourriez le rendre moins laid en corrigeant 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 ceci:

some_date = some_date - n.hours

n est le nombre d'heures dont vous souhaitez soustraire some_date

4
Mike Spross

Vous n'avez pas dit quelle utilisation vous devez faire de la valeur que vous obtenez, mais qu'en est-il simplement de diviser vos heures par 24 afin de soustraire une fraction de journée?

mydatetime = DateTime.parse(formvalue)
nhoursbefore = mydatetime - n / 24.0
4
glenn mcdonald

Si je suis autorisé à utiliser Time au lieu de DateTime (Il existe plusieurs façons de se traduire les unes par les autres ):

# Just remove the number of seconds from the Time object
Time.now - (6 * 60 * 60) # 6 hours ago
3
fguillen

J'aime utiliser les assistants dans 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 cherchez, si la date actuelle est utilisée.

2
danmayer