Class: ActiveSupport::Duration
- Defined in:
- activesupport/lib/active_support/duration.rb,
activesupport/lib/active_support/duration/iso8601_parser.rb,
activesupport/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
trueifotheris also a Duration instance with the samevalue, or if other == 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
trueifotheris 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 'activesupport/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)
516 517 518 |
# File 'activesupport/lib/active_support/duration.rb', line 516 def method_missing(...) value.public_send(...) end |
Instance Attribute Details
#value ⇒ Object (readonly)
Returns the value of attribute value.
133 134 135 |
# File 'activesupport/lib/active_support/duration.rb', line 133 def value @value end |
Class Method Details
.===(other) ⇒ Object
:nodoc:
149 150 151 152 153 |
# File 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/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 8601[https://en.wikipedia.org/wiki/ISO_8601#Durations] 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 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/lib/active_support/duration.rb', line 282 def -(other) self + (-other) end |
#-@ ⇒ Object
:nodoc:
322 323 324 |
# File 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/lib/active_support/duration.rb', line 426 def eql?(other) Duration === other && other.value.eql?(value) end |
#hash ⇒ Object
430 431 432 |
# File 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/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 'activesupport/lib/active_support/duration.rb', line 353 def to_s @value.to_s end |
#variable? ⇒ Boolean
:nodoc:
477 478 479 |
# File 'activesupport/lib/active_support/duration.rb', line 477 def variable? # :nodoc: @variable end |