timerizer 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/timerizer.rb +336 -0
- metadata +46 -0
data/lib/timerizer.rb
ADDED
@@ -0,0 +1,336 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
# Represents a relative amount of time. For example, `5 days`, `4 years`, and `5 years, 4 hours, 3 minutes, 2 seconds` are all RelativeTimes.
|
4
|
+
class RelativeTime
|
5
|
+
# All potential units. Key is the unit name, and the value is its plural form.
|
6
|
+
@@units = {
|
7
|
+
:second => :seconds,
|
8
|
+
:minute => :minutes,
|
9
|
+
:hour => :hours,
|
10
|
+
:day => :days,
|
11
|
+
:week => :weeks,
|
12
|
+
:month => :months,
|
13
|
+
:year => :years,
|
14
|
+
:decade => :decades,
|
15
|
+
:century => :centuries,
|
16
|
+
:millennium => :millennia
|
17
|
+
}
|
18
|
+
|
19
|
+
# Unit values in seconds. If a unit is not present in this hash, it is assumed to be in the {@@in_months} hash.
|
20
|
+
@@in_seconds = {
|
21
|
+
:second => 1,
|
22
|
+
:minute => 60,
|
23
|
+
:hour => 3600,
|
24
|
+
:day => 86400,
|
25
|
+
:week => 604800
|
26
|
+
}
|
27
|
+
|
28
|
+
# Unit values in months. If a unit is not present in this hash, it is assumed to be in the {@@in_seconds} hash.
|
29
|
+
@@in_months = {
|
30
|
+
:month => 1,
|
31
|
+
:year => 12,
|
32
|
+
:decade => 120,
|
33
|
+
:century => 1200,
|
34
|
+
:millennium => 12000
|
35
|
+
}
|
36
|
+
|
37
|
+
# Average amount of time in a given unit. Used internally within the {#average} and {#unaverage} methods.
|
38
|
+
@@average_seconds = {
|
39
|
+
:month => 2629746,
|
40
|
+
:year => 31556952
|
41
|
+
}
|
42
|
+
|
43
|
+
# Initialize a new instance of RelativeTime.
|
44
|
+
# @overload new(hash)
|
45
|
+
# @param [Hash] units The base units to initialize with
|
46
|
+
# @option units [Fixnum] :seconds The number of seconds
|
47
|
+
# @option units [Fixnum] :months The number of months
|
48
|
+
# @overload new(count, unit)
|
49
|
+
# @param [Fixnum] count The number of units to initialize with
|
50
|
+
# @param [Symbol] unit The unit to initialize. See {@@units}
|
51
|
+
def initialize(count = 0, unit = :second)
|
52
|
+
if(count.is_a? Hash)
|
53
|
+
@seconds = count[:seconds] || 0
|
54
|
+
@months = count[:months] || 0
|
55
|
+
return
|
56
|
+
end
|
57
|
+
|
58
|
+
@seconds = 0
|
59
|
+
@months = 0
|
60
|
+
|
61
|
+
if(@@in_seconds.has_key?(unit))
|
62
|
+
@seconds = count * @@in_seconds.fetch(unit)
|
63
|
+
elsif(@@in_months.has_key?(unit))
|
64
|
+
@months = count * @@in_months.fetch(unit)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Return the number of base units in a RelativeTime.
|
69
|
+
# @param [Symbol] unit The unit to return, either :seconds or :months
|
70
|
+
# @return [Fixnum] The requested unit count
|
71
|
+
# @raise [ArgumentError] Unit requested was not :seconds or :months
|
72
|
+
def get(unit)
|
73
|
+
return @seconds if unit == :seconds
|
74
|
+
return @months if unit == :months
|
75
|
+
raise ArgumentError
|
76
|
+
end
|
77
|
+
|
78
|
+
# Determines the time between RelativeTime and the given time.
|
79
|
+
# @param [Time] time The initial time.
|
80
|
+
# @return [Time] The difference between the current RelativeTime and the given time
|
81
|
+
# @example 5 hours before January 1st, 2000 at noon
|
82
|
+
# 5.minutes.before(Time.now(2000, 1, 1, 12, 00, 00))
|
83
|
+
# => 2000-01-01 11:55:00 -0800
|
84
|
+
# @see #ago
|
85
|
+
# @see #after
|
86
|
+
# @see #from_now
|
87
|
+
def before(time)
|
88
|
+
time = time.to_time - @seconds
|
89
|
+
|
90
|
+
new_month = time.month - @months
|
91
|
+
new_year = time.year
|
92
|
+
while new_month < 1
|
93
|
+
new_month += 12
|
94
|
+
new_year -= 1
|
95
|
+
end
|
96
|
+
if(Date.valid_date?(new_year, new_month, time.day))
|
97
|
+
new_day = time.day
|
98
|
+
else
|
99
|
+
new_day = Date.new(new_year, new_month).days_in_month
|
100
|
+
end
|
101
|
+
|
102
|
+
new_time = Time.new(
|
103
|
+
new_year, new_month, new_day,
|
104
|
+
time.hour, time.min, time.sec
|
105
|
+
)
|
106
|
+
Time.at(new_time.to_i, time.nsec/1000)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Return the time between the RelativeTime and the current time.
|
110
|
+
# @return [Time] The difference between the current RelativeTime and Time#now
|
111
|
+
# @see #before
|
112
|
+
def ago
|
113
|
+
self.before(Time.now)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Return the time after the given time according to the current RelativeTime.
|
117
|
+
# @param [Time] time The starting time
|
118
|
+
# @return [Time] The time after the current RelativeTime and the given time
|
119
|
+
# @see #before
|
120
|
+
def after(time)
|
121
|
+
time = time.to_time + @seconds
|
122
|
+
|
123
|
+
new_year = time.year
|
124
|
+
new_month = time.month + @months
|
125
|
+
while new_month > 12
|
126
|
+
new_year += 1
|
127
|
+
new_month -= 12
|
128
|
+
end
|
129
|
+
if(Date.valid_date?(new_year, new_month, time.day))
|
130
|
+
new_day = time.day
|
131
|
+
else
|
132
|
+
new_day = Date.new(new_year, new_month).days_in_month
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
new_time = Time.new(
|
137
|
+
new_year, new_month, new_day,
|
138
|
+
time.hour, time.min, time.sec
|
139
|
+
)
|
140
|
+
Time.at(new_time.to_i, time.nsec/1000.0)
|
141
|
+
end
|
142
|
+
|
143
|
+
# Return the time after the current time and the RelativeTime.
|
144
|
+
# @return [Time] The time after the current time
|
145
|
+
def from_now
|
146
|
+
self.after(Time.now)
|
147
|
+
end
|
148
|
+
|
149
|
+
@@units.each do |unit, plural|
|
150
|
+
in_method = "in_#{plural}"
|
151
|
+
count_method = plural
|
152
|
+
superior_unit = @@units.keys.index(unit) + 1
|
153
|
+
|
154
|
+
define_method(in_method) do
|
155
|
+
if(@@in_seconds.has_key?(unit))
|
156
|
+
@seconds / @@in_seconds[unit]
|
157
|
+
elsif(@@in_months.has_key?(unit))
|
158
|
+
@months / @@in_months[unit]
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
define_method(count_method) do
|
163
|
+
in_superior = "in_#{@@units.values[superior_unit]}"
|
164
|
+
count_superior = @@units.keys[superior_unit]
|
165
|
+
|
166
|
+
time = self.send(in_method)
|
167
|
+
if(@@units.length > superior_unit)
|
168
|
+
time -= self.send(in_superior).send(count_superior).send(in_method)
|
169
|
+
end
|
170
|
+
time
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# Average second-based units to month-based units.
|
175
|
+
# @return [RelativeTime] The averaged RelativeTime
|
176
|
+
# @example
|
177
|
+
# 5.weeks.average
|
178
|
+
# => 1 month, 4 days, 13 hours, 30 minutes, 54 seconds
|
179
|
+
# @see #average!
|
180
|
+
# @see #unaverage
|
181
|
+
def average
|
182
|
+
return self unless @seconds > 0
|
183
|
+
|
184
|
+
months = (@seconds / @@average_seconds[:month])
|
185
|
+
seconds = @seconds - months.months.unaverage.get(:seconds)
|
186
|
+
RelativeTime.new({
|
187
|
+
:seconds => seconds,
|
188
|
+
:months => months + @months
|
189
|
+
})
|
190
|
+
end
|
191
|
+
|
192
|
+
# Destructively average second-based units to month-based units.
|
193
|
+
# @see #average
|
194
|
+
def average!
|
195
|
+
averaged = self.average
|
196
|
+
@seconds = averaged.get(:seconds)
|
197
|
+
@months = averaged.get(:months)
|
198
|
+
self
|
199
|
+
end
|
200
|
+
|
201
|
+
# Average month-based units to second-based units.
|
202
|
+
# @return [RelativeTime] the unaveraged RelativeTime.
|
203
|
+
# @example
|
204
|
+
# 1.month.unaverage
|
205
|
+
# => 4 weeks, 2 days, 10 hours, 29 minutes, 6 seconds
|
206
|
+
# @see #average
|
207
|
+
# @see #unaverage!
|
208
|
+
def unaverage
|
209
|
+
seconds = @@average_seconds[:month] * @months
|
210
|
+
seconds += @seconds
|
211
|
+
RelativeTime.new({:seconds => seconds})
|
212
|
+
end
|
213
|
+
|
214
|
+
# Destructively average month-based units to second-based units.
|
215
|
+
# @see #unaverage
|
216
|
+
def unaverage!
|
217
|
+
unaveraged = self.average
|
218
|
+
@seconds = unaverage.get(:seconds)
|
219
|
+
@months = unaverage.get(:months)
|
220
|
+
self
|
221
|
+
end
|
222
|
+
|
223
|
+
# Add two {RelativeTime}s together.
|
224
|
+
# @raise ArgumentError Argument isn't a {RelativeTime}
|
225
|
+
# @see #-
|
226
|
+
def +(time)
|
227
|
+
raise ArgumentError unless time.is_a?(RelativeTime)
|
228
|
+
RelativeTime.new({
|
229
|
+
:seconds => @seconds + time.get(:seconds),
|
230
|
+
:months => @months + time.get(:months)
|
231
|
+
})
|
232
|
+
end
|
233
|
+
|
234
|
+
# Find the difference between two {RelativeTime}s.
|
235
|
+
# @raise ArgumentError Argument isn't a {RelativeTime}
|
236
|
+
# @see #+
|
237
|
+
def -(time)
|
238
|
+
raise ArgumentError unless time.is_a?(RelativeTime)
|
239
|
+
RelativeTime.new({
|
240
|
+
:seconds => @seconds - time.get(:seconds),
|
241
|
+
:months => @months - time.get(:months)
|
242
|
+
})
|
243
|
+
end
|
244
|
+
|
245
|
+
# Convert {RelativeTime} to a human-readable format.
|
246
|
+
# @example
|
247
|
+
# (14.months 49.hours).to_s
|
248
|
+
# => 2 years, 2 months, 3 days, 1 hour
|
249
|
+
def to_s
|
250
|
+
times = []
|
251
|
+
|
252
|
+
@@units.each do |unit, plural|
|
253
|
+
time = self.respond_to?(plural) ? self.send(plural) : 0
|
254
|
+
times << [time, (time != 1) ? plural : unit] if time > 0
|
255
|
+
end
|
256
|
+
|
257
|
+
times.map do |time|
|
258
|
+
time.join(' ')
|
259
|
+
end.reverse.join(', ')
|
260
|
+
end
|
261
|
+
end
|
262
|
+
# {Time} class monkeywrenched with {RelativeTime} support.
|
263
|
+
class Time
|
264
|
+
add = instance_method(:+)
|
265
|
+
define_method(:+) do |time|
|
266
|
+
if(time.class == RelativeTime)
|
267
|
+
time.after(self)
|
268
|
+
else
|
269
|
+
add.bind(self).(time)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
subtract = instance_method(:-)
|
274
|
+
define_method(:-) do |time|
|
275
|
+
if(time.class == RelativeTime)
|
276
|
+
time.before(self)
|
277
|
+
else
|
278
|
+
subtract.bind(self).(time)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
# Convert {Time} to {Date}.
|
283
|
+
# @return [Date] {Time} as {Date}
|
284
|
+
# @example
|
285
|
+
# Time.new(2000, 1, 1, 12, 30).to_date
|
286
|
+
# => #<Date: 2000-01-01 ((2451545j,0s,0n),+0s,2299161j)>
|
287
|
+
def to_date
|
288
|
+
Date.new(self.year, self.month, self.day)
|
289
|
+
end
|
290
|
+
|
291
|
+
# Convert self to {Time}.
|
292
|
+
# @see Date#to_time
|
293
|
+
def to_time
|
294
|
+
self
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
# {Date} class monkeywrenched with {RelativeTime} helpers.
|
299
|
+
class Date
|
300
|
+
# Return the number of days in a given month.
|
301
|
+
# @return [Fixnum] Number of days in the month of the {Date}.
|
302
|
+
# @example
|
303
|
+
# Date.new(2000, 2).days_in_month
|
304
|
+
# => 29
|
305
|
+
def days_in_month
|
306
|
+
days_in_feb = (not self.leap?) ? 28 : 29
|
307
|
+
number_of_days = [
|
308
|
+
31, days_in_feb, 31, 30, 31, 30,
|
309
|
+
31, 31, 30, 31, 30, 31
|
310
|
+
]
|
311
|
+
|
312
|
+
number_of_days.fetch(self.month - 1)
|
313
|
+
end
|
314
|
+
|
315
|
+
# Return self as {Date}.
|
316
|
+
# @see Time#to_date
|
317
|
+
def to_date
|
318
|
+
self
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
# Monkeywrenched {Fixnum} class enabled to return {RelativeTime} objects.
|
323
|
+
# @example
|
324
|
+
# 5.minutes
|
325
|
+
# => 5 minutes
|
326
|
+
# @see RelativeTime @@units
|
327
|
+
class Fixnum
|
328
|
+
units = RelativeTime.class_variable_get(:@@units)
|
329
|
+
units.each do |unit, plural|
|
330
|
+
define_method(unit) do |added_time = RelativeTime.new|
|
331
|
+
time = RelativeTime.new(self, unit)
|
332
|
+
time + added_time
|
333
|
+
end
|
334
|
+
alias_method(plural, unit)
|
335
|
+
end
|
336
|
+
end
|
metadata
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: timerizer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Kyle Lacy
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-07-17 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: A simple set of Rails-like time helpers
|
15
|
+
email: kylelacy@me.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- lib/timerizer.rb
|
21
|
+
homepage: http://github.com/kylewlacy/timerizer
|
22
|
+
licenses: []
|
23
|
+
post_install_message:
|
24
|
+
rdoc_options: []
|
25
|
+
require_paths:
|
26
|
+
- lib
|
27
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ! '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
requirements: []
|
40
|
+
rubyforge_project:
|
41
|
+
rubygems_version: 1.8.24
|
42
|
+
signing_key:
|
43
|
+
specification_version: 3
|
44
|
+
summary: Rails time helpers... without the Rails
|
45
|
+
test_files: []
|
46
|
+
has_rdoc:
|