class ActiveSupport::Duration
Provides accurate date and time measurements using Date#advance
and Time#advance
, respectively. It mainly supports the methods on Numeric
.
1.month.ago # equivalent to Time.now.advance(months: -1)
Constants
- PARTS
- PARTS_IN_SECONDS
- SECONDS_PER_DAY
- SECONDS_PER_HOUR
- SECONDS_PER_MINUTE
- SECONDS_PER_MONTH
- SECONDS_PER_WEEK
- SECONDS_PER_YEAR
Attributes
Public Class Methods
Creates a new Duration
from a seconds value that is converted to the individual parts:
ActiveSupport::Duration.build(31556952).parts # => {:years=>1} ActiveSupport::Duration.build(2716146).parts # => {:months=>1, :days=>1}
# File activesupport/lib/active_support/duration.rb, line 184 def build(value) parts = {} remainder = value.to_f PARTS.each do |part| unless part == :seconds part_in_seconds = PARTS_IN_SECONDS[part] parts[part] = remainder.div(part_in_seconds) remainder = (remainder % part_in_seconds).round(9) end end parts[:seconds] = remainder parts.reject! { |k, v| v.zero? } new(value, parts) end
Creates a new Duration
from string formatted according to ISO 8601 Duration
.
See ISO 8601 for more information. This method allows negative parts to be present in pattern. If invalid string is provided, it will raise ActiveSupport::Duration::ISO8601Parser::ParsingError
.
# File activesupport/lib/active_support/duration.rb, line 139 def parse(iso8601duration) parts = ISO8601Parser.new(iso8601duration).parse! new(calculate_total_seconds(parts), parts) end
Private Class Methods
# File activesupport/lib/active_support/duration.rb, line 204 def calculate_total_seconds(parts) parts.inject(0) do |total, (part, value)| total + value * PARTS_IN_SECONDS[part] end end
Public Instance Methods
Returns the modulo of this Duration
by another Duration
or Numeric
. Numeric
values are treated as seconds.
# File activesupport/lib/active_support/duration.rb, line 281 def %(other) if Duration === other || Scalar === other Duration.build(value % other.value) elsif Numeric === other Duration.build(value % other) else raise_type_error(other) end end
Multiplies this Duration
by a Numeric
and returns a new Duration
.
# File activesupport/lib/active_support/duration.rb, line 256 def *(other) if Scalar === other || Duration === other Duration.new(value * other.value, parts.map { |type, number| [type, number * other.value] }) elsif Numeric === other Duration.new(value * other, parts.map { |type, number| [type, number * other] }) else raise_type_error(other) end end
Adds another Duration
or a Numeric
to this Duration
. Numeric
values are treated as seconds.
# File activesupport/lib/active_support/duration.rb, line 236 def +(other) if Duration === other parts = @parts.dup other.parts.each do |(key, value)| parts[key] += value end Duration.new(value + other.value, parts) else seconds = @parts[:seconds] + other Duration.new(value + other, @parts.merge(seconds: seconds)) end end
Divides this Duration
by a Numeric
and returns a new Duration
.
# File activesupport/lib/active_support/duration.rb, line 267 def /(other) if Scalar === other Duration.new(value / other.value, parts.map { |type, number| [type, number / other.value] }) elsif Duration === other value / other.value elsif Numeric === other Duration.new(value / other, parts.map { |type, number| [type, number / other] }) else raise_type_error(other) end end
Returns true
if other
is also a Duration
instance with the same value
, or if other == value
.
# File activesupport/lib/active_support/duration.rb, line 306 def ==(other) if Duration === other other.value == value else other == value end end
Returns true
if other
is also a Duration
instance, which has the same parts as this one.
# File activesupport/lib/active_support/duration.rb, line 348 def eql?(other) Duration === other && other.value.eql?(value) end
# File activesupport/lib/active_support/duration.rb, line 352 def hash @value.hash end
Returns the number of seconds that this Duration
represents.
1.minute.to_i # => 60 1.hour.to_i # => 3600 1.day.to_i # => 86400
Note that this conversion makes some assumptions about the duration of some periods, e.g. months are always 1/12 of year and years are 365.2425 days:
# equivalent to (1.year / 12).to_i 1.month.to_i # => 2629746 # equivalent to 365.2425.days.to_i 1.year.to_i # => 31556952
In such cases, Ruby's core Date and Time should be used for precision date and time arithmetic.
# File activesupport/lib/active_support/duration.rb, line 342 def to_i @value.to_i end
Returns the amount of seconds a duration covers as a string. For more information check to_i
method.
1.day.to_s # => "86400"
# File activesupport/lib/active_support/duration.rb, line 318 def to_s @value.to_s end
Private Instance Methods
# File activesupport/lib/active_support/duration.rb, line 414 def method_missing(method, *args, &block) value.public_send(method, *args, &block) end
# File activesupport/lib/active_support/duration.rb, line 418 def raise_type_error(other) raise TypeError, "no implicit conversion of #{other.class} into #{self.class}" end
# File activesupport/lib/active_support/duration.rb, line 410 def respond_to_missing?(method, _) value.respond_to?(method) end
# File activesupport/lib/active_support/duration.rb, line 392 def sum(sign, time = ::Time.current) parts.inject(time) do |t, (type, number)| if t.acts_like?(:time) || t.acts_like?(:date) if type == :seconds t.since(sign * number) elsif type == :minutes t.since(sign * number * 60) elsif type == :hours t.since(sign * number * 3600) else t.advance(type => sign * number) end else raise ::ArgumentError, "expected a time or date, got #{time.inspect}" end end end