Module: ActiveSupport::CoreExtensions::Time::Calculations

Included in:
Time
Defined in:
lib/active_support/core_ext/time/calculations.rb

Overview

Enables the use of time calculations within Time itself

Defined Under Namespace

Modules: ClassMethods

Constant Summary collapse

COMMON_YEAR_DAYS_IN_MONTH =
[nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object

:nodoc:


6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/active_support/core_ext/time/calculations.rb', line 6

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

Instance Method Details

#advance(options) ⇒ Object

Uses Date to provide precise Time calculations for years, months, and days. The options parameter takes a hash with any of these keys: :years, :months, :weeks, :days, :hours, :minutes, :seconds.


85
86
87
88
89
90
# File 'lib/active_support/core_ext/time/calculations.rb', line 85

def advance(options)
  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

#ago(seconds) ⇒ Object

Returns a new Time representing the time a number of seconds ago, this is basically a wrapper around the Numeric extension


93
94
95
# File 'lib/active_support/core_ext/time/calculations.rb', line 93

def ago(seconds)
  self.since(-seconds)
end

#beginning_of_dayObject Also known as: midnight, at_midnight, at_beginning_of_day

Returns a new Time representing the start of the day (0:00)


176
177
178
# File 'lib/active_support/core_ext/time/calculations.rb', line 176

def beginning_of_day
  (self - self.seconds_since_midnight).change(:usec => 0)
end

#beginning_of_monthObject Also known as: at_beginning_of_month

Returns a new Time representing the start of the month (1st of the month, 0:00)


189
190
191
192
# File 'lib/active_support/core_ext/time/calculations.rb', line 189

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

#beginning_of_quarterObject Also known as: at_beginning_of_quarter

Returns a new Time representing the start of the quarter (1st of january, april, july, october, 0:00)


204
205
206
# File 'lib/active_support/core_ext/time/calculations.rb', line 204

def beginning_of_quarter
  beginning_of_month.change(:month => [10, 7, 4, 1].detect { |m| m <= self.month })
end

#beginning_of_weekObject Also known as: monday, at_beginning_of_week

Returns a new Time representing the “start” of this week (Monday, 0:00)


155
156
157
158
# File 'lib/active_support/core_ext/time/calculations.rb', line 155

def beginning_of_week
  days_to_monday = self.wday!=0 ? self.wday-1 : 6
  (self - days_to_monday.days).midnight
end

#beginning_of_yearObject Also known as: at_beginning_of_year

Returns a new Time representing the start of the year (1st of january, 0:00)


216
217
218
# File 'lib/active_support/core_ext/time/calculations.rb', line 216

def beginning_of_year
  change(:month => 1,:day => 1,:hour => 0, :min => 0, :sec => 0, :usec => 0)
end

#change(options) ⇒ Object

Returns a new Time where one or more of the elements have been changed according to the options parameter. The time 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 minute is passed, then sec and usec is set to 0.


68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/active_support/core_ext/time/calculations.rb', line 68

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

#compare_with_coercion(other) ⇒ Object

Layers additional behavior on Time#<=> so that DateTime and ActiveSupport::TimeWithZone instances can be chronologically compared with a Time


263
264
265
266
267
268
269
270
271
272
# File 'lib/active_support/core_ext/time/calculations.rb', line 263

def compare_with_coercion(other)
  # if other is an ActiveSupport::TimeWithZone, coerce a Time instance from it so we can do <=> comparision
  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

#end_of_dayObject

Returns a new Time representing the end of the day (23:59:59)


184
185
186
# File 'lib/active_support/core_ext/time/calculations.rb', line 184

def end_of_day
  change(:hour => 23, :min => 59, :sec => 59)
end

#end_of_monthObject Also known as: at_end_of_month

Returns a new Time representing the end of the month (last day of the month, 0:00)


196
197
198
199
200
# File 'lib/active_support/core_ext/time/calculations.rb', line 196

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 => 0)
end

#end_of_quarterObject Also known as: at_end_of_quarter

Returns a new Time representing the end of the quarter (last day of march, june, september, december, 23:59:59)


210
211
212
# File 'lib/active_support/core_ext/time/calculations.rb', line 210

def end_of_quarter
  change(:month => [3, 6, 9, 12].detect { |m| m >= self.month }).end_of_month
end

#end_of_weekObject Also known as: at_end_of_week

Returns a new Time representing the end of this week (Sunday, 23:59:59)


163
164
165
166
# File 'lib/active_support/core_ext/time/calculations.rb', line 163

def end_of_week
  days_to_sunday = self.wday!=0 ? 7-self.wday : 0
  (self + days_to_sunday.days).end_of_day
end

#end_of_yearObject Also known as: at_end_of_year

Returns a new Time representing the end of the year (31st of december, 23:59:59)


222
223
224
# File 'lib/active_support/core_ext/time/calculations.rb', line 222

def end_of_year
  change(:month => 12,:day => 31,:hour => 23, :min => 59, :sec => 59)
end

#last_monthObject

Short-hand for months_ago(1)


145
146
147
# File 'lib/active_support/core_ext/time/calculations.rb', line 145

def last_month
  months_ago(1)
end

#last_yearObject

Short-hand for years_ago(1)


134
135
136
# File 'lib/active_support/core_ext/time/calculations.rb', line 134

def last_year
  years_ago(1)
end

#minus_with_coercion(other) ⇒ Object

Time#- can also be used to determine the number of seconds between two Time instances. We're layering on additional behavior so that ActiveSupport::TimeWithZone instances are coerced into values that Time#- will recognize


256
257
258
259
# File 'lib/active_support/core_ext/time/calculations.rb', line 256

def minus_with_coercion(other)
  other = other.comparable_time if other.respond_to?(:comparable_time)
  minus_without_coercion(other)
end

#minus_with_duration(other) ⇒ Object

:nodoc:


245
246
247
248
249
250
251
# File 'lib/active_support/core_ext/time/calculations.rb', line 245

def minus_with_duration(other) #:nodoc:
  if ActiveSupport::Duration === other
    other.until(self)
  else
    minus_without_duration(other)
  end
end

#months_ago(months) ⇒ Object

Returns a new Time representing the time a number of specified months ago


114
115
116
# File 'lib/active_support/core_ext/time/calculations.rb', line 114

def months_ago(months)
  advance(:months => -months)
end

#months_since(months) ⇒ Object

Returns a new Time representing the time a number of specified months in the future


119
120
121
# File 'lib/active_support/core_ext/time/calculations.rb', line 119

def months_since(months)
  advance(:months => months)
end

#next_monthObject

Short-hand for months_since(1)


150
151
152
# File 'lib/active_support/core_ext/time/calculations.rb', line 150

def next_month
  months_since(1)
end

#next_week(day = :monday) ⇒ Object

Returns a new Time representing the start of the given day in next week (default is Monday).


170
171
172
173
# File 'lib/active_support/core_ext/time/calculations.rb', line 170

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

#next_yearObject

Short-hand for years_since(1)


139
140
141
# File 'lib/active_support/core_ext/time/calculations.rb', line 139

def next_year
  years_since(1)
end

#plus_with_duration(other) ⇒ Object

:nodoc:


237
238
239
240
241
242
243
# File 'lib/active_support/core_ext/time/calculations.rb', line 237

def plus_with_duration(other) #:nodoc:
  if ActiveSupport::Duration === other
    other.since(self)
  else
    plus_without_duration(other)
  end
end

#seconds_since_midnightObject

Seconds since midnight: Time.now.seconds_since_midnight


61
62
63
# File 'lib/active_support/core_ext/time/calculations.rb', line 61

def seconds_since_midnight
  self.to_i - self.change(:hour => 0).to_i + (self.usec/1.0e+6)
end

#since(seconds) ⇒ Object Also known as: in

Returns a new Time representing the time a number of seconds since the instance time, this is basically a wrapper around the Numeric extension.


99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/active_support/core_ext/time/calculations.rb', line 99

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

#tomorrowObject

Convenience method which returns a new Time representing the time 1 day since the instance time


233
234
235
# File 'lib/active_support/core_ext/time/calculations.rb', line 233

def tomorrow
  advance(:days => 1)
end

#years_ago(years) ⇒ Object

Returns a new Time representing the time a number of specified years ago


124
125
126
# File 'lib/active_support/core_ext/time/calculations.rb', line 124

def years_ago(years)
  advance(:years => -years)
end

#years_since(years) ⇒ Object

Returns a new Time representing the time a number of specified years in the future


129
130
131
# File 'lib/active_support/core_ext/time/calculations.rb', line 129

def years_since(years)
  advance(:years => years)
end

#yesterdayObject

Convenience method which returns a new Time representing the time 1 day ago


228
229
230
# File 'lib/active_support/core_ext/time/calculations.rb', line 228

def yesterday
  advance(:days => -1)
end