timerizer 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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:
|