Class: ActiveSupport::Duration
- Defined in:
- lib/active_support/duration.rb,
lib/active_support/duration/iso8601_parser.rb,
lib/active_support/duration/iso8601_serializer.rb
Overview
Active Support 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)
Defined Under Namespace
Classes: ISO8601Parser, ISO8601Serializer, Scalar
Constant Summary collapse
- SECONDS_PER_MINUTE =
60
- SECONDS_PER_HOUR =
3600
- SECONDS_PER_DAY =
86400
- SECONDS_PER_WEEK =
604800
- SECONDS_PER_MONTH =
1/12 of a gregorian year
2629746
- SECONDS_PER_YEAR =
length of a gregorian year (365.2425 days)
31556952
- PARTS_IN_SECONDS =
{ seconds: 1, minutes: SECONDS_PER_MINUTE, hours: SECONDS_PER_HOUR, days: SECONDS_PER_DAY, weeks: SECONDS_PER_WEEK, months: SECONDS_PER_MONTH, years: SECONDS_PER_YEAR }.freeze
- PARTS =
[:years, :months, :weeks, :days, :hours, :minutes, :seconds].freeze
- VARIABLE_PARTS =
[:years, :months, :weeks, :days].freeze
Instance Attribute Summary collapse
-
#value ⇒ Object
readonly
Returns the value of attribute value.
Class Method Summary collapse
-
.===(other) ⇒ Object
:nodoc:.
-
.build(value) ⇒ Object
Creates a new Duration from a seconds value that is converted to the individual parts:.
-
.days(value) ⇒ Object
:nodoc:.
-
.hours(value) ⇒ Object
:nodoc:.
-
.minutes(value) ⇒ Object
:nodoc:.
-
.months(value) ⇒ Object
:nodoc:.
-
.parse(iso8601duration) ⇒ Object
Creates a new Duration from string formatted according to ISO 8601 Duration.
-
.seconds(value) ⇒ Object
:nodoc:.
-
.weeks(value) ⇒ Object
:nodoc:.
-
.years(value) ⇒ Object
:nodoc:.
Instance Method Summary collapse
-
#%(other) ⇒ Object
Returns the modulo of this Duration by another Duration or Numeric.
-
#*(other) ⇒ Object
Multiplies this Duration by a Numeric and returns a new Duration.
-
#+(other) ⇒ Object
Adds another Duration or a Numeric to this Duration.
-
#+@ ⇒ Object
:nodoc:.
-
#-(other) ⇒ Object
Subtracts another Duration or a Numeric from this Duration.
-
#-@ ⇒ Object
:nodoc:.
-
#/(other) ⇒ Object
Divides this Duration by a Numeric and returns a new Duration.
-
#<=>(other) ⇒ Object
Compares one Duration with another or a Numeric to this Duration.
-
#==(other) ⇒ Object
Returns
true
ifother
is also a Duration instance with the samevalue
, or ifother == value
. -
#_parts ⇒ Object
:nodoc:.
-
#ago(time = ::Time.current) ⇒ Object
(also: #until, #before)
Calculates a new Time or Date that is as far in the past as this Duration represents.
-
#as_json(options = nil) ⇒ Object
:nodoc:.
-
#coerce(other) ⇒ Object
:nodoc:.
-
#encode_with(coder) ⇒ Object
:nodoc:.
-
#eql?(other) ⇒ Boolean
Returns
true
ifother
is also a Duration instance, which has the same parts as this one. - #hash ⇒ Object
-
#in_days ⇒ Object
Returns the amount of days a duration covers as a float.
-
#in_hours ⇒ Object
Returns the amount of hours a duration covers as a float.
-
#in_minutes ⇒ Object
Returns the amount of minutes a duration covers as a float.
-
#in_months ⇒ Object
Returns the amount of months a duration covers as a float.
-
#in_weeks ⇒ Object
Returns the amount of weeks a duration covers as a float.
-
#in_years ⇒ Object
Returns the amount of years a duration covers as a float.
-
#init_with(coder) ⇒ Object
:nodoc:.
-
#initialize(value, parts, variable = nil) ⇒ Duration
constructor
:nodoc:.
-
#inspect ⇒ Object
:nodoc:.
-
#instance_of?(klass) ⇒ Boolean
:nodoc:.
-
#is_a?(klass) ⇒ Boolean
(also: #kind_of?)
:nodoc:.
-
#iso8601(precision: nil) ⇒ Object
Build ISO 8601 Duration string for this duration.
-
#parts ⇒ Object
Returns a copy of the parts hash that defines the duration.
-
#since(time = ::Time.current) ⇒ Object
(also: #from_now, #after)
Calculates a new Time or Date that is as far in the future as this Duration represents.
-
#to_i ⇒ Object
(also: #in_seconds)
Returns the number of seconds that this Duration represents.
-
#to_s ⇒ Object
Returns the amount of seconds a duration covers as a string.
-
#variable? ⇒ Boolean
:nodoc:.
Constructor Details
#initialize(value, parts, variable = nil) ⇒ Duration
:nodoc:
226 227 228 229 230 231 232 233 234 235 |
# File 'lib/active_support/duration.rb', line 226 def initialize(value, parts, variable = nil) # :nodoc: @value, @parts = value, parts @parts.reject! { |k, v| v.zero? } unless value == 0 @parts.freeze @variable = variable if @variable.nil? @variable = @parts.any? { |part, _| VARIABLE_PARTS.include?(part) } end end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing ⇒ Object (private)
512 513 514 |
# File 'lib/active_support/duration.rb', line 512 def method_missing(...) value.public_send(...) end |
Instance Attribute Details
#value ⇒ Object (readonly)
Returns the value of attribute value.
133 134 135 |
# File 'lib/active_support/duration.rb', line 133 def value @value end |
Class Method Details
.===(other) ⇒ Object
:nodoc:
149 150 151 152 153 |
# File 'lib/active_support/duration.rb', line 149 def ===(other) # :nodoc: other.is_a?(Duration) rescue ::NoMethodError false end |
.build(value) ⇒ Object
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}
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/active_support/duration.rb', line 189 def build(value) unless value.is_a?(::Numeric) raise TypeError, "can't build an #{self.name} from a #{value.class.name}" end parts = {} remainder_sign = value <=> 0 remainder = value.round(9).abs variable = false PARTS.each do |part| unless part == :seconds part_in_seconds = PARTS_IN_SECONDS[part] parts[part] = remainder.div(part_in_seconds) * remainder_sign remainder %= part_in_seconds unless parts[part].zero? variable ||= VARIABLE_PARTS.include?(part) end end end unless value == 0 parts[:seconds] = remainder * remainder_sign new(value, parts, variable) end |
.days(value) ⇒ Object
:nodoc:
167 168 169 |
# File 'lib/active_support/duration.rb', line 167 def days(value) # :nodoc: new(value * SECONDS_PER_DAY, { days: value }, true) end |
.hours(value) ⇒ Object
:nodoc:
163 164 165 |
# File 'lib/active_support/duration.rb', line 163 def hours(value) # :nodoc: new(value * SECONDS_PER_HOUR, { hours: value }, false) end |
.minutes(value) ⇒ Object
:nodoc:
159 160 161 |
# File 'lib/active_support/duration.rb', line 159 def minutes(value) # :nodoc: new(value * SECONDS_PER_MINUTE, { minutes: value }, false) end |
.months(value) ⇒ Object
:nodoc:
175 176 177 |
# File 'lib/active_support/duration.rb', line 175 def months(value) # :nodoc: new(value * SECONDS_PER_MONTH, { months: value }, true) end |
.parse(iso8601duration) ⇒ Object
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
.
144 145 146 147 |
# File 'lib/active_support/duration.rb', line 144 def parse(iso8601duration) parts = ISO8601Parser.new(iso8601duration).parse! new(calculate_total_seconds(parts), parts) end |
.seconds(value) ⇒ Object
:nodoc:
155 156 157 |
# File 'lib/active_support/duration.rb', line 155 def seconds(value) # :nodoc: new(value, { seconds: value }, false) end |
.weeks(value) ⇒ Object
:nodoc:
171 172 173 |
# File 'lib/active_support/duration.rb', line 171 def weeks(value) # :nodoc: new(value * SECONDS_PER_WEEK, { weeks: value }, true) end |
.years(value) ⇒ Object
:nodoc:
179 180 181 |
# File 'lib/active_support/duration.rb', line 179 def years(value) # :nodoc: new(value * SECONDS_PER_YEAR, { years: value }, true) end |
Instance Method Details
#%(other) ⇒ Object
Returns the modulo of this Duration by another Duration or Numeric. Numeric values are treated as seconds.
312 313 314 315 316 317 318 319 320 |
# File 'lib/active_support/duration.rb', line 312 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 |
#*(other) ⇒ Object
Multiplies this Duration by a Numeric and returns a new Duration.
287 288 289 290 291 292 293 294 295 |
# File 'lib/active_support/duration.rb', line 287 def *(other) if Scalar === other || Duration === other Duration.new(value * other.value, @parts.transform_values { |number| number * other.value }, @variable || other.variable?) elsif Numeric === other Duration.new(value * other, @parts.transform_values { |number| number * other }, @variable) else raise_type_error(other) end end |
#+(other) ⇒ Object
Adds another Duration or a Numeric to this Duration. Numeric values are treated as seconds.
268 269 270 271 272 273 274 275 276 277 278 |
# File 'lib/active_support/duration.rb', line 268 def +(other) if Duration === other parts = @parts.merge(other._parts) do |_key, value, other_value| value + other_value end Duration.new(value + other.value, parts, @variable || other.variable?) else seconds = @parts.fetch(:seconds, 0) + other Duration.new(value + other, @parts.merge(seconds: seconds), @variable) end end |
#+@ ⇒ Object
:nodoc:
326 327 328 |
# File 'lib/active_support/duration.rb', line 326 def +@ # :nodoc: self end |
#-(other) ⇒ Object
Subtracts another Duration or a Numeric from this Duration. Numeric values are treated as seconds.
282 283 284 |
# File 'lib/active_support/duration.rb', line 282 def -(other) self + (-other) end |
#-@ ⇒ Object
:nodoc:
322 323 324 |
# File 'lib/active_support/duration.rb', line 322 def -@ # :nodoc: Duration.new(-value, @parts.transform_values(&:-@), @variable) end |
#/(other) ⇒ Object
Divides this Duration by a Numeric and returns a new Duration.
298 299 300 301 302 303 304 305 306 307 308 |
# File 'lib/active_support/duration.rb', line 298 def /(other) if Scalar === other Duration.new(value / other.value, @parts.transform_values { |number| number / other.value }, @variable) elsif Duration === other value / other.value elsif Numeric === other Duration.new(value / other, @parts.transform_values { |number| number / other }, @variable) else raise_type_error(other) end end |
#<=>(other) ⇒ Object
Compares one Duration with another or a Numeric to this Duration. Numeric values are treated as seconds.
258 259 260 261 262 263 264 |
# File 'lib/active_support/duration.rb', line 258 def <=>(other) if Duration === other value <=> other.value elsif Numeric === other value <=> other end end |
#==(other) ⇒ Object
Returns true
if other
is also a Duration instance with the same value
, or if other == value
.
341 342 343 344 345 346 347 |
# File 'lib/active_support/duration.rb', line 341 def ==(other) if Duration === other other.value == value else other == value end end |
#_parts ⇒ Object
:nodoc:
481 482 483 |
# File 'lib/active_support/duration.rb', line 481 def _parts # :nodoc: @parts end |
#ago(time = ::Time.current) ⇒ Object Also known as: until, before
Calculates a new Time or Date that is as far in the past as this Duration represents.
444 445 446 |
# File 'lib/active_support/duration.rb', line 444 def ago(time = ::Time.current) sum(-1, time) end |
#as_json(options = nil) ⇒ Object
:nodoc:
459 460 461 |
# File 'lib/active_support/duration.rb', line 459 def as_json( = nil) # :nodoc: to_i end |
#coerce(other) ⇒ Object
:nodoc:
245 246 247 248 249 250 251 252 253 254 |
# File 'lib/active_support/duration.rb', line 245 def coerce(other) # :nodoc: case other when Scalar [other, self] when Duration [Scalar.new(other.value), self] else [Scalar.new(other), self] end end |
#encode_with(coder) ⇒ Object
:nodoc:
467 468 469 |
# File 'lib/active_support/duration.rb', line 467 def encode_with(coder) # :nodoc: coder.map = { "value" => @value, "parts" => @parts } end |
#eql?(other) ⇒ Boolean
Returns true
if other
is also a Duration instance, which has the same parts as this one.
426 427 428 |
# File 'lib/active_support/duration.rb', line 426 def eql?(other) Duration === other && other.value.eql?(value) end |
#hash ⇒ Object
430 431 432 |
# File 'lib/active_support/duration.rb', line 430 def hash @value.hash end |
#in_days ⇒ Object
Returns the amount of days a duration covers as a float
12.hours.in_days # => 0.5
399 400 401 |
# File 'lib/active_support/duration.rb', line 399 def in_days in_seconds / SECONDS_PER_DAY.to_f end |
#in_hours ⇒ Object
Returns the amount of hours a duration covers as a float
1.day.in_hours # => 24.0
392 393 394 |
# File 'lib/active_support/duration.rb', line 392 def in_hours in_seconds / SECONDS_PER_HOUR.to_f end |
#in_minutes ⇒ Object
Returns the amount of minutes a duration covers as a float
1.day.in_minutes # => 1440.0
385 386 387 |
# File 'lib/active_support/duration.rb', line 385 def in_minutes in_seconds / SECONDS_PER_MINUTE.to_f end |
#in_months ⇒ Object
Returns the amount of months a duration covers as a float
9.weeks.in_months # => 2.07
413 414 415 |
# File 'lib/active_support/duration.rb', line 413 def in_months in_seconds / SECONDS_PER_MONTH.to_f end |
#in_weeks ⇒ Object
Returns the amount of weeks a duration covers as a float
2.months.in_weeks # => 8.696
406 407 408 |
# File 'lib/active_support/duration.rb', line 406 def in_weeks in_seconds / SECONDS_PER_WEEK.to_f end |
#in_years ⇒ Object
Returns the amount of years a duration covers as a float
30.days.in_years # => 0.082
420 421 422 |
# File 'lib/active_support/duration.rb', line 420 def in_years in_seconds / SECONDS_PER_YEAR.to_f end |
#init_with(coder) ⇒ Object
:nodoc:
463 464 465 |
# File 'lib/active_support/duration.rb', line 463 def init_with(coder) # :nodoc: initialize(coder["value"], coder["parts"]) end |
#inspect ⇒ Object
:nodoc:
450 451 452 453 454 455 456 457 |
# File 'lib/active_support/duration.rb', line 450 def inspect # :nodoc: return "#{value} seconds" if @parts.empty? @parts. sort_by { |unit, _ | PARTS.index(unit) }. map { |unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}" }. to_sentence(locale: false) end |
#instance_of?(klass) ⇒ Boolean
:nodoc:
335 336 337 |
# File 'lib/active_support/duration.rb', line 335 def instance_of?(klass) # :nodoc: Duration == klass || value.instance_of?(klass) end |
#is_a?(klass) ⇒ Boolean Also known as: kind_of?
:nodoc:
330 331 332 |
# File 'lib/active_support/duration.rb', line 330 def is_a?(klass) # :nodoc: Duration == klass || value.is_a?(klass) end |
#iso8601(precision: nil) ⇒ Object
Build ISO 8601 Duration string for this duration. The precision
parameter can be used to limit seconds’ precision of duration.
473 474 475 |
# File 'lib/active_support/duration.rb', line 473 def iso8601(precision: nil) ISO8601Serializer.new(self, precision: precision).serialize end |
#parts ⇒ Object
Returns a copy of the parts hash that defines the duration.
5.minutes.parts # => {:minutes=>5}
3.years.parts # => {:years=>3}
241 242 243 |
# File 'lib/active_support/duration.rb', line 241 def parts @parts.dup end |
#since(time = ::Time.current) ⇒ Object Also known as: from_now, after
Calculates a new Time or Date that is as far in the future as this Duration represents.
436 437 438 |
# File 'lib/active_support/duration.rb', line 436 def since(time = ::Time.current) sum(1, time) end |
#to_i ⇒ Object Also known as: in_seconds
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.
377 378 379 |
# File 'lib/active_support/duration.rb', line 377 def to_i @value.to_i end |
#to_s ⇒ Object
Returns the amount of seconds a duration covers as a string. For more information check to_i method.
1.day.to_s # => "86400"
353 354 355 |
# File 'lib/active_support/duration.rb', line 353 def to_s @value.to_s end |
#variable? ⇒ Boolean
:nodoc:
477 478 479 |
# File 'lib/active_support/duration.rb', line 477 def variable? # :nodoc: @variable end |