Module: PassiveSupport::CoreExtensions::Time::Calculations

Included in:
Time
Defined in:
lib/passive_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:



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

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.



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/passive_support/core_ext/time/calculations.rb', line 102

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

#ago(seconds) ⇒ Object

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



120
121
122
# File 'lib/passive_support/core_ext/time/calculations.rb', line 120

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)



212
213
214
215
# File 'lib/passive_support/core_ext/time/calculations.rb', line 212

def beginning_of_day
  #(self - seconds_since_midnight).change(:usec => 0)
  change(:hour => 0, :min => 0, :sec => 0, :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)



226
227
228
229
# File 'lib/passive_support/core_ext/time/calculations.rb', line 226

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)



241
242
243
# File 'lib/passive_support/core_ext/time/calculations.rb', line 241

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)



191
192
193
194
# File 'lib/passive_support/core_ext/time/calculations.rb', line 191

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)



253
254
255
# File 'lib/passive_support/core_ext/time/calculations.rb', line 253

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.



85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/passive_support/core_ext/time/calculations.rb', line 85

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 PassiveSupport::TimeWithZone instances can be chronologically compared with a Time



300
301
302
303
304
305
306
307
308
309
# File 'lib/passive_support/core_ext/time/calculations.rb', line 300

def compare_with_coercion(other)
  # if other is an PassiveSupport::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

#end_of_dayObject

Returns a new Time representing the end of the day, 23:59:59.999999 (.999999999 in ruby1.9)



221
222
223
# File 'lib/passive_support/core_ext/time/calculations.rb', line 221

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

#end_of_monthObject Also known as: at_end_of_month

Returns a new Time representing the end of the month (end of the last day of the month)



233
234
235
236
237
# File 'lib/passive_support/core_ext/time/calculations.rb', line 233

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

#end_of_quarterObject Also known as: at_end_of_quarter

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



247
248
249
# File 'lib/passive_support/core_ext/time/calculations.rb', line 247

def end_of_quarter
  beginning_of_month.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)



199
200
201
202
# File 'lib/passive_support/core_ext/time/calculations.rb', line 199

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 (end of the 31st of december)



259
260
261
# File 'lib/passive_support/core_ext/time/calculations.rb', line 259

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

#future?Boolean

Tells whether the Time object’s time lies in the future

Returns:

  • (Boolean)


73
74
75
# File 'lib/passive_support/core_ext/time/calculations.rb', line 73

def future?
  self > ::Time.current
end

#last_monthObject

:nodoc:



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

def last_month # :nodoc:
  PassiveSupport::Deprecation.warn("Time#last_month is deprecated and has been removed in Rails 3, please use Time#prev_month instead", caller)
  prev_month
end

#last_yearObject

:nodoc:



160
161
162
163
# File 'lib/passive_support/core_ext/time/calculations.rb', line 160

def last_year # :nodoc:
  PassiveSupport::Deprecation.warn("Time#last_year is deprecated and has been removed in Rails 3, please use Time#prev_year instead", caller)
  prev_year
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 PassiveSupport::TimeWithZone instances are coerced into values that Time#- will recognize



293
294
295
296
# File 'lib/passive_support/core_ext/time/calculations.rb', line 293

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

#minus_with_duration(other) ⇒ Object

:nodoc:



282
283
284
285
286
287
288
# File 'lib/passive_support/core_ext/time/calculations.rb', line 282

def minus_with_duration(other) #:nodoc:
  if PassiveSupport::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



141
142
143
# File 'lib/passive_support/core_ext/time/calculations.rb', line 141

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



146
147
148
# File 'lib/passive_support/core_ext/time/calculations.rb', line 146

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

#next_monthObject

Short-hand for months_since(1)



186
187
188
# File 'lib/passive_support/core_ext/time/calculations.rb', line 186

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).



206
207
208
209
# File 'lib/passive_support/core_ext/time/calculations.rb', line 206

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)



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

def next_year
  years_since(1)
end

#past?Boolean

Tells whether the Time object’s time lies in the past

Returns:

  • (Boolean)


63
64
65
# File 'lib/passive_support/core_ext/time/calculations.rb', line 63

def past?
  self < ::Time.current
end

#plus_with_duration(other) ⇒ Object

:nodoc:



274
275
276
277
278
279
280
# File 'lib/passive_support/core_ext/time/calculations.rb', line 274

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

#prev_monthObject

Short-hand for months_ago(1)



181
182
183
# File 'lib/passive_support/core_ext/time/calculations.rb', line 181

def prev_month
  months_ago(1)
end

#prev_yearObject

Short-hand for years_ago(1)



166
167
168
# File 'lib/passive_support/core_ext/time/calculations.rb', line 166

def prev_year
  years_ago(1)
end

#seconds_since_midnightObject

Seconds since midnight: Time.now.seconds_since_midnight



78
79
80
# File 'lib/passive_support/core_ext/time/calculations.rb', line 78

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.



126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/passive_support/core_ext/time/calculations.rb', line 126

def since(seconds)
  f = seconds.since(self)
  if PassiveSupport::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

#today?Boolean

Tells whether the Time object’s time is today

Returns:

  • (Boolean)


68
69
70
# File 'lib/passive_support/core_ext/time/calculations.rb', line 68

def today?
  self.to_date == ::Date.current
end

#tomorrowObject

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



270
271
272
# File 'lib/passive_support/core_ext/time/calculations.rb', line 270

def tomorrow
  advance(:days => 1)
end

#years_ago(years) ⇒ Object

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



151
152
153
# File 'lib/passive_support/core_ext/time/calculations.rb', line 151

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



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

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

#yesterdayObject

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



265
266
267
# File 'lib/passive_support/core_ext/time/calculations.rb', line 265

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