aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport
diff options
context:
space:
mode:
authorAndrew White <andrew.white@unboxed.co>2017-01-12 10:02:16 +0000
committerAndrew White <andrew.white@unboxed.co>2017-01-12 10:05:16 +0000
commit9ae511f55047b2380224ad993a7d0ed97dbcf944 (patch)
treef381ee6e343edc72cc89036928737383fde690e7 /activesupport
parent2a5ae2b714046d3f7eb1219eb366cf84c1cf9bb5 (diff)
downloadrails-9ae511f55047b2380224ad993a7d0ed97dbcf944.tar.gz
rails-9ae511f55047b2380224ad993a7d0ed97dbcf944.tar.bz2
rails-9ae511f55047b2380224ad993a7d0ed97dbcf944.zip
Add duration constructors for use in Numeric extensions
The Numeric extensions like 1.day, 1.month, etc. shouldn't know how the internals of ActiveSupport::Duration works.
Diffstat (limited to 'activesupport')
-rw-r--r--activesupport/lib/active_support/core_ext/integer/time.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/numeric/time.rb12
-rw-r--r--activesupport/lib/active_support/duration.rb94
3 files changed, 76 insertions, 34 deletions
diff --git a/activesupport/lib/active_support/core_ext/integer/time.rb b/activesupport/lib/active_support/core_ext/integer/time.rb
index 66ac3ca1b4..74baae3639 100644
--- a/activesupport/lib/active_support/core_ext/integer/time.rb
+++ b/activesupport/lib/active_support/core_ext/integer/time.rb
@@ -18,12 +18,12 @@ class Integer
# # equivalent to Time.now.advance(months: 4, years: 5)
# (4.months + 5.years).from_now
def months
- ActiveSupport::Duration.new(self * ActiveSupport::Duration::PARTS_IN_SECONDS[:months].to_i, [[:months, self]])
+ ActiveSupport::Duration.months(self)
end
alias :month :months
def years
- ActiveSupport::Duration.new(self * ActiveSupport::Duration::PARTS_IN_SECONDS[:years].to_i, [[:years, self]])
+ ActiveSupport::Duration.years(self)
end
alias :year :years
end
diff --git a/activesupport/lib/active_support/core_ext/numeric/time.rb b/activesupport/lib/active_support/core_ext/numeric/time.rb
index 6ca2468047..2e6c70d418 100644
--- a/activesupport/lib/active_support/core_ext/numeric/time.rb
+++ b/activesupport/lib/active_support/core_ext/numeric/time.rb
@@ -19,7 +19,7 @@ class Numeric
# # equivalent to Time.current.advance(months: 4, years: 5)
# (4.months + 5.years).from_now
def seconds
- ActiveSupport::Duration.new(self, [[:seconds, self]])
+ ActiveSupport::Duration.seconds(self)
end
alias :second :seconds
@@ -27,7 +27,7 @@ class Numeric
#
# 2.minutes # => 2 minutes
def minutes
- ActiveSupport::Duration.new(self * ActiveSupport::Duration::PARTS_IN_SECONDS[:minutes], [[:minutes, self]])
+ ActiveSupport::Duration.minutes(self)
end
alias :minute :minutes
@@ -35,7 +35,7 @@ class Numeric
#
# 2.hours # => 2 hours
def hours
- ActiveSupport::Duration.new(self * ActiveSupport::Duration::PARTS_IN_SECONDS[:hours], [[:hours, self]])
+ ActiveSupport::Duration.hours(self)
end
alias :hour :hours
@@ -43,7 +43,7 @@ class Numeric
#
# 2.days # => 2 days
def days
- ActiveSupport::Duration.new(self * ActiveSupport::Duration::PARTS_IN_SECONDS[:days], [[:days, self]])
+ ActiveSupport::Duration.days(self)
end
alias :day :days
@@ -51,7 +51,7 @@ class Numeric
#
# 2.weeks # => 2 weeks
def weeks
- ActiveSupport::Duration.new(self * ActiveSupport::Duration::PARTS_IN_SECONDS[:weeks], [[:weeks, self]])
+ ActiveSupport::Duration.weeks(self)
end
alias :week :weeks
@@ -59,7 +59,7 @@ class Numeric
#
# 2.fortnights # => 4 weeks
def fortnights
- ActiveSupport::Duration.new(self * 2 * ActiveSupport::Duration::PARTS_IN_SECONDS[:weeks], [[:weeks, self * 2]])
+ ActiveSupport::Duration.weeks(self * 2)
end
alias :fortnight :fortnights
diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb
index 71b6001503..003f6203ef 100644
--- a/activesupport/lib/active_support/duration.rb
+++ b/activesupport/lib/active_support/duration.rb
@@ -7,14 +7,21 @@ module ActiveSupport
#
# 1.month.ago # equivalent to Time.now.advance(months: -1)
class Duration
+ SECONDS_PER_MINUTE = 60
+ SECONDS_PER_HOUR = 3600
+ SECONDS_PER_DAY = 86400
+ SECONDS_PER_WEEK = 604800
+ SECONDS_PER_MONTH = 2629746 # 1/12 of a gregorian year
+ SECONDS_PER_YEAR = 31556952 # length of a gregorian year (365.2425 days)
+
PARTS_IN_SECONDS = {
- seconds: 1, # Used in parse method for ease of handling part hashes with seconds
- minutes: 60,
- hours: 60 * 60,
- days: 24 * 60 * 60,
- weeks: 7 * 24 * 60 * 60,
- months: ((365.2425 / 12) * 24 * 60 * 60).ceil, # 1 year is always 12 months long, no sense to bind it to days
- years: (365.2425 * 24 * 60 * 60).ceil, # Gregorian year length to account for the every 400 non-leap year
+ 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
attr_accessor :value, :parts
@@ -22,6 +29,60 @@ module ActiveSupport
autoload :ISO8601Parser, "active_support/duration/iso8601_parser"
autoload :ISO8601Serializer, "active_support/duration/iso8601_serializer"
+ class << self
+ # Creates a new Duration from string formatted according to ISO 8601 Duration.
+ #
+ # See {ISO 8601}[http://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+.
+ def parse(iso8601duration)
+ parts = ISO8601Parser.new(iso8601duration).parse!
+ new(calculate_total_seconds(parts), parts)
+ end
+
+ def ===(other) #:nodoc:
+ other.is_a?(Duration)
+ rescue ::NoMethodError
+ false
+ end
+
+ def seconds(value) #:nodoc:
+ new(value, [[:seconds, value]])
+ end
+
+ def minutes(value) #:nodoc:
+ new(value * SECONDS_PER_MINUTE, [[:minutes, value]])
+ end
+
+ def hours(value) #:nodoc:
+ new(value * SECONDS_PER_HOUR, [[:hours, value]])
+ end
+
+ def days(value) #:nodoc:
+ new(value * SECONDS_PER_DAY, [[:days, value]])
+ end
+
+ def weeks(value) #:nodoc:
+ new(value * SECONDS_PER_WEEK, [[:weeks, value]])
+ end
+
+ def months(value) #:nodoc:
+ new(value * SECONDS_PER_MONTH, [[:months, value]])
+ end
+
+ def years(value) #:nodoc:
+ new(value * SECONDS_PER_YEAR, [[:years, value]])
+ end
+
+ private
+
+ def calculate_total_seconds(parts)
+ parts.inject(0) do |total, (part, value)|
+ total + value * PARTS_IN_SECONDS[part]
+ end
+ end
+ end
+
def initialize(value, parts) #:nodoc:
@value, @parts = value, parts.to_h
@parts.default = 0
@@ -113,12 +174,6 @@ module ActiveSupport
@value.hash
end
- def self.===(other) #:nodoc:
- other.is_a?(Duration)
- rescue ::NoMethodError
- false
- end
-
# Calculates a new Time or Date that is as far in the future
# as this Duration represents.
def since(time = ::Time.current)
@@ -149,19 +204,6 @@ module ActiveSupport
@value.respond_to?(method, include_private)
end
- # Creates a new Duration from string formatted according to ISO 8601 Duration.
- #
- # See {ISO 8601}[http://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+.
- def self.parse(iso8601duration)
- parts = ISO8601Parser.new(iso8601duration).parse!
- total_seconds = parts.inject(0) do |total, (part, value)|
- total + value * PARTS_IN_SECONDS[part]
- end
- new(total_seconds, parts)
- end
-
# Build ISO 8601 Duration string for this duration.
# The +precision+ parameter can be used to limit seconds' precision of duration.
def iso8601(precision: nil)