Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 345 lines (291 sloc) 12.586 kB
2ace3d9 @jeremy Explicitly require AS::Duration
jeremy authored
1 require 'active_support/duration'
3018f47 @joshk add some missing requires for AS core_ext/numeric/time. Closes #1038
joshk authored
2 require 'active_support/core_ext/time/zones'
780ee36 @guilleiguaran Fix AR test suite error under Rubinius 2.0
guilleiguaran authored
3 require 'active_support/core_ext/time/conversions'
2ace3d9 @jeremy Explicitly require AS::Duration
jeremy authored
4
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
5 class Time
6 COMMON_YEAR_DAYS_IN_MONTH = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
7 DAYS_INTO_WEEK = { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6 }
8
9 class << self
10 # Overriding case equality method so that it returns true for ActiveSupport::TimeWithZone instances
11 def ===(other)
1ebf84a @jeremyevans Edited activesupport/lib/active_support/core_ext/time/calculations.rb…
jeremyevans authored
12 super || (self == Time && other.is_a?(ActiveSupport::TimeWithZone))
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
13 end
14
15 # Return the number of days in the given month.
16 # If no year is specified, it will use the current year.
17 def days_in_month(month, year = now.year)
18 return 29 if month == 2 && ::Date.gregorian_leap?(year)
19 COMMON_YEAR_DAYS_IN_MONTH[month]
20 end
21
22 # Returns a new Time if requested year can be accommodated by Ruby's Time class
23 # (i.e., if year is within either 1970..2038 or 1902..2038, depending on system architecture);
bc1bcdd @fxn implements weeks_ago and prev_week for Date/DateTime/Time [#5122 stat…
fxn authored
24 # otherwise returns a DateTime.
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
25 def time_with_datetime_fallback(utc_or_local, year, month=1, day=1, hour=0, min=0, sec=0, usec=0)
53c1cd6 @fxn let Time.time_with_datetime_fallback handle properly years in the ran…
fxn authored
26 time = ::Time.send(utc_or_local, year, month, day, hour, min, sec, usec)
27 # This check is needed because Time.utc(y) returns a time object in the 2000s for 0 <= y <= 138.
28 time.year == year ? time : ::DateTime.civil_from_format(utc_or_local, year, month, day, hour, min, sec)
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
29 rescue
53c1cd6 @fxn let Time.time_with_datetime_fallback handle properly years in the ran…
fxn authored
30 ::DateTime.civil_from_format(utc_or_local, year, month, day, hour, min, sec)
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
31 end
32
33 # Wraps class method +time_with_datetime_fallback+ with +utc_or_local+ set to <tt>:utc</tt>.
34 def utc_time(*args)
35 time_with_datetime_fallback(:utc, *args)
36 end
37
38 # Wraps class method +time_with_datetime_fallback+ with +utc_or_local+ set to <tt>:local</tt>.
39 def local_time(*args)
40 time_with_datetime_fallback(:local, *args)
41 end
201f373 @spastorino Refactor move some date, time and date_time methods to */zones and fi…
spastorino authored
42
0f8d279 @joshk updated Time, Date and DateTime current methods in AS to use Time.zon…
joshk authored
43 # Returns <tt>Time.zone.now</tt> when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise just returns <tt>Time.now</tt>.
201f373 @spastorino Refactor move some date, time and date_time methods to */zones and fi…
spastorino authored
44 def current
0f8d279 @joshk updated Time, Date and DateTime current methods in AS to use Time.zon…
joshk authored
45 ::Time.zone ? ::Time.zone.now : ::Time.now
201f373 @spastorino Refactor move some date, time and date_time methods to */zones and fi…
spastorino authored
46 end
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
47 end
48
49 # Tells whether the Time object's time lies in the past
50 def past?
51 self < ::Time.current
52 end
53
54 # Tells whether the Time object's time is today
55 def today?
56 to_date == ::Date.current
57 end
58
59 # Tells whether the Time object's time lies in the future
60 def future?
61 self > ::Time.current
62 end
63
64 # Seconds since midnight: Time.now.seconds_since_midnight
65 def seconds_since_midnight
66 to_i - change(:hour => 0).to_i + (usec / 1.0e+6)
67 end
68
69 # Returns a new Time where one or more of the elements have been changed according to the +options+ parameter. The time options
ea54d2c @oestrich Update Time#change docs to reflect the options it uses
oestrich authored
70 # (hour, min, sec, usec) reset cascadingly, so if only the hour is passed, then minute, sec, and usec is set to 0. If the hour and
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
71 # minute is passed, then sec and usec is set to 0.
72 def change(options)
73 ::Time.send(
74 utc? ? :utc_time : :local_time,
75 options[:year] || year,
76 options[:month] || month,
77 options[:day] || day,
78 options[:hour] || hour,
79 options[:min] || (options[:hour] ? 0 : min),
80 options[:sec] || ((options[:hour] || options[:min]) ? 0 : sec),
81 options[:usec] || ((options[:hour] || options[:min] || options[:sec]) ? 0 : usec)
82 )
83 end
84
85 # Uses Date to provide precise Time calculations for years, months, and days.
86 # The +options+ parameter takes a hash with any of these keys: <tt>:years</tt>,
87 # <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>, <tt>:hours</tt>,
88 # <tt>:minutes</tt>, <tt>:seconds</tt>.
89 def advance(options)
90 unless options[:weeks].nil?
91 options[:weeks], partial_weeks = options[:weeks].divmod(1)
92 options[:days] = (options[:days] || 0) + 7 * partial_weeks
93 end
b451de0 @spastorino Deletes trailing whitespaces (over text files only find * -type f -ex…
spastorino authored
94
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
95 unless options[:days].nil?
96 options[:days], partial_days = options[:days].divmod(1)
97 options[:hours] = (options[:hours] || 0) + 24 * partial_days
98 end
b451de0 @spastorino Deletes trailing whitespaces (over text files only find * -type f -ex…
spastorino authored
99
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
100 d = to_date.advance(options)
101 time_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day)
102 seconds_to_advance = (options[:seconds] || 0) + (options[:minutes] || 0) * 60 + (options[:hours] || 0) * 3600
103 seconds_to_advance == 0 ? time_advanced_by_date : time_advanced_by_date.since(seconds_to_advance)
104 end
105
106 # Returns a new Time representing the time a number of seconds ago, this is basically a wrapper around the Numeric extension
107 def ago(seconds)
108 since(-seconds)
109 end
110
685a53a @jeremy Merge branch 'master' into cherry
jeremy authored
111 # Returns a new Time representing the time a number of seconds since the instance time
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
112 def since(seconds)
685a53a @jeremy Merge branch 'master' into cherry
jeremy authored
113 self + seconds
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
114 rescue
685a53a @jeremy Merge branch 'master' into cherry
jeremy authored
115 to_datetime.since(seconds)
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
116 end
117 alias :in :since
118
bc1bcdd @fxn implements weeks_ago and prev_week for Date/DateTime/Time [#5122 stat…
fxn authored
119 # Returns a new Time representing the time a number of specified weeks ago.
120 def weeks_ago(weeks)
121 advance(:weeks => -weeks)
122 end
123
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
124 # Returns a new Time representing the time a number of specified months ago
125 def months_ago(months)
126 advance(:months => -months)
127 end
128
129 # Returns a new Time representing the time a number of specified months in the future
130 def months_since(months)
131 advance(:months => months)
132 end
133
134 # Returns a new Time representing the time a number of specified years ago
135 def years_ago(years)
136 advance(:years => -years)
137 end
138
139 # Returns a new Time representing the time a number of specified years in the future
140 def years_since(years)
141 advance(:years => years)
142 end
143
144 # Short-hand for years_ago(1)
2203c78 @fxn defines prev_(month|year) in Date and Time to ease transition to 1.9,…
fxn authored
145 def prev_year
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
146 years_ago(1)
147 end
148
149 # Short-hand for years_since(1)
150 def next_year
151 years_since(1)
152 end
153
154 # Short-hand for months_ago(1)
2203c78 @fxn defines prev_(month|year) in Date and Time to ease transition to 1.9,…
fxn authored
155 def prev_month
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
156 months_ago(1)
157 end
158
159 # Short-hand for months_since(1)
160 def next_month
161 months_since(1)
162 end
163
7a33a00 @gregolsen API docstrings updated with default value info
gregolsen authored
164 # Returns number of days to start of this week, week starts on start_day (default is :monday).
3f1a4c3 @gregolsen beginning_of_week extended in both Time and Date so that to return we…
gregolsen authored
165 def days_to_week_start(start_day = :monday)
166 start_day_number = DAYS_INTO_WEEK[start_day]
167 current_day_number = wday != 0 ? wday - 1 : 6
168 days_span = current_day_number - start_day_number
169 days_span >= 0 ? days_span : 7 + days_span
170 end
171
7a33a00 @gregolsen API docstrings updated with default value info
gregolsen authored
172 # Returns a new Time representing the "start" of this week, week starts on start_day (default is :monday, i.e. Monday, 0:00).
3f1a4c3 @gregolsen beginning_of_week extended in both Time and Date so that to return we…
gregolsen authored
173 def beginning_of_week(start_day = :monday)
174 days_to_start = days_to_week_start(start_day)
175 (self - days_to_start.days).midnight
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
176 end
177 alias :at_beginning_of_week :beginning_of_week
178
a8f2860 @vijaydev Convert aliases monday and sunday to methods
vijaydev authored
179 # Returns a new +Date+/+DateTime+ representing the start of this week. Week is
180 # assumed to start on a Monday. +DateTime+ objects have their time set to 0:00.
181 def monday
182 beginning_of_week
183 end
184
7a33a00 @gregolsen API docstrings updated with default value info
gregolsen authored
185 # Returns a new Time representing the end of this week, week starts on start_day (default is :monday, i.e. end of Sunday).
3f1a4c3 @gregolsen beginning_of_week extended in both Time and Date so that to return we…
gregolsen authored
186 def end_of_week(start_day = :monday)
187 days_to_end = 6 - days_to_week_start(start_day)
188 (self + days_to_end.days).end_of_day
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
189 end
190 alias :at_end_of_week :end_of_week
191
80ac4dc @vijaydev Adds Time#sunday method
vijaydev authored
192 # Returns a new +Date+/+DateTime+ representing the end of this week. Week is
193 # assumed to start on a Monday. +DateTime+ objects have their time set to 23:59:59.
194 def sunday
195 end_of_week
196 end
197
8d83e33 @gregolsen updating API docstring so that user can infer default value
gregolsen authored
198 # Returns a new Time representing the start of the given day in the previous week (default is :monday).
bc1bcdd @fxn implements weeks_ago and prev_week for Date/DateTime/Time [#5122 stat…
fxn authored
199 def prev_week(day = :monday)
200 ago(1.week).beginning_of_week.since(DAYS_INTO_WEEK[day].day).change(:hour => 0)
201 end
202
8d83e33 @gregolsen updating API docstring so that user can infer default value
gregolsen authored
203 # Returns a new Time representing the start of the given day in next week (default is :monday).
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
204 def next_week(day = :monday)
205 since(1.week).beginning_of_week.since(DAYS_INTO_WEEK[day].day).change(:hour => 0)
206 end
207
208 # Returns a new Time representing the start of the day (0:00)
209 def beginning_of_day
e93d0a5 @jeremy Ruby 1.9: fix Time#beginning_of_day inaccuracy due to subtracting a F…
jeremy authored
210 #(self - seconds_since_midnight).change(:usec => 0)
377bd69 @farski Removed some redundant Time#change time options from beginning_of_ me…
farski authored
211 change(:hour => 0)
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
212 end
213 alias :midnight :beginning_of_day
214 alias :at_midnight :beginning_of_day
215 alias :at_beginning_of_day :beginning_of_day
216
3545d6b @hugopeixoto Setting usec (and nsec for Ruby 1.9) on Time#end_of_* methods [#1255 …
hugopeixoto authored
217 # Returns a new Time representing the end of the day, 23:59:59.999999 (.999999999 in ruby1.9)
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
218 def end_of_day
3545d6b @hugopeixoto Setting usec (and nsec for Ruby 1.9) on Time#end_of_* methods [#1255 …
hugopeixoto authored
219 change(:hour => 23, :min => 59, :sec => 59, :usec => 999999.999)
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
220 end
221
222 # Returns a new Time representing the start of the month (1st of the month, 0:00)
223 def beginning_of_month
224 #self - ((self.mday-1).days + self.seconds_since_midnight)
377bd69 @farski Removed some redundant Time#change time options from beginning_of_ me…
farski authored
225 change(:day => 1, :hour => 0)
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
226 end
227 alias :at_beginning_of_month :beginning_of_month
228
3545d6b @hugopeixoto Setting usec (and nsec for Ruby 1.9) on Time#end_of_* methods [#1255 …
hugopeixoto authored
229 # Returns a new Time representing the end of the month (end of the last day of the month)
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
230 def end_of_month
231 #self - ((self.mday-1).days + self.seconds_since_midnight)
232 last_day = ::Time.days_in_month(month, year)
3545d6b @hugopeixoto Setting usec (and nsec for Ruby 1.9) on Time#end_of_* methods [#1255 …
hugopeixoto authored
233 change(:day => last_day, :hour => 23, :min => 59, :sec => 59, :usec => 999999.999)
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
234 end
235 alias :at_end_of_month :end_of_month
236
237 # Returns a new Time representing the start of the quarter (1st of january, april, july, october, 0:00)
238 def beginning_of_quarter
239 beginning_of_month.change(:month => [10, 7, 4, 1].detect { |m| m <= month })
240 end
241 alias :at_beginning_of_quarter :beginning_of_quarter
242
3545d6b @hugopeixoto Setting usec (and nsec for Ruby 1.9) on Time#end_of_* methods [#1255 …
hugopeixoto authored
243 # Returns a new Time representing the end of the quarter (end of the last day of march, june, september, december)
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
244 def end_of_quarter
245 beginning_of_month.change(:month => [3, 6, 9, 12].detect { |m| m >= month }).end_of_month
246 end
247 alias :at_end_of_quarter :end_of_quarter
248
d6cc0e5 @dhh Added Time#whole_day/week/quarter/year as a way of generating ranges …
dhh authored
249 # Returns a new Time representing the start of the year (1st of january, 0:00)
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
250 def beginning_of_year
377bd69 @farski Removed some redundant Time#change time options from beginning_of_ me…
farski authored
251 change(:month => 1, :day => 1, :hour => 0)
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
252 end
253 alias :at_beginning_of_year :beginning_of_year
254
3545d6b @hugopeixoto Setting usec (and nsec for Ruby 1.9) on Time#end_of_* methods [#1255 …
hugopeixoto authored
255 # Returns a new Time representing the end of the year (end of the 31st of december)
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
256 def end_of_year
3545d6b @hugopeixoto Setting usec (and nsec for Ruby 1.9) on Time#end_of_* methods [#1255 …
hugopeixoto authored
257 change(:month => 12, :day => 31, :hour => 23, :min => 59, :sec => 59, :usec => 999999.999)
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
258 end
259 alias :at_end_of_year :end_of_year
260
261 # Convenience method which returns a new Time representing the time 1 day ago
262 def yesterday
263 advance(:days => -1)
264 end
265
266 # Convenience method which returns a new Time representing the time 1 day since the instance time
267 def tomorrow
268 advance(:days => 1)
269 end
270
d6cc0e5 @dhh Added Time#whole_day/week/quarter/year as a way of generating ranges …
dhh authored
271 # Returns a Range representing the whole day of the current time.
a646780 @dhh Rename Time#whole_* to Time#all_* [thanks Pratik!]
dhh authored
272 def all_day
d6cc0e5 @dhh Added Time#whole_day/week/quarter/year as a way of generating ranges …
dhh authored
273 beginning_of_day..end_of_day
274 end
275
b037401 @kennyj GH #4883. Optional start_day argument for Time#all_week
kennyj authored
276 # Returns a Range representing the whole week of the current time. Week starts on start_day (default is :monday, i.e. end of Sunday).
277 def all_week(start_day = :monday)
278 beginning_of_week(start_day)..end_of_week(start_day)
d6cc0e5 @dhh Added Time#whole_day/week/quarter/year as a way of generating ranges …
dhh authored
279 end
280
281 # Returns a Range representing the whole month of the current time.
a646780 @dhh Rename Time#whole_* to Time#all_* [thanks Pratik!]
dhh authored
282 def all_month
d6cc0e5 @dhh Added Time#whole_day/week/quarter/year as a way of generating ranges …
dhh authored
283 beginning_of_month..end_of_month
284 end
285
286 # Returns a Range representing the whole quarter of the current time.
a646780 @dhh Rename Time#whole_* to Time#all_* [thanks Pratik!]
dhh authored
287 def all_quarter
d6cc0e5 @dhh Added Time#whole_day/week/quarter/year as a way of generating ranges …
dhh authored
288 beginning_of_quarter..end_of_quarter
289 end
290
291 # Returns a Range representing the whole year of the current time.
a646780 @dhh Rename Time#whole_* to Time#all_* [thanks Pratik!]
dhh authored
292 def all_year
d6cc0e5 @dhh Added Time#whole_day/week/quarter/year as a way of generating ranges …
dhh authored
293 beginning_of_year..end_of_year
294 end
295
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
296 def plus_with_duration(other) #:nodoc:
297 if ActiveSupport::Duration === other
298 other.since(self)
299 else
300 plus_without_duration(other)
301 end
302 end
303 alias_method :plus_without_duration, :+
304 alias_method :+, :plus_with_duration
305
306 def minus_with_duration(other) #:nodoc:
307 if ActiveSupport::Duration === other
308 other.until(self)
309 else
310 minus_without_duration(other)
311 end
312 end
313 alias_method :minus_without_duration, :-
314 alias_method :-, :minus_with_duration
315
316 # Time#- can also be used to determine the number of seconds between two Time instances.
317 # We're layering on additional behavior so that ActiveSupport::TimeWithZone instances
318 # are coerced into values that Time#- will recognize
319 def minus_with_coercion(other)
320 other = other.comparable_time if other.respond_to?(:comparable_time)
e98f957 @gbuesing Time#- with a DateTime argument behaves the same as with a Time argum…
gbuesing authored
321 other.is_a?(DateTime) ? to_f - other.to_f : minus_without_coercion(other)
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
322 end
323 alias_method :minus_without_coercion, :-
324 alias_method :-, :minus_with_coercion
325
326 # Layers additional behavior on Time#<=> so that DateTime and ActiveSupport::TimeWithZone instances
327 # can be chronologically compared with a Time
328 def compare_with_coercion(other)
7872cc9 @dcrec1 refactored Time#<=> and DateTime#<=> by removing unnecessary calls wi…
dcrec1 authored
329 # we're avoiding Time#to_datetime cause it's expensive
330 other.is_a?(Time) ? compare_without_coercion(other.to_time) : to_datetime <=> other
6ff54f7 @dhh Added Time::Calculations to ask for things like Time.now.tomorrow, Ti…
dhh authored
331 end
1c5a694 @jeremy Convert Time extension modules to class reopens
jeremy authored
332 alias_method :compare_without_coercion, :<=>
333 alias_method :<=>, :compare_with_coercion
a491207 @marcandre Fix inconsistencies with Time{WithZone}#{hash,eql?}
marcandre authored
334
335 # Layers additional behavior on Time#eql? so that ActiveSupport::TimeWithZone instances
336 # can be eql? to an equivalent Time
337 def eql_with_coercion(other)
338 # if other is an ActiveSupport::TimeWithZone, coerce a Time instance from it so we can do eql? comparison
339 other = other.comparable_time if other.respond_to?(:comparable_time)
340 eql_without_coercion(other)
341 end
342 alias_method :eql_without_coercion, :eql?
343 alias_method :eql?, :eql_with_coercion
b4c34f7 @jamis Fixed that Time#midnight would have a non-zero usec on some platforms…
jamis authored
344 end
Something went wrong with that request. Please try again.