module ActiveSupport::CoreExtensions::Time::Calculations
def self.included(base) #:nodoc:
def self.included(base) #:nodoc: base.extend ClassMethods base.class_eval do alias_method :plus_without_duration, :+ alias_method :+, :plus_with_duration alias_method :minus_without_duration, :- alias_method :-, :minus_with_duration alias_method :minus_without_coercion, :- alias_method :-, :minus_with_coercion alias_method :compare_without_coercion, :<=> alias_method :<=>, :compare_with_coercion end end
def advance(options)
:months, :weeks, :days, :hours,
The +options+ parameter takes a hash with any of these keys: :years,
Uses Date to provide precise Time calculations for years, months, and days.
def advance(options) unless options[:weeks].nil? options[:weeks], partial_weeks = options[:weeks].divmod(1) options[:days] = (options[:days] || 0) + 7 * partial_weeks end unless options[:days].nil? options[:days], partial_days = options[:days].divmod(1) options[:hours] = (options[:hours] || 0) + 24 * partial_days end d = to_date.advance(options) time_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day) seconds_to_advance = (options[:seconds] || 0) + (options[:minutes] || 0) * 60 + (options[:hours] || 0) * 3600 seconds_to_advance == 0 ? time_advanced_by_date : time_advanced_by_date.since(seconds_to_advance) end
def ago(seconds)
def ago(seconds) self.since(-seconds) end
def beginning_of_day
def beginning_of_day #(self - seconds_since_midnight).change(:usec => 0) change(:hour => 0, :min => 0, :sec => 0, :usec => 0) end
def beginning_of_month
def beginning_of_month #self - ((self.mday-1).days + self.seconds_since_midnight) change(:day => 1,:hour => 0, :min => 0, :sec => 0, :usec => 0) end
def beginning_of_quarter
def beginning_of_quarter beginning_of_month.change(:month => [10, 7, 4, 1].detect { |m| m <= self.month }) end
def beginning_of_week
def beginning_of_week days_to_monday = self.wday!=0 ? self.wday-1 : 6 (self - days_to_monday.days).midnight end
def beginning_of_year
def beginning_of_year change(:month => 1,:day => 1,:hour => 0, :min => 0, :sec => 0, :usec => 0) end
def change(options)
(hour, minute, sec, usec) reset cascadingly, so if only the hour is passed, then minute, sec, and usec is set to 0. If the hour and
Returns a new Time where one or more of the elements have been changed according to the +options+ parameter. The time options
def change(options) ::Time.send( self.utc? ? :utc_time : :local_time, options[:year] || self.year, options[:month] || self.month, options[:day] || self.day, options[:hour] || self.hour, options[:min] || (options[:hour] ? 0 : self.min), options[:sec] || ((options[:hour] || options[:min]) ? 0 : self.sec), options[:usec] || ((options[:hour] || options[:min] || options[:sec]) ? 0 : self.usec) ) end
def compare_with_coercion(other)
Layers additional behavior on Time#<=> so that DateTime and ActiveSupport::TimeWithZone instances
def compare_with_coercion(other) # if other is an ActiveSupport::TimeWithZone, coerce a Time instance from it so we can do <=> comparison other = other.comparable_time if other.respond_to?(:comparable_time) if other.acts_like?(:date) # other is a Date/DateTime, so coerce self #to_datetime and hand off to DateTime#<=> to_datetime.compare_without_coercion(other) else compare_without_coercion(other) end end
def end_of_day
def end_of_day change(:hour => 23, :min => 59, :sec => 59, :usec => 999999.999) end
def end_of_month
def end_of_month #self - ((self.mday-1).days + self.seconds_since_midnight) last_day = ::Time.days_in_month( self.month, self.year ) change(:day => last_day, :hour => 23, :min => 59, :sec => 59, :usec => 999999.999) end
def end_of_quarter
def end_of_quarter beginning_of_month.change(:month => [3, 6, 9, 12].detect { |m| m >= self.month }).end_of_month end
def end_of_week
def end_of_week days_to_sunday = self.wday!=0 ? 7-self.wday : 0 (self + days_to_sunday.days).end_of_day end
def end_of_year
def end_of_year change(:month => 12, :day => 31, :hour => 23, :min => 59, :sec => 59, :usec => 999999.999) end
def future?
def future? self > ::Time.current end
def last_month # :nodoc:
def last_month # :nodoc: ActiveSupport::Deprecation.warn("Time#last_month is deprecated and has been removed in Rails 3, please use Time#prev_month instead", caller) prev_month end
def last_year # :nodoc:
def last_year # :nodoc: ActiveSupport::Deprecation.warn("Time#last_year is deprecated and has been removed in Rails 3, please use Time#prev_year instead", caller) prev_year end
def minus_with_coercion(other)
We're layering on additional behavior so that ActiveSupport::TimeWithZone instances
Time#- can also be used to determine the number of seconds between two Time instances.
def minus_with_coercion(other) other = other.comparable_time if other.respond_to?(:comparable_time) other.is_a?(::DateTime) ? to_f - other.to_f : minus_without_coercion(other) end
def minus_with_duration(other) #:nodoc:
def minus_with_duration(other) #:nodoc: if ActiveSupport::Duration === other other.until(self) else minus_without_duration(other) end end
def months_ago(months)
def months_ago(months) advance(:months => -months) end
def months_since(months)
def months_since(months) advance(:months => months) end
def next_month
def next_month months_since(1) end
def next_week(day = :monday)
def next_week(day = :monday) days_into_week = { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6} since(1.week).beginning_of_week.since(days_into_week[day].day).change(:hour => 0) end
def next_year
def next_year years_since(1) end
def past?
def past? self < ::Time.current end
def plus_with_duration(other) #:nodoc:
def plus_with_duration(other) #:nodoc: if ActiveSupport::Duration === other other.since(self) else plus_without_duration(other) end end
def prev_month
def prev_month months_ago(1) end
def prev_year
def prev_year years_ago(1) end
def seconds_since_midnight
def seconds_since_midnight self.to_i - self.change(:hour => 0).to_i + (self.usec/1.0e+6) end
def since(seconds)
Returns a new Time representing the time a number of seconds since the instance time, this is basically a wrapper around
def since(seconds) f = seconds.since(self) if ActiveSupport::Duration === seconds f else initial_dst = self.dst? ? 1 : 0 final_dst = f.dst? ? 1 : 0 (seconds.abs >= 86400 && initial_dst != final_dst) ? f + (initial_dst - final_dst).hours : f end rescue self.to_datetime.since(seconds) end
def today?
def today? self.to_date == ::Date.current end
def tomorrow
def tomorrow advance(:days => 1) end
def years_ago(years)
def years_ago(years) advance(:years => -years) end
def years_since(years)
def years_since(years) advance(:years => years) end
def yesterday
def yesterday advance(:days => -1) end