2008-10-26 6 views
39

Ich habe eine Ruby DateTime, die aus einem Formular gefüllt wird. Zusätzlich habe ich noch Stunden vom Formular entfernt. Ich möchte diese n Stunden von der vorherigen DateTime subtrahieren. (Um einen Zeitbereich zu erhalten).N Stunden von einer DateTime in Ruby subtrahieren

DateTime hat zwei Methoden "-" und "< <", um Tag und Monat, aber nicht Stunde zu subtrahieren. (API). Irgendwelche Vorschläge, wie ich das machen kann?

Antwort

46

Sie könnten dies tun.

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

Beachten Sie, dass dies nur in einem Rails-Kontext funktioniert. Die hours() -Methode kommt von einem der date-Helfer in Rails, nicht von der Ruby-Standard-lib. –

+43

Es ist frustrierend, wie so viele Leute denken, Ruby = Rails –

3

DateTime kann dies nicht tun, aber Time kann:

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

Beachten Sie, dass Time auch speichert Informationen stammen, es ist alles ein wenig seltsam.

Wenn Sie mit DateTime

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

arbeiten könnte funktionieren, ist aber hässlich. Der Doc sagt DateTime unveränderlich ist, so dass ich bin nicht einmal sicher über - und <<

+0

Ja, "DateTime-Objekte sind unveränderlich, sobald sie erstellt wurden." - Es ist wie die String-Klasse in Java. Trotzdem: ** method "-": ** "Wenn x ein numerischer Wert ist, erzeuge ein neues Date-Objekt, das x Tage früher ist als das aktuelle.", Damit könnten wir arbeiten. Ich habe auch über Ihre Idee nachgedacht, aber ich bin mir nicht sicher, wie Rubin einen negativen Wert von "Stunde-x" umrechnet. Ist es das, was es für dich hässlich macht? Nun, ich probiere es aus. Mal sehen was die DateTime Klasse macht;) –

3

EDIT: einen Blick auf this question nehmen, bevor Sie sich entscheiden, den Ansatz zu verwenden, die ich hier skizziert habe. Es scheint, dass es nicht die beste Vorgehensweise ist, das Verhalten einer Basisklasse in Ruby zu ändern (was ich verstehen kann). Also, nehmen Sie diese Antwort mit einem Körnchen Salz ...


MattW's answer das erste, was war ich dachte, aber ich habe auch mag es nicht sehr viel.

Ich nehme an, Sie es weniger hässlich machen könnte durch DateTime und Fixnum Patchen zu tun, was Sie wollen:

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 

Dann können Sie Ihren Code wie folgt schreiben:

some_date = some_date - n.hours 

wo n das ist Anzahl der Stunden, die Sie abziehen möchten some_date

+0

Dies ist eine komplette Lösung. Ich weiß nicht, ob die Frage tatsächlich danach gefragt wurde oder ob er nicht entdeckt hatte, dass der neue Operator ist. "Negative Werte von h, min und sec werden vom Ende der nächstgrößeren Einheit rückwärts gezählt." – Jonke

3

Sie haben nicht gesagt, was Sie brauchen, um von der VA zu machen Kommst du, aber wie wäre es, wenn du deine Stunden mit 24 teilst, also subtrahierst du nur einen Bruchteil eines Tages?

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

Ich benutze gerne die Helfer in active_support. Es macht es wirklich sauber und leicht zu lesen.

siehe Beispiel unten:

require 'active_support' 

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

Es könnte ein Weg sein, diese Art von Syntax zu verwenden, zu tun, was Sie suchen, wenn das aktuelle Datum verwendet wird.

26

Sie können nur subtrahieren weniger als einen ganzen Tag:

two_hours_ago = DateTime.now - (2/24.0) 

Das sonst auch für Minuten und alles funktioniert:

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)> 

Wenn Gleitpunktarithmetik Ungenauigkeiten ein Problem sind, können Sie Rational verwenden oder ein anderes sicheres arithmetisches Dienstprogramm.

+3

Vorsicht, dies führt zu unerwarteten Ergebnissen wegen arithmetischer Ungenauigkeiten Gleitkomma ... – JtR

+2

In diesem Fall Rational-Klasse ist eine Lösung ^^ – Smar

10

n/24.0 Trick funktioniert nicht richtig, als Schwimmer schließlich abgerundet sind:

>> 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 

Sie können jedoch Rational-Klasse verwenden statt:

>> 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

Auch nicht Kern Ruby. – dfrankow

+1

Ich meinte: Ich glaube, "Schritt" ist nicht Kern Ruby. Sie können jedoch die Rational-Strategie mit direkter Subtraktion wie in einigen anderen Antworten verwenden. – dfrankow

+3

1) 'step' war da nur um den Punkt zu verdeutlichen 2)' step' ist im Standard lib: http://rubydoc.info/stdlib/date/1.9.3/Date:step –

20

Der Fortschritt Methode ist schön, wenn Sie wollen expliziter sein über Verhalten wie dieses.

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

Für jeden, der dies versucht: Es ist nicht in der Stdlib, es wird von ActiveSupport zur Verfügung gestellt. Siehe hier: http://stackoverflow.com/questions/617284/where-is-time-advance-documented. – jkp

+2

Danke jkp. Ich war frustriert über all diese Beispiele, die nicht in einfacher Sprache funktionierten. – swilliams

-1

Sie können diese verwenden:

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

Zum Beispiel Time.now.ago(7200) wird das Datum und die Zeit geben, die von jetzt vor 2 Stunden war.

+6

Diese Frage betrifft Ruby, nicht Ruby on Rails. – Mischa

10

Sie müssen nur Bruchteile eines Tages ausziehen.

two_hours_ago = DateTime.now - (2.0/24) 
  • 1,0 = 1 Tag
  • 1,0/24 = 1 Stunde
  • 1,0/(24 * 60) = 1 minute
  • 1,0/(24 * 60 * 60) = 1 zweite