可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a Ruby DateTime which gets filled from a form. Additionally I have n hours from the form as well. I'd like to subtract those n hours from the previous DateTime. (To get a time range).
DateTime has two methods "-" and "<<" to subtract day and month, but not hour. (API). Any suggestions how I can do that?
回答1:
You could do this.
adjusted_datetime = (datetime_from_form.to_time - n.hours).to_datetime
回答2:
You can just subtract less than one whole day:
two_hours_ago = DateTime.now - (2/24.0)
This works for minutes and anything else too:
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)>
If floating point arithmetic inaccuracies are a problem, you can use Rational
or some other safe arithmetic utility.
回答3:
The advance method is nice if you want to be more explicit about behavior like this.
adjusted = time_from_form.advance(:hours => -n)
回答4:
You just need to take off fractions of a day.
two_hours_ago = DateTime.now - (2.0/24)
- 1.0 = one day
- 1.0/24 = 1 hour
- 1.0/(24*60) = 1 minute
- 1.0/(24*60*60) = 1 second
回答5:
n/24.0
trick won't work properly as floats are eventually rounded:
>> 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
You can, however, use Rational class instead:
>> 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
回答6:
EDIT: Take a look at this question before you decide to use the approach I've outlined here. It seems it may not be best practice to modify the behavior of a base class in Ruby (which I can understand). So, take this answer with a grain of salt...
MattW's answer was the first thing I thought of, but I also didn't like it very much.
I suppose you could make it less ugly by patching DateTime
and Fixnum
to do what you want:
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
Then you can write your code like this:
some_date = some_date - n.hours
where n
is the number of hours you want to substract from some_date
回答7:
DateTime
can't do this, but Time
can:
t = Time.now
t = t-hours*60
Note that Time
also stores date information, it's all a little strange.
If you have to work with DateTime
DateTime.commercial(date.year,date.month,date.day,date.hour-x,date.minute,date.second)
might work, but is ugly. The doc says DateTime
is immutable, so I'm not even sure about -
and <<
回答8:
You didn't say what use you need to make of the value you get, but what about just dividing your hours by 24 so you're subtracting a fraction of a day?
mydatetime = DateTime.parse(formvalue)
nhoursbefore = mydatetime - n / 24.0
回答9:
If I'm allowed to use Time
instead of DateTime
(There are several ways to translate one to another):
# Just remove the number of seconds from the Time object
Time.now - (6 * 60 * 60) # 6 hours ago
回答10:
I like using the helpers in active_support. It makes it really clean and easy to read.
See the example below:
require 'active_support'
last_accessed = 2.hours.ago
last_accessed = 2.weeks.ago
last_accessed = 1.days.ago
There might be a way to use that kind of syntax to do what you are looking for, if the current date is used.
回答11:
You can use this :
Time.now.ago(n*60*60)
For example Time.now.ago(7200)
will give the date and time that was before 2 hours from now.