timerizer 0.2.1 → 0.3.0
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.
- checksums.yaml +4 -4
- data/lib/timerizer.rb +70 -637
- data/lib/timerizer/core.rb +3 -0
- data/lib/timerizer/duration.rb +766 -0
- data/lib/timerizer/version.rb +3 -0
- data/lib/timerizer/wall_clock.rb +224 -0
- metadata +65 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4180c0ffb871e50a47486d122ae17fc42f37cab8
|
|
4
|
+
data.tar.gz: dad648d7c6fb1f996b61d5aa1c55a7c2637abc52
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 369ea12d3065905306b0649f81b6517fe49d50cd2ddd601cb7d7132571ea07b21846760bc24e75b3dee56aab841c7b42a6107f54283472e014905910ba1bbca5
|
|
7
|
+
data.tar.gz: 02487b58f59677d6cf481f3a3a78a6fe10f04bb598a67a445e8e1e81cb73395267526913041a53c95477882f3bd7fe95868da9a2c190bf13791775b31b4a1510
|
data/lib/timerizer.rb
CHANGED
|
@@ -1,621 +1,7 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "date"
|
|
2
|
+
require_relative "./timerizer/core"
|
|
2
3
|
|
|
3
|
-
#
|
|
4
|
-
class RelativeTime
|
|
5
|
-
@@units = {
|
|
6
|
-
:second => :seconds,
|
|
7
|
-
:minute => :minutes,
|
|
8
|
-
:hour => :hours,
|
|
9
|
-
:day => :days,
|
|
10
|
-
:week => :weeks,
|
|
11
|
-
:month => :months,
|
|
12
|
-
:year => :years,
|
|
13
|
-
:decade => :decades,
|
|
14
|
-
:century => :centuries,
|
|
15
|
-
:millennium => :millennia
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
@@in_seconds = {
|
|
19
|
-
:second => 1,
|
|
20
|
-
:minute => 60,
|
|
21
|
-
:hour => 3600,
|
|
22
|
-
:day => 86400,
|
|
23
|
-
:week => 604800
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
@@in_months = {
|
|
27
|
-
:month => 1,
|
|
28
|
-
:year => 12,
|
|
29
|
-
:decade => 120,
|
|
30
|
-
:century => 1200,
|
|
31
|
-
:millennium => 12000
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
# Average amount of time in a given unit. Used internally within the {#average} and {#unaverage} methods.
|
|
35
|
-
@@average_seconds = {
|
|
36
|
-
:month => 2629746,
|
|
37
|
-
:year => 31556952
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
# Default syntax formats that can be used with #to_s
|
|
41
|
-
# @see #to_s
|
|
42
|
-
@@syntaxes = {
|
|
43
|
-
:micro => {
|
|
44
|
-
:units => {
|
|
45
|
-
:seconds => 's',
|
|
46
|
-
:minutes => 'm',
|
|
47
|
-
:hours => 'h',
|
|
48
|
-
:days => 'd',
|
|
49
|
-
:weeks => 'w',
|
|
50
|
-
:months => 'mn',
|
|
51
|
-
:years => 'y',
|
|
52
|
-
},
|
|
53
|
-
:separator => '',
|
|
54
|
-
:delimiter => ' ',
|
|
55
|
-
:count => 1
|
|
56
|
-
},
|
|
57
|
-
:short => {
|
|
58
|
-
:units => {
|
|
59
|
-
:seconds => 'sec',
|
|
60
|
-
:minutes => 'min',
|
|
61
|
-
:hours => 'hr',
|
|
62
|
-
:days => 'd',
|
|
63
|
-
:weeks => 'wk',
|
|
64
|
-
:months => 'mn',
|
|
65
|
-
:years => 'yr',
|
|
66
|
-
:centuries => 'ct',
|
|
67
|
-
:millenia => 'ml'
|
|
68
|
-
},
|
|
69
|
-
:separator => '',
|
|
70
|
-
:delimiter => ' ',
|
|
71
|
-
:count => 2
|
|
72
|
-
},
|
|
73
|
-
:long => {
|
|
74
|
-
:units => {
|
|
75
|
-
:seconds => ['second', 'seconds'],
|
|
76
|
-
:minutes => ['minute', 'minutes'],
|
|
77
|
-
:hours => ['hour', 'hours'],
|
|
78
|
-
:days => ['day', 'days'],
|
|
79
|
-
:weeks => ['week', 'weeks'],
|
|
80
|
-
:months => ['month', 'months'],
|
|
81
|
-
:years => ['year', 'years'],
|
|
82
|
-
:centuries => ['century', 'centuries'],
|
|
83
|
-
:millennia => ['millenium', 'millenia'],
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
# All potential units. Key is the unit name, and the value is its plural form.
|
|
89
|
-
def self.units
|
|
90
|
-
@@units
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
# Unit values in seconds. If a unit is not present in this hash, it is assumed to be in the {@@in_months} hash.
|
|
94
|
-
def self.units_in_seconds
|
|
95
|
-
@@in_seconds
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
# Unit values in months. If a unit is not present in this hash, it is assumed to be in the {@@in_seconds} hash.
|
|
99
|
-
def self.units_in_months
|
|
100
|
-
@@in_months
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
# Initialize a new instance of RelativeTime.
|
|
104
|
-
# @overload new(hash)
|
|
105
|
-
# @param [Hash] units The base units to initialize with
|
|
106
|
-
# @option units [Integer] :seconds The number of seconds
|
|
107
|
-
# @option units [Integer] :months The number of months
|
|
108
|
-
# @overload new(count, unit)
|
|
109
|
-
# @param [Integer] count The number of units to initialize with
|
|
110
|
-
# @param [Symbol] unit The unit to initialize. See {RelativeTime#units}
|
|
111
|
-
def initialize(count = 0, unit = :second)
|
|
112
|
-
if count.is_a? Hash
|
|
113
|
-
units = count
|
|
114
|
-
units.default = 0
|
|
115
|
-
@seconds, @months = units.values_at(:seconds, :months)
|
|
116
|
-
else
|
|
117
|
-
@seconds = @months = 0
|
|
118
|
-
|
|
119
|
-
if @@in_seconds.has_key?(unit)
|
|
120
|
-
@seconds = count * @@in_seconds.fetch(unit)
|
|
121
|
-
elsif @@in_months.has_key?(unit)
|
|
122
|
-
@months = count * @@in_months.fetch(unit)
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
# Compares two RelativeTimes to determine if they are equal
|
|
128
|
-
# @param [RelativeTime] time The RelativeTime to compare
|
|
129
|
-
# @return [Boolean] True if both RelativeTimes are equal
|
|
130
|
-
# @note Be weary of rounding; this method compares both RelativeTimes' base units
|
|
131
|
-
def ==(time)
|
|
132
|
-
if time.is_a?(RelativeTime)
|
|
133
|
-
@seconds == time.get(:seconds) && @months == time.get(:months)
|
|
134
|
-
else
|
|
135
|
-
false
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
# Return the number of base units in a RelativeTime.
|
|
140
|
-
# @param [Symbol] unit The unit to return, either :seconds or :months
|
|
141
|
-
# @return [Integer] The requested unit count
|
|
142
|
-
# @raise [ArgumentError] Unit requested was not :seconds or :months
|
|
143
|
-
def get(unit)
|
|
144
|
-
if unit == :seconds
|
|
145
|
-
@seconds
|
|
146
|
-
elsif unit == :months
|
|
147
|
-
@months
|
|
148
|
-
else
|
|
149
|
-
raise ArgumentError
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
# Determines the time between RelativeTime and the given time.
|
|
154
|
-
# @param [Time] time The initial time.
|
|
155
|
-
# @return [Time] The difference between the current RelativeTime and the given time
|
|
156
|
-
# @example 5 hours before January 1st, 2000 at noon
|
|
157
|
-
# 5.minutes.before(Time.new(2000, 1, 1, 12, 00, 00))
|
|
158
|
-
# => 2000-01-01 11:55:00 -0800
|
|
159
|
-
# @see #ago
|
|
160
|
-
# @see #after
|
|
161
|
-
# @see #from_now
|
|
162
|
-
def before(time)
|
|
163
|
-
time = time.to_time - @seconds
|
|
164
|
-
|
|
165
|
-
new_month = time.month - self.months
|
|
166
|
-
new_year = time.year - self.years
|
|
167
|
-
while new_month < 1
|
|
168
|
-
new_month += 12
|
|
169
|
-
new_year -= 1
|
|
170
|
-
end
|
|
171
|
-
if Date.valid_date?(new_year, new_month, time.day)
|
|
172
|
-
new_day = time.day
|
|
173
|
-
else
|
|
174
|
-
new_day = Date.new(new_year, new_month).days_in_month
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
new_time = Time.new(
|
|
178
|
-
new_year, new_month, new_day,
|
|
179
|
-
time.hour, time.min, time.sec
|
|
180
|
-
)
|
|
181
|
-
Time.at(new_time.to_i, time.nsec/1000)
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
# Return the time between the RelativeTime and the current time.
|
|
185
|
-
# @return [Time] The difference between the current RelativeTime and Time#now
|
|
186
|
-
# @see #before
|
|
187
|
-
def ago
|
|
188
|
-
self.before(Time.now)
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
# Return the time after the given time according to the current RelativeTime.
|
|
192
|
-
# @param [Time] time The starting time
|
|
193
|
-
# @return [Time] The time after the current RelativeTime and the given time
|
|
194
|
-
# @see #before
|
|
195
|
-
def after(time)
|
|
196
|
-
time = time.to_time + @seconds
|
|
197
|
-
|
|
198
|
-
new_year = time.year + self.years
|
|
199
|
-
new_month = time.month + self.months
|
|
200
|
-
while new_month > 12
|
|
201
|
-
new_year += 1
|
|
202
|
-
new_month -= 12
|
|
203
|
-
end
|
|
204
|
-
if Date.valid_date?(new_year, new_month, time.day)
|
|
205
|
-
new_day = time.day
|
|
206
|
-
else
|
|
207
|
-
new_day = Date.new(new_year, new_month).days_in_month
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
new_time = Time.new(
|
|
212
|
-
new_year, new_month, new_day,
|
|
213
|
-
time.hour, time.min, time.sec
|
|
214
|
-
)
|
|
215
|
-
Time.at(new_time.to_i, time.nsec/1000.0)
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
# Return the time after the current time and the RelativeTime.
|
|
219
|
-
# @return [Time] The time after the current time
|
|
220
|
-
def from_now
|
|
221
|
-
self.after(Time.now)
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
@@units.each do |unit, plural|
|
|
225
|
-
in_method = "in_#{plural}"
|
|
226
|
-
count_method = plural
|
|
227
|
-
superior_unit = @@units.keys.index(unit) + 1
|
|
228
|
-
|
|
229
|
-
if @@in_seconds.has_key? unit
|
|
230
|
-
class_eval "
|
|
231
|
-
def #{in_method}
|
|
232
|
-
@seconds / #{@@in_seconds[unit]}
|
|
233
|
-
end
|
|
234
|
-
"
|
|
235
|
-
elsif @@in_months.has_key? unit
|
|
236
|
-
class_eval "
|
|
237
|
-
def #{in_method}
|
|
238
|
-
@months / #{@@in_months[unit]}
|
|
239
|
-
end
|
|
240
|
-
"
|
|
241
|
-
end
|
|
242
|
-
|
|
243
|
-
in_superior = "in_#{@@units.values[superior_unit]}"
|
|
244
|
-
count_superior = @@units.keys[superior_unit]
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
class_eval "
|
|
248
|
-
def #{count_method}
|
|
249
|
-
time = self.#{in_method}
|
|
250
|
-
if @@units.length > #{superior_unit}
|
|
251
|
-
time -= self.#{in_superior}.#{count_superior}.#{in_method}
|
|
252
|
-
end
|
|
253
|
-
time
|
|
254
|
-
end
|
|
255
|
-
"
|
|
256
|
-
end
|
|
257
|
-
|
|
258
|
-
# Average second-based units to month-based units.
|
|
259
|
-
# @return [RelativeTime] The averaged RelativeTime
|
|
260
|
-
# @example
|
|
261
|
-
# 5.weeks.average
|
|
262
|
-
# => 1 month, 4 days, 13 hours, 30 minutes, 54 seconds
|
|
263
|
-
# @see #average!
|
|
264
|
-
# @see #unaverage
|
|
265
|
-
def average
|
|
266
|
-
if @seconds > 0
|
|
267
|
-
months = (@seconds / @@average_seconds[:month])
|
|
268
|
-
seconds = @seconds - months.months.unaverage.get(:seconds)
|
|
269
|
-
RelativeTime.new(
|
|
270
|
-
:seconds => seconds,
|
|
271
|
-
:months => months + @months
|
|
272
|
-
)
|
|
273
|
-
else
|
|
274
|
-
self
|
|
275
|
-
end
|
|
276
|
-
end
|
|
277
|
-
|
|
278
|
-
# Destructively average second-based units to month-based units.
|
|
279
|
-
# @see #average
|
|
280
|
-
def average!
|
|
281
|
-
averaged = self.average
|
|
282
|
-
@seconds = averaged.get(:seconds)
|
|
283
|
-
@months = averaged.get(:months)
|
|
284
|
-
self
|
|
285
|
-
end
|
|
286
|
-
|
|
287
|
-
# Average month-based units to second-based units.
|
|
288
|
-
# @return [RelativeTime] the unaveraged RelativeTime.
|
|
289
|
-
# @example
|
|
290
|
-
# 1.month.unaverage
|
|
291
|
-
# => 4 weeks, 2 days, 10 hours, 29 minutes, 6 seconds
|
|
292
|
-
# @see #average
|
|
293
|
-
# @see #unaverage!
|
|
294
|
-
def unaverage
|
|
295
|
-
seconds = @@average_seconds[:month] * @months
|
|
296
|
-
seconds += @seconds
|
|
297
|
-
RelativeTime.new(:seconds => seconds)
|
|
298
|
-
end
|
|
299
|
-
|
|
300
|
-
# Destructively average month-based units to second-based units.
|
|
301
|
-
# @see #unaverage
|
|
302
|
-
def unaverage!
|
|
303
|
-
unaveraged = self.average
|
|
304
|
-
@seconds = unaverage.get(:seconds)
|
|
305
|
-
@months = unaverage.get(:months)
|
|
306
|
-
self
|
|
307
|
-
end
|
|
308
|
-
|
|
309
|
-
# Add two {RelativeTime}s together.
|
|
310
|
-
# @raise ArgumentError Argument isn't a {RelativeTime}
|
|
311
|
-
# @see #-
|
|
312
|
-
def +(time)
|
|
313
|
-
raise ArgumentError unless time.is_a?(RelativeTime)
|
|
314
|
-
RelativeTime.new({
|
|
315
|
-
:seconds => @seconds + time.get(:seconds),
|
|
316
|
-
:months => @months + time.get(:months)
|
|
317
|
-
})
|
|
318
|
-
end
|
|
319
|
-
|
|
320
|
-
# Find the difference between two {RelativeTime}s.
|
|
321
|
-
# @raise ArgumentError Argument isn't a {RelativeTime}
|
|
322
|
-
# @see #+
|
|
323
|
-
def -(time)
|
|
324
|
-
raise ArgumentError unless time.is_a?(RelativeTime)
|
|
325
|
-
RelativeTime.new({
|
|
326
|
-
:seconds => @seconds - time.get(:seconds),
|
|
327
|
-
:months => @months - time.get(:months)
|
|
328
|
-
})
|
|
329
|
-
end
|
|
330
|
-
|
|
331
|
-
# Converts {RelativeTime} to {WallClock}
|
|
332
|
-
# @return [WallClock] {RelativeTime} as {WallClock}
|
|
333
|
-
# @example
|
|
334
|
-
# (17.hours 30.minutes).to_wall
|
|
335
|
-
# # => 5:30:00 PM
|
|
336
|
-
def to_wall
|
|
337
|
-
raise WallClock::TimeOutOfBoundsError if @months > 0
|
|
338
|
-
WallClock.new(:second => @seconds)
|
|
339
|
-
end
|
|
340
|
-
|
|
341
|
-
# Convert {RelativeTime} to a human-readable format.
|
|
342
|
-
# @overload to_s(syntax)
|
|
343
|
-
# @param [Symbol] syntax The syntax from @@syntaxes to use
|
|
344
|
-
# @overload to_s(hash)
|
|
345
|
-
# @param [Hash] hash The custom hash to use
|
|
346
|
-
# @option hash [Hash] :units The unit names to use. See @@syntaxes for examples
|
|
347
|
-
# @option hash [Integer] :count The maximum number of units to output. `1` would output only the unit of greatest example (such as the hour value in `1.hour 3.minutes 2.seconds`).
|
|
348
|
-
# @option hash [String] :separator The separator to use in between a unit and its value
|
|
349
|
-
# @option hash [String] :delimiter The delimiter to use in between different unit-value pairs
|
|
350
|
-
# @example
|
|
351
|
-
# (14.months 49.hours).to_s
|
|
352
|
-
# => 2 years, 2 months, 3 days, 1 hour
|
|
353
|
-
# (1.day 3.hours 4.minutes).to_s(:short)
|
|
354
|
-
# => 1d 3hr
|
|
355
|
-
# @raise KeyError Symbol argument isn't in @@syntaxes
|
|
356
|
-
# @raise ArgumentError Argument isn't a hash (if not a symbol)
|
|
357
|
-
# @see @@syntaxes
|
|
358
|
-
def to_s(syntax = :long)
|
|
359
|
-
if syntax.is_a? Symbol
|
|
360
|
-
syntax = @@syntaxes.fetch(syntax)
|
|
361
|
-
end
|
|
362
|
-
|
|
363
|
-
raise ArgumentError unless syntax.is_a? Hash
|
|
364
|
-
times = []
|
|
365
|
-
|
|
366
|
-
if syntax[:count].nil? || syntax[:count] == :all
|
|
367
|
-
syntax[:count] = @@units.count
|
|
368
|
-
end
|
|
369
|
-
units = syntax.fetch(:units)
|
|
370
|
-
|
|
371
|
-
count = 0
|
|
372
|
-
units = Hash[units.to_a.reverse]
|
|
373
|
-
units.each do |unit, (singular, plural)|
|
|
374
|
-
if count < syntax.fetch(:count)
|
|
375
|
-
time = self.respond_to?(unit) ? self.send(unit) : 0
|
|
376
|
-
|
|
377
|
-
if time > 1 && !plural.nil?
|
|
378
|
-
times << [time, plural]
|
|
379
|
-
count += 1
|
|
380
|
-
elsif time > 0
|
|
381
|
-
times << [time, singular]
|
|
382
|
-
count += 1
|
|
383
|
-
end
|
|
384
|
-
end
|
|
385
|
-
end
|
|
386
|
-
|
|
387
|
-
times.map do |time|
|
|
388
|
-
time.join(syntax[:separator] || ' ')
|
|
389
|
-
end.join(syntax[:delimiter] || ', ')
|
|
390
|
-
end
|
|
391
|
-
end
|
|
392
|
-
|
|
393
|
-
# Represents a time, but not a date. '`7:00 PM`' would be an example of a WallClock object
|
|
394
|
-
class WallClock
|
|
395
|
-
# Represents an error where an invalid meridiem was passed to WallClock.
|
|
396
|
-
# @see #new
|
|
397
|
-
class InvalidMeridiemError < ArgumentError; end
|
|
398
|
-
# Represents an error where a time beyond 24 hours was passed to WallClock.
|
|
399
|
-
# @see #new
|
|
400
|
-
class TimeOutOfBoundsError < ArgumentError; end
|
|
401
|
-
|
|
402
|
-
# Initialize a new instance of WallClock
|
|
403
|
-
# @overload new(hash)
|
|
404
|
-
# @param [Hash] units The units to initialize with
|
|
405
|
-
# @option units [Integer] :hour The hour to initialize with
|
|
406
|
-
# @option units [Integer] :minute The minute to initialize with
|
|
407
|
-
# @option units [Integer] :second The second to initialize with
|
|
408
|
-
# @overload new(hour, minute, meridiem)
|
|
409
|
-
# @param [Integer] hour The hour to initialize with
|
|
410
|
-
# @param [Integer] minute The minute to initialize with
|
|
411
|
-
# @param [Symbol] meridiem The meridiem to initialize with (`:am` or `:pm`)
|
|
412
|
-
# @overload new(hour, minute, second, meridiem)
|
|
413
|
-
# @param [Integer] hour The hour to initialize with
|
|
414
|
-
# @param [Integer] minute The minute to initialize with
|
|
415
|
-
# @param [Integer] second The second to initialize with
|
|
416
|
-
# @param [Symbol] meridiem The meridiem to initialize with (`:am` or `:pm`)
|
|
417
|
-
# @overload new(seconds)
|
|
418
|
-
# @param [Integer] seconds The number of seconds to initialize with (for use with #to_i)
|
|
419
|
-
# @raise InvalidMeridiemError Meridiem is not `:am` or `:pm`
|
|
420
|
-
def initialize(hour = nil, minute = nil, second = 0, meridiem = :am)
|
|
421
|
-
units = nil
|
|
422
|
-
if hour.is_a?(Integer) && minute.nil?
|
|
423
|
-
units = {:second => hour}
|
|
424
|
-
elsif hour.is_a?(Hash)
|
|
425
|
-
units = hour
|
|
426
|
-
end
|
|
427
|
-
|
|
428
|
-
if !units.nil?
|
|
429
|
-
second = units[:second] || 0
|
|
430
|
-
minute = units[:minute] || 0
|
|
431
|
-
hour = units[:hour] || 0
|
|
432
|
-
else
|
|
433
|
-
if second.is_a?(String) || second.is_a?(Symbol)
|
|
434
|
-
meridiem = second
|
|
435
|
-
second = 0
|
|
436
|
-
end
|
|
437
|
-
|
|
438
|
-
meridiem = meridiem.downcase.to_sym
|
|
439
|
-
if !(meridiem == :am || meridiem == :pm)
|
|
440
|
-
raise InvalidMeridiemError
|
|
441
|
-
elsif meridiem == :pm && hour > 12
|
|
442
|
-
raise TimeOutOfBoundsError, "hour must be <= 12 for PM"
|
|
443
|
-
elsif hour >= 24 || minute >= 60 || second >= 60
|
|
444
|
-
raise TimeOutOfBoundsError
|
|
445
|
-
end
|
|
446
|
-
|
|
447
|
-
hour += 12 if (meridiem == :pm and !(hour == 12))
|
|
448
|
-
end
|
|
449
|
-
|
|
450
|
-
@seconds =
|
|
451
|
-
RelativeTime.units_in_seconds.fetch(:hour) * hour +
|
|
452
|
-
RelativeTime.units_in_seconds.fetch(:minute) * minute +
|
|
453
|
-
second
|
|
454
|
-
|
|
455
|
-
if @seconds >= RelativeTime.units_in_seconds.fetch(:day)
|
|
456
|
-
raise TimeOutOfBoundsError
|
|
457
|
-
end
|
|
458
|
-
end
|
|
459
|
-
|
|
460
|
-
# Takes a string and turns it into a WallClock time
|
|
461
|
-
# @param [String] string The string to convert
|
|
462
|
-
# @return [WallClock] The time as a WallClock
|
|
463
|
-
# @example
|
|
464
|
-
# WallClock.from_string("10:30 PM")
|
|
465
|
-
# # => 10:30:00 PM
|
|
466
|
-
# WallClock.from_string("13:01:23")
|
|
467
|
-
# # => 1:01:23 PM
|
|
468
|
-
# @see #to_s
|
|
469
|
-
def self.from_string(string)
|
|
470
|
-
time, meridiem = string.split(' ', 2)
|
|
471
|
-
hour, minute, second = time.split(':', 3)
|
|
472
|
-
WallClock.new(hour.to_i, minute.to_i, second.to_i || 0, meridiem || :am)
|
|
473
|
-
end
|
|
474
|
-
|
|
475
|
-
# Returns the time of the WallClock on a date
|
|
476
|
-
# @param [Date] date The date to apply the time on
|
|
477
|
-
# @return [Time] The time after the given date
|
|
478
|
-
# @example yesterday at 5:00
|
|
479
|
-
# time = WallClock.new(5, 00, :pm)
|
|
480
|
-
# time.on(Date.yesterday)
|
|
481
|
-
# => 2000-1-1 17:00:00 -0800
|
|
482
|
-
def on(date)
|
|
483
|
-
date.to_date.to_time + @seconds
|
|
484
|
-
end
|
|
485
|
-
|
|
486
|
-
# Comparse two {WallClock}s.
|
|
487
|
-
# @return [Boolean] True if the WallClocks are identical
|
|
488
|
-
def ==(time)
|
|
489
|
-
if time.is_a? WallClock
|
|
490
|
-
self.in_seconds == time.in_seconds
|
|
491
|
-
else
|
|
492
|
-
false
|
|
493
|
-
end
|
|
494
|
-
end
|
|
495
|
-
|
|
496
|
-
# Get the time of the WallClock, in seconds
|
|
497
|
-
# @return [Integer] The total time of the WallClock, in seconds
|
|
498
|
-
def in_seconds
|
|
499
|
-
@seconds
|
|
500
|
-
end
|
|
501
|
-
|
|
502
|
-
# Get the time of the WallClock, in minutes
|
|
503
|
-
# @return [Integer] The total time of the WallClock, in minutes
|
|
504
|
-
def in_minutes
|
|
505
|
-
@seconds / RelativeTime.units_in_seconds[:minute]
|
|
506
|
-
end
|
|
507
|
-
|
|
508
|
-
# Get the time of the WallClock, in hours
|
|
509
|
-
# @return [Integer] The total time of the WallClock, in hours
|
|
510
|
-
def in_hours
|
|
511
|
-
@seconds / RelativeTime.units_in_seconds[:hour]
|
|
512
|
-
end
|
|
513
|
-
|
|
514
|
-
# Get the second of the WallClock.
|
|
515
|
-
# @return [Integer] The second component of the WallClock
|
|
516
|
-
def second
|
|
517
|
-
self.to_relative.seconds
|
|
518
|
-
end
|
|
519
|
-
|
|
520
|
-
# Get the minute of the WallClock.
|
|
521
|
-
# @return [Integer] The minute component of the WallClock
|
|
522
|
-
def minute
|
|
523
|
-
self.to_relative.minutes
|
|
524
|
-
end
|
|
525
|
-
|
|
526
|
-
# Get the hour of the WallClock.
|
|
527
|
-
# @param [Symbol] system The houring system to use (either `:twelve_hour` or `:twenty_four_hour`; default `:twenty_four_hour`)
|
|
528
|
-
# @return [Integer] The hour component of the WallClock
|
|
529
|
-
def hour(system = :twenty_four_hour)
|
|
530
|
-
hour = self.to_relative.hours
|
|
531
|
-
if system == :twelve_hour
|
|
532
|
-
if hour == 0
|
|
533
|
-
12
|
|
534
|
-
elsif hour > 12
|
|
535
|
-
hour - 12
|
|
536
|
-
else
|
|
537
|
-
hour
|
|
538
|
-
end
|
|
539
|
-
elsif (system == :twenty_four_hour)
|
|
540
|
-
hour
|
|
541
|
-
else
|
|
542
|
-
raise ArgumentError, "system should be :twelve_hour or :twenty_four_hour"
|
|
543
|
-
end
|
|
544
|
-
end
|
|
545
|
-
|
|
546
|
-
# Get the meridiem of the WallClock.
|
|
547
|
-
# @return [Symbol] The meridiem (either `:am` or `:pm`)
|
|
548
|
-
def meridiem
|
|
549
|
-
if self.hour > 12 || self.hour == 0
|
|
550
|
-
:pm
|
|
551
|
-
else
|
|
552
|
-
:am
|
|
553
|
-
end
|
|
554
|
-
end
|
|
555
|
-
|
|
556
|
-
# Converts self to {WallClock}
|
|
557
|
-
# @see Time#to_wall
|
|
558
|
-
def to_wall
|
|
559
|
-
self
|
|
560
|
-
end
|
|
561
|
-
|
|
562
|
-
# Converts {WallClock} to {RelativeTime}
|
|
563
|
-
# @return [RelativeTime] {WallClock} as {RelativeTime}
|
|
564
|
-
# @example
|
|
565
|
-
# time = WallClock.new(5, 30, :pm)
|
|
566
|
-
# time.to_relative
|
|
567
|
-
# => 5 hours, 30 minutes
|
|
568
|
-
def to_relative
|
|
569
|
-
@seconds.seconds
|
|
570
|
-
end
|
|
571
|
-
|
|
572
|
-
# Get the time of the WallClock in a more portable format (for a database, for example)
|
|
573
|
-
# @see #in_seconds
|
|
574
|
-
def to_i
|
|
575
|
-
self.in_seconds
|
|
576
|
-
end
|
|
577
|
-
|
|
578
|
-
# Convert {WallClock} to a human-readable format.
|
|
579
|
-
# @param [Symbol] system The hour system to use (`:twelve_hour` or `:twenty_four_hour`; default `:twelve_hour`)
|
|
580
|
-
# @param [Hash] options Extra options for the string to use
|
|
581
|
-
# @option options [Boolean] :use_seconds Whether or not to include seconds in the conversion to a string
|
|
582
|
-
# @option options [Boolean] :include_meridian Whether or not to include the meridian for a twelve-hour time
|
|
583
|
-
# @example
|
|
584
|
-
# time = WallClock.new(5, 37, 41, :pm)
|
|
585
|
-
# time.to_s
|
|
586
|
-
# => "5:37:41 PM"
|
|
587
|
-
# time.to_s(:twenty_four_hour, :use_seconds => true)
|
|
588
|
-
# => "17:37:41"
|
|
589
|
-
# time.to_s(:twelve_hour, :use_seconds => false, :include_meridiem => false)
|
|
590
|
-
# => "5:37"
|
|
591
|
-
# time.to_s(:twenty_four_hour, :use_seconds =>false)
|
|
592
|
-
# => "17:37"
|
|
593
|
-
# @raise ArgumentError Argument isn't a proper system
|
|
594
|
-
def to_s(system = :twelve_hour, options = {})
|
|
595
|
-
options = {:use_seconds => true, :include_meridiem => true}.merge(options)
|
|
596
|
-
pad = "%02d"
|
|
597
|
-
meridiem = self.meridiem.to_s.upcase
|
|
598
|
-
hour = self.hour(system)
|
|
599
|
-
minute = pad % self.minute
|
|
600
|
-
second = pad % self.second
|
|
601
|
-
|
|
602
|
-
string = [hour, minute].join(':')
|
|
603
|
-
if options[:use_seconds]
|
|
604
|
-
string = [string, second].join(':')
|
|
605
|
-
end
|
|
606
|
-
|
|
607
|
-
case system
|
|
608
|
-
when :twelve_hour
|
|
609
|
-
options[:include_meridiem] ? [string, meridiem].join(' ') : string
|
|
610
|
-
when :twenty_four_hour
|
|
611
|
-
string
|
|
612
|
-
else
|
|
613
|
-
raise ArgumentError, "system should be :twelve_hour or :twenty_four_hour"
|
|
614
|
-
end
|
|
615
|
-
end
|
|
616
|
-
end
|
|
617
|
-
|
|
618
|
-
# {Time} class monkeywrenched with {RelativeTime} support.
|
|
4
|
+
# {Time} class monkey-patched with {Timerizer::Duration} support.
|
|
619
5
|
class Time
|
|
620
6
|
# Represents an error where two times were expected to be in the future, but were in the past.
|
|
621
7
|
# @see #until
|
|
@@ -650,7 +36,7 @@ class Time
|
|
|
650
36
|
|
|
651
37
|
alias_method :add, :+
|
|
652
38
|
def +(time)
|
|
653
|
-
if time.is_a?
|
|
39
|
+
if time.is_a?(Timerizer::Duration)
|
|
654
40
|
time.after(self)
|
|
655
41
|
else
|
|
656
42
|
self.add(time)
|
|
@@ -659,7 +45,7 @@ class Time
|
|
|
659
45
|
|
|
660
46
|
alias_method :subtract, :-
|
|
661
47
|
def -(time)
|
|
662
|
-
if time.is_a?
|
|
48
|
+
if time.is_a?(Timerizer::Duration)
|
|
663
49
|
time.before(self)
|
|
664
50
|
else
|
|
665
51
|
self.subtract(time)
|
|
@@ -668,7 +54,7 @@ class Time
|
|
|
668
54
|
|
|
669
55
|
# Calculates the time until a given time
|
|
670
56
|
# @param [Time] time The time until now to calculate
|
|
671
|
-
# @return [
|
|
57
|
+
# @return [Duration] The time until the provided time
|
|
672
58
|
# @raise[TimeIsInThePastException] The provided time is in the past
|
|
673
59
|
# @example
|
|
674
60
|
# Time.until(Time.new(2012, 12, 25))
|
|
@@ -683,7 +69,7 @@ class Time
|
|
|
683
69
|
|
|
684
70
|
# Calculates the time since a given time
|
|
685
71
|
# @param [Time] time The time to calculate since now
|
|
686
|
-
# @return [
|
|
72
|
+
# @return [Duration] The time since the provided time
|
|
687
73
|
# @raise[TimeIsInTheFutureException] The provided time is in the future
|
|
688
74
|
# @example
|
|
689
75
|
# Time.since(Time.new(2011, 10, 31))
|
|
@@ -699,7 +85,7 @@ class Time
|
|
|
699
85
|
# Calculate the amount of time between two times.
|
|
700
86
|
# @param [Time] time1 The initial time
|
|
701
87
|
# @param [Time] time2 The final time
|
|
702
|
-
# @return [
|
|
88
|
+
# @return [Duration] Calculated time between time1 and time2
|
|
703
89
|
# @example
|
|
704
90
|
# Time.between(1.minute.ago, 1.hour.ago)
|
|
705
91
|
# => 59.minutes
|
|
@@ -709,7 +95,7 @@ class Time
|
|
|
709
95
|
def self.between(time1, time2)
|
|
710
96
|
time_between = (time2.to_time - time1.to_time).abs
|
|
711
97
|
|
|
712
|
-
|
|
98
|
+
Timerizer::Duration.new(seconds: time_between.round)
|
|
713
99
|
end
|
|
714
100
|
|
|
715
101
|
# Convert {Time} to {Date}.
|
|
@@ -727,19 +113,19 @@ class Time
|
|
|
727
113
|
self
|
|
728
114
|
end
|
|
729
115
|
|
|
730
|
-
# Converts {Time} to {WallClock}
|
|
731
|
-
# @return [WallClock] {Time} as {WallClock}
|
|
116
|
+
# Converts {Time} to {Timerizer::WallClock}
|
|
117
|
+
# @return [Timerizer::WallClock] {Time} as {Timerizer::WallClock}
|
|
732
118
|
# @example
|
|
733
119
|
# time = Time.now.to_wall
|
|
734
120
|
# Date.tomorrow.at(time)
|
|
735
121
|
# => 2000-1-2 13:13:27 -0800
|
|
736
122
|
# # "Same time tomorrow?"
|
|
737
123
|
def to_wall
|
|
738
|
-
WallClock.new(self.hour, self.min, self.sec)
|
|
124
|
+
Timerizer::WallClock.new(self.hour, self.min, self.sec)
|
|
739
125
|
end
|
|
740
126
|
end
|
|
741
127
|
|
|
742
|
-
# {Date} class
|
|
128
|
+
# {Date} class monkey-patched with {Timerizer::Duration} helpers.
|
|
743
129
|
class Date
|
|
744
130
|
# Return the number of days in a given month.
|
|
745
131
|
# @return [Integer] Number of days in the month of the {Date}.
|
|
@@ -783,19 +169,66 @@ class Date
|
|
|
783
169
|
end
|
|
784
170
|
end
|
|
785
171
|
|
|
786
|
-
#
|
|
172
|
+
# Monkey-patched {Integer} class enabled to return {Timerizer::Duration}s.
|
|
787
173
|
# @example
|
|
788
174
|
# 5.minutes
|
|
789
|
-
#
|
|
790
|
-
# @see
|
|
175
|
+
# # => 5 minutes
|
|
176
|
+
# @see Timerizer::Duration::UNITS
|
|
791
177
|
class Integer
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
178
|
+
private
|
|
179
|
+
|
|
180
|
+
# @!macro [attach] _define_duration_unit
|
|
181
|
+
# @method $1(other = nil)
|
|
182
|
+
#
|
|
183
|
+
# Return a {Timerizer::Duration} with `self` of the given unit. This
|
|
184
|
+
# method is a helper that is equivalent to
|
|
185
|
+
# `Timerizer::Duration::new($1: self)`.
|
|
186
|
+
#
|
|
187
|
+
# @param [Timerizer::Duration, nil] other Another duration to add to the
|
|
188
|
+
# resulting duration, if present. This argument allows "chaining" multiple
|
|
189
|
+
# durations together, to combine multiple units succiently.
|
|
190
|
+
#
|
|
191
|
+
# @return [Timerizer::Duration] the quantity of the unit in the duration.
|
|
192
|
+
#
|
|
193
|
+
# @see Timerizer::Duration#initialize
|
|
194
|
+
#
|
|
195
|
+
# @example
|
|
196
|
+
# n.$1 == Timerizer::Duration.new($1: n)
|
|
197
|
+
# 5.minutes == Timerizer::Duration.new(minutes: 5)
|
|
198
|
+
# (1.week 1.day) == 8.days # "Chaining" multiple units
|
|
199
|
+
# (n.$1 x.minutes) == (n.$1 + x.minutes)
|
|
200
|
+
def self._define_duration_unit(unit)
|
|
201
|
+
define_method(unit) do |other = nil|
|
|
202
|
+
duration = Timerizer::Duration.new(unit => self)
|
|
203
|
+
|
|
204
|
+
if other.nil?
|
|
205
|
+
duration
|
|
206
|
+
else
|
|
207
|
+
duration + other
|
|
797
208
|
end
|
|
798
|
-
|
|
799
|
-
alias_method(plural, unit)
|
|
209
|
+
end
|
|
800
210
|
end
|
|
211
|
+
|
|
212
|
+
public
|
|
213
|
+
|
|
214
|
+
self._define_duration_unit(:seconds)
|
|
215
|
+
self._define_duration_unit(:minutes)
|
|
216
|
+
self._define_duration_unit(:hours)
|
|
217
|
+
self._define_duration_unit(:days)
|
|
218
|
+
self._define_duration_unit(:weeks)
|
|
219
|
+
self._define_duration_unit(:months)
|
|
220
|
+
self._define_duration_unit(:years)
|
|
221
|
+
self._define_duration_unit(:decades)
|
|
222
|
+
self._define_duration_unit(:centuries)
|
|
223
|
+
self._define_duration_unit(:millennia)
|
|
224
|
+
self._define_duration_unit(:second)
|
|
225
|
+
self._define_duration_unit(:minute)
|
|
226
|
+
self._define_duration_unit(:hour)
|
|
227
|
+
self._define_duration_unit(:day)
|
|
228
|
+
self._define_duration_unit(:week)
|
|
229
|
+
self._define_duration_unit(:month)
|
|
230
|
+
self._define_duration_unit(:year)
|
|
231
|
+
self._define_duration_unit(:decade)
|
|
232
|
+
self._define_duration_unit(:century)
|
|
233
|
+
self._define_duration_unit(:millennium)
|
|
801
234
|
end
|