Вычтите n часов из DateTime в Ruby - PullRequest
41 голосов
/ 27 октября 2008

У меня есть Ruby DateTime, который заполняется из формы. Кроме того, у меня есть n часов от формы. Я хотел бы вычесть эти n часов из предыдущего DateTime. (Чтобы получить диапазон времени).

DateTime имеет два метода «-» и «<<» для вычитания дня и месяца, но не часа. (<a href="https://ruby-doc.org/stdlib-2.5.1/libdoc/date/rdoc/DateTime.html" rel="nofollow noreferrer"> API ). Любые предложения, как я могу это сделать?

Ответы [ 11 ]

52 голосов
/ 27 октября 2008

Вы могли бы сделать это.

adjusted_datetime = (datetime_from_form.to_time - n.hours).to_datetime
33 голосов
/ 05 декабря 2008

Вы можете просто вычесть менее одного дня:

two_hours_ago = DateTime.now - (2/24.0)

Это работает в течение нескольких минут и всего остального:

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

Если арифметические неточности с плавающей запятой являются проблемой, вы можете использовать Rational или другую безопасную арифметическую утилиту.

20 голосов
/ 16 мая 2011

Продвинутый метод хорош, если вы хотите быть более откровенным в отношении поведения, подобного этому.

adjusted = time_from_form.advance(:hours => -n)
15 голосов
/ 16 июля 2015

Тебе просто нужно снимать доли дня.

two_hours_ago = DateTime.now - (2.0/24)
  • 1,0 = один день
  • 1,0 / 24 = 1 час
  • 1,0 / (24 * 60) = 1 минута
  • 1,0 / (24 * 60 * 60) = 1 секунда
9 голосов
/ 14 августа 2009

n/24.0 трюк не будет работать должным образом, поскольку числа с плавающей точкой в ​​конечном итоге будут округлены:

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

Однако вместо этого вы можете использовать класс Rational:

>> 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
4 голосов
/ 27 октября 2008

Вы не сказали, как вам нужно использовать ценность, которую вы получаете, но как насчет простого деления ваших часов на 24, чтобы вы вычитали часть дня?

mydatetime = DateTime.parse(formvalue)
nhoursbefore = mydatetime - n / 24.0
4 голосов
/ 27 октября 2008

РЕДАКТИРОВАТЬ : взгляните на этот вопрос , прежде чем вы решите использовать подход, который я изложил здесь. Похоже, что не рекомендуется изменять поведение базового класса в Ruby (что я могу понять). Итак, примите этот ответ с крошкой соли ...


Ответ MattW был первым, о чем я подумал, но мне он тоже не очень понравился.

Полагаю, вы могли бы сделать это менее уродливо, добавив DateTime и Fixnum, чтобы сделать то, что вы хотите:

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

Тогда вы можете написать свой код так:

some_date = some_date - n.hours

где n - количество часов, которое вы хотите вычесть из some_date

4 голосов
/ 27 октября 2008

DateTime не может этого сделать, но Time может:

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

Обратите внимание, что Time также хранит информацию о дате, все это немного странно.

Если вам нужно работать с DateTime

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

может работать, но безобразно. Док говорит, что DateTime является неизменным, поэтому я даже не уверен насчет - и <<

3 голосов
/ 14 августа 2014

Если мне разрешено использовать Time вместо DateTime (есть несколько способов перевести один на другой ):

# Just remove the number of seconds from the Time object
Time.now - (6 * 60 * 60) # 6 hours ago
2 голосов
/ 31 октября 2008

Мне нравится использовать помощников в active_support. Это делает его действительно чистым и легким для чтения.

См. Пример ниже:

require 'active_support'

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

Возможно, можно использовать такой синтаксис для выполнения того, что вы ищете, если используется текущая дата.

...