richunits 0.2.0 → 0.5.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.
@@ -0,0 +1,113 @@
1
+ # TITLE:
2
+ #
3
+ # Multipliers
4
+ #
5
+ # DESCRIPTION:
6
+ #
7
+ # Adds methods to Numeric to make working with
8
+ # magnitudes (kilo, mega, giga, milli, micro, etc.)
9
+ # as well as bits and bytes easier.
10
+ #
11
+ # COPYRIGHT:
12
+ #
13
+ # Copyright (c) 2005 Thomas Sawyer
14
+ #
15
+ # LICENSE:
16
+ #
17
+ # Ruby License
18
+ #
19
+ # This module is free software. You may use, modify, and/or redistribute this
20
+ # software under the same terms as Ruby.
21
+ #
22
+ # This program is distributed in the hope that it will be useful, but WITHOUT
23
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
24
+ # FOR A PARTICULAR PURPOSE.
25
+ #
26
+ # HISTORY:
27
+ #
28
+ # Thanks to Rich Kilmer and bytes.rb which inspired this library.
29
+ #
30
+ # AUTHORS:
31
+ #
32
+ # - Thomas Sawyer
33
+ #
34
+ # NOTES:
35
+ #
36
+ # - This library is not compatible with STICK's units.rb (an spin-off
37
+ # of Facets old units.rb library). Do not attempt to use both at the same time.
38
+
39
+ #
40
+ module RichUnits
41
+
42
+ # = Multipliers
43
+ #
44
+ module Multiplers
45
+
46
+ # = Numeric Multipliers
47
+ #
48
+ # Adds methods to Numeric to make working with
49
+ # magnitudes (kilo, mega, giga, milli, micro, etc.)
50
+ # as well as bits and bytes easier.
51
+ #
52
+ # 1.kilo #=> 1000
53
+ # 1.milli #=> 0.001
54
+ # 1.kibi #=> 1024
55
+ #
56
+ # To display a value in a certain denomination, simply
57
+ # perform the inverse operation by placing the
58
+ # multiplier called on unit (1) in the denominator.
59
+ #
60
+ # 1000 / 1.kilo #=> 1
61
+ # 1024 / 1.kibi #=> 1
62
+ #
63
+ module Numeric
64
+
65
+ # SI Multipliers
66
+
67
+ def deka ; self * 10 ; end
68
+ def hecto ; self * 100 ; end
69
+ def kilo ; self * 1000 ; end
70
+ def mega ; self * 1000000 ; end
71
+ def giga ; self * 1000000000 ; end
72
+ def tera ; self * 1000000000000 ; end
73
+ def peta ; self * 1000000000000000 ; end
74
+ def exa ; self * 1000000000000000000 ; end
75
+
76
+ # SI Fractional
77
+
78
+ def deci ; self.to_f / 10 ; end
79
+ def centi ; self.to_f / 100 ; end
80
+ def milli ; self.to_f / 1000 ; end
81
+ def micro ; self.to_f / 1000000 ; end
82
+ def nano ; self.to_f / 1000000000 ; end
83
+ def pico ; self.to_f / 1000000000000 ; end
84
+ def femto ; self.to_f / 1000000000000000 ; end
85
+ def atto ; self.to_f / 1000000000000000000 ; end
86
+
87
+ # SI Binary
88
+
89
+ def kibi ; self * 1024 ; end
90
+ def mebi ; self * 1024**2 ; end
91
+ def gibi ; self * 1024**3 ; end
92
+ def tebi ; self * 1024**4 ; end
93
+ def pebi ; self * 1024**5 ; end
94
+ def exbi ; self * 1024**6 ; end
95
+
96
+ # Bits and Bytes
97
+
98
+ def bit ; self ; end
99
+ def bits ; self ; end
100
+ def byte ; self * 8 ; end
101
+ def bytes ; self * 8 ; end
102
+
103
+ end#module Numeric
104
+
105
+ end#module Multipliers
106
+
107
+ end#module RichUnits
108
+
109
+
110
+ class Numeric #:nodoc:
111
+ include RichUnits::Multiplers::Numeric
112
+ end
113
+
@@ -0,0 +1,491 @@
1
+ # Copyright (c) 2005 Rich Kilmer, Thomas Sawyer
2
+ #
3
+ # Ruby License
4
+ #
5
+ # This module is free software. You may use, modify, and/or redistribute this
6
+ # software under the same terms as Ruby.
7
+ #
8
+ # This program is distributed in the hope that it will be useful, but WITHOUT
9
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
+ # FITNESS FOR A PARTICULAR PURPOSE.
11
+ #
12
+ # HISTORY:
13
+ #
14
+ # Thanks to Richard Kilmer for the orignal work and Alexander Kellett
15
+ # for suggesting it for Facets.
16
+ #
17
+ # Thanks to Dave Hoover and Ryan Platte for the Weekdays implementation.
18
+ #
19
+ # AUTHORS:
20
+ #
21
+ # - Rich Kilmer
22
+ # - Thomas Sawyer
23
+ # - Dave Hoover
24
+ # - Ryan Platte
25
+ # - George Moschovitis
26
+ #
27
+ # NOTES:
28
+ #
29
+ # - This library is not compatible with STICK's units.rb (an spin-off
30
+ # of Facets old units.rb library). Do not attempt to use both at the same time.
31
+ #
32
+ # TODOs:
33
+ #
34
+ # TODO Extra Add in_* methods, like in_days, in_hours, etc. ?
35
+
36
+ #
37
+ module RichUnits
38
+ require 'richunits/weekdays'
39
+ require 'richunits/duration'
40
+
41
+ # = Times
42
+ #
43
+ # Plain-English convenience methods for dealing with dates and times.
44
+ #
45
+ module Times
46
+
47
+ NEVER = ::Time.mktime(2038)
48
+ ZERO = ::Time.mktime(1972)
49
+
50
+ # = Time Extensions
51
+ #
52
+ module Time
53
+
54
+ # Like change but does not reset earlier times.
55
+ #
56
+ # NOTE: It would be better, probably if this were called "change".
57
+ # and that #change were called "reset".
58
+ #
59
+ def set(options)
60
+ opts={}; options.each_pair{ |k,v| opts[k] = v.to_i }
61
+ self.class.send( self.utc? ? :utc : :local,
62
+ opts[:year] || self.year,
63
+ opts[:month] || self.month,
64
+ opts[:day] || self.day,
65
+ opts[:hour] || self.hour,
66
+ opts[:min] || self.min,
67
+ opts[:sec] || self.sec,
68
+ opts[:usec] || self.usec
69
+ )
70
+ end
71
+
72
+ # Returns a new Time where one or more of the elements
73
+ # have been changed according to the +options+ parameter.
74
+ # The time options (hour, minute, sec, usec) reset
75
+ # cascadingly, so if only the hour is passed, then
76
+ # minute, sec, and usec is set to 0. If the hour and
77
+ # minute is passed, then sec and usec is set to 0.
78
+ #
79
+ # t = Time.now #=> Sat Dec 01 14:10:15 -0500 2007
80
+ # t.change(:hour => 11) #=> Sat Dec 01 11:00:00 -0500 2007
81
+ #
82
+ def change(options)
83
+ opts=options; #{}; options.each_pair{ |k,v| opts[k] = v.to_i }
84
+ self.class.send(
85
+ self.utc? ? :utc : :local,
86
+ opts[:year] || self.year,
87
+ opts[:month] || self.month,
88
+ opts[:day] || self.day,
89
+ opts[:hour] || self.hour,
90
+ opts[:min] || (opts[:hour] ? 0 : self.min),
91
+ opts[:sec] || ((opts[:hour] || opts[:min]) ? 0 : self.sec),
92
+ opts[:usec] || ((opts[:hour] || opts[:min] || opts[:sec]) ? 0 : self.usec)
93
+ )
94
+ end
95
+
96
+ # Returns a new Time representing the time
97
+ # a number of time-units ago.
98
+ #
99
+ def ago(number, units=:seconds)
100
+ time =(
101
+ case units.to_s.downcase.to_sym
102
+ when :years
103
+ set(:year => (year - number))
104
+ when :months
105
+ #years = ((month - number) / 12).to_i
106
+ y = ((month - number) / 12).to_i
107
+ m = ((month - number - 1) % 12) + 1
108
+ set(:year => (year - y), :month => m)
109
+ when :weeks
110
+ self - (number * 604800)
111
+ when :days
112
+ self - (number * 86400)
113
+ when :hours
114
+ self - (number * 3600)
115
+ when :minutes
116
+ self - (number * 60)
117
+ when :seconds, nil
118
+ self - number
119
+ else
120
+ raise ArgumentError, "unrecognized time units -- #{units}"
121
+ end
122
+ )
123
+ dst_adjustment(time)
124
+ end
125
+
126
+ #
127
+ # Returns a new Time representing the time
128
+ # a number of time-units hence.
129
+
130
+ def hence(number, units=:seconds)
131
+ time =(
132
+ case units.to_s.downcase.to_sym
133
+ when :years
134
+ set( :year=>(year + number) )
135
+ when :months
136
+ y = ((month + number) / 12).to_i
137
+ m = ((month + number - 1) % 12) + 1
138
+ set(:year => (year + y), :month => m)
139
+ when :weeks
140
+ self + (number * 604800)
141
+ when :days
142
+ self + (number * 86400)
143
+ when :hours
144
+ self + (number * 3600)
145
+ when :minutes
146
+ self + (number * 60)
147
+ when :seconds
148
+ self + number
149
+ else
150
+ raise ArgumentError, "unrecognized time units -- #{units}"
151
+ end
152
+ )
153
+ dst_adjustment(time)
154
+ end
155
+
156
+ alias_method :in, :hence
157
+
158
+ # This is a Railism.
159
+ alias_method :since, :hence
160
+
161
+ # This is a Railism.
162
+ alias_method :from_now, :hence
163
+
164
+ # Adjust DST
165
+ #
166
+ # TODO: Can't seem to get this to pass ActiveSupport tests.
167
+ # Even though it is essentially identical to the
168
+ # ActiveSupport code (see Time#since in time/calculations.rb).
169
+ # It handels all but 4 tests.
170
+ def dst_adjustment(time)
171
+ self_dst = self.dst? ? 1 : 0
172
+ time_dst = time.dst? ? 1 : 0
173
+ seconds = (self - time).abs
174
+ if (seconds >= 86400 && self_dst != time_dst)
175
+ time + ((self_dst - time_dst) * 60 * 60)
176
+ else
177
+ time
178
+ end
179
+ end
180
+
181
+ # Seconds since midnight: Time.now.seconds_since_midnight
182
+
183
+ def seconds_since_midnight
184
+ self.hour.hours + self.min.minutes + self.sec + (self.usec/1.0e+6)
185
+ end
186
+
187
+ # Returns a new Time representing the time a number of seconds ago.
188
+ # Do not use this method in combination with x.months, use months_ago instead!
189
+ #def ago(seconds)
190
+ # # This is basically a wrapper around the Numeric extension.
191
+ # #seconds.until(self)
192
+ # self - seconds
193
+ #end
194
+
195
+ # Returns a new Time representing the time
196
+ # a number of minutes ago.
197
+
198
+ def minutes_ago(minutes)
199
+ self - (minutes * 60)
200
+ end
201
+
202
+ # Returns a new Time representing the time
203
+ # a number of hours ago.
204
+
205
+ def hours_ago(hours)
206
+ self - (hours * 3600)
207
+ end
208
+
209
+ # Returns a new Time representing the time
210
+ # a number of days ago.
211
+
212
+ def days_ago(days)
213
+ self - (days * 86400)
214
+ end
215
+
216
+ # Returns a new Time representing the time
217
+ # a number of weeks ago.
218
+
219
+ def weeks_ago(weeks)
220
+ self - (weeks * 604800)
221
+ end
222
+
223
+ # Returns a new Time representing the time
224
+ # a number of months ago.
225
+
226
+ def months_ago(months)
227
+ years = (month - months / 12).to_i
228
+ set(:year=>(year - years), :month=>(month - months) % 12)
229
+ end
230
+
231
+ # Returns a new Time representing the time a number of specified
232
+ # months ago.
233
+ #def months_ago(months)
234
+ # if months >= self.month
235
+ # change(:year => self.year - 1, :month => 12).months_ago(months - self.month)
236
+ # else
237
+ # change(:year => self.year, :month => self.month - months)
238
+ # end
239
+ #end
240
+
241
+ # Returns a new Time representing the time
242
+ # a number of years ago.
243
+
244
+ def years_ago(years)
245
+ set(:year=>(year - years))
246
+ end
247
+
248
+ # Returns a new Time representing the time a number of specified
249
+ # years ago.
250
+ #def years_ago(years)
251
+ # change(:year => self.year - years)
252
+ #end
253
+
254
+ # Returns a new Time representing the time
255
+ # a number of minutes hence.
256
+
257
+ def minutes_hence(minutes)
258
+ self + (minutes * 60)
259
+ end
260
+
261
+ # Returns a new Time representing the time
262
+ # a number of hours hence.
263
+
264
+ def hours_hence(hours)
265
+ self + (hours * 3600)
266
+ end
267
+
268
+ # Returns a new Time representing the time
269
+ # a number of days hence.
270
+
271
+ def days_hence(days)
272
+ self + (days * 86400)
273
+ end
274
+
275
+ # Returns a new Time representing the time
276
+ # a number of weeks hence.
277
+
278
+ def weeks_hence(weeks)
279
+ self + (weeks * 604800)
280
+ end
281
+
282
+ # Returns a new Time representing the time
283
+ # a number of months hence.
284
+
285
+ def months_hence(months)
286
+ years = (month + months / 12).to_i
287
+ set(:year=>(year + years), :month=>(month + months) % 12)
288
+ end
289
+
290
+ #def months_hence(months)
291
+ # if months + self.month > 12
292
+ # change(:year => self.year + 1, :month => 1).months_since(months - (self.month == 1 ? 12 : (self.month + 1)))
293
+ # else
294
+ # change(:year => self.year, :month => self.month + months)
295
+ # end
296
+ #end
297
+
298
+ # Returns a new Time representing the time
299
+ # a number of years hence.
300
+
301
+ def years_hence(years)
302
+ set(:year=>(year + years))
303
+ end
304
+
305
+ # Returns a new Time representing the time a number of seconds
306
+ # since the instance time. Do not use this method in combination
307
+ # with x.months, use months_since instead!
308
+ alias_method :since, :hence
309
+
310
+ alias_method :minutes_since, :minutes_hence
311
+ alias_method :days_since, :days_hence
312
+ alias_method :weeks_since, :weeks_hence
313
+ alias_method :months_since, :months_hence
314
+ alias_method :years_since, :years_hence
315
+
316
+ #def years_since(years)
317
+ # change(:year => self.year + years)
318
+ #end
319
+
320
+ # Short-hand for years_ago(1)
321
+ def last_year
322
+ years_ago(1)
323
+ end
324
+
325
+ # Short-hand for years_since(1)
326
+ def next_year
327
+ years_since(1)
328
+ end
329
+
330
+ # Short-hand for months_ago(1)
331
+ def last_month
332
+ months_ago(1)
333
+ end
334
+
335
+ # Short-hand for months_since(1)
336
+ def next_month
337
+ months_since(1)
338
+ end
339
+
340
+ # Returns a new Time representing the "start" of this week (Monday, 0:00)
341
+ def beginning_of_week
342
+ (self - self.wday.days).midnight + 1.day
343
+ end
344
+ alias :monday :beginning_of_week
345
+ alias :at_beginning_of_week :beginning_of_week
346
+
347
+ # Returns a new Time representing the start of the given
348
+ # day in next week (default is Monday).
349
+ def next_week(day = :monday)
350
+ days_into_week = { :monday => 0, :tuesday => 1, :wednesday => 2,
351
+ :thursday => 3, :friday => 4, :saturday => 5,
352
+ :sunday => 6 }
353
+ since(1.week).beginning_of_week.since(days_into_week[day].day).change(:hour => 0)
354
+ end
355
+
356
+ # Set time to end of day
357
+ #def end_of_day
358
+ # return Time.mktime(year, month, day, 23, 59, 59, 999)
359
+ #end
360
+
361
+ # Returns a new Time representing the start of the day (0:00)
362
+ def beginning_of_day
363
+ self - self.seconds_since_midnight
364
+ end
365
+ alias :midnight :beginning_of_day
366
+ alias :at_midnight :beginning_of_day
367
+ alias :at_beginning_of_day :beginning_of_day
368
+ alias :start_of_day :beginning_of_day
369
+
370
+ # Returns a new Time representing the start of the month
371
+ # (1st of the month, 0:00)
372
+ def beginning_of_month
373
+ #self - ((self.mday-1).days + self.seconds_since_midnight)
374
+ change(:day => 1,:hour => 0, :min => 0, :sec => 0, :usec => 0)
375
+ end
376
+ alias_method :at_beginning_of_month, :beginning_of_month
377
+
378
+ # Returns a new Time representing the start of the year (1st of january, 0:00)
379
+ def beginning_of_year
380
+ change(:month => 1,:day => 1,:hour => 0, :min => 0, :sec => 0, :usec => 0)
381
+ end
382
+ alias :at_beginning_of_year :beginning_of_year
383
+
384
+ # Convenience method which returns a new Time representing
385
+ # the time 1 day ago
386
+ def yesterday
387
+ self.ago(1.day)
388
+ end
389
+
390
+ # Convenience method which returns a new Time representing
391
+ # the time 1 day since the instance time
392
+ def tomorrow
393
+ self.since(1.day)
394
+ end
395
+
396
+ # Returns a new time at start of day
397
+ def start_of_day
398
+ return Time.mktime(year, month, day, 0, 0, 0, 0)
399
+ end
400
+ alias_method :to_start_of_day, :start_of_day
401
+
402
+ # Returns a new time at end of day
403
+ def end_of_day
404
+ Time.mktime(year, month, day, 23, 59, 59, 999)
405
+ end
406
+ alias_method :to_end_of_day, :end_of_day
407
+
408
+ # Returns true only if day of time is included in the
409
+ # range (stime..etime). Only year days are checked.
410
+ def in_day_range?(stime=ZERO, etime=NEVER)
411
+ if (etime <= stime)
412
+ warn "invalid end time (#{etime} < #{stime})" if $DEBUG
413
+ etime = NEVER
414
+ end
415
+
416
+ stime = stime.to_start_of_day
417
+ etime = etime.to_end_of_day
418
+
419
+ return (stime..etime).include?(time)
420
+ end
421
+
422
+ end#module Time
423
+
424
+ # = Time Metaclass Extensions
425
+ #
426
+ module TimeClass
427
+
428
+ # This method calculates the days extrema given two time objects.
429
+ # start time is the given time1 at 00:00:00
430
+ # end time is the given time2 at 23:59:59:999
431
+ #
432
+ # Input:
433
+ # - the two times (if only time1 is provided then you get an extrema
434
+ # of exactly one day extrema.
435
+ #
436
+ # Output:
437
+ # - the time range. you can get the start/end times using
438
+ # range methods.
439
+ #
440
+ # CREDIT George Moschovitis
441
+
442
+ def days_extrema(time1, time2=nil)
443
+ time2 = time1 if (not time2.valid? Time)
444
+ time2 = NEVER if (time2 <= time1)
445
+ start_time = time1.start_of_day #start_of_day(time1)
446
+ end_time = time2.end_of_day #end_of_day(time2)
447
+ return (start_time..end_time)
448
+ end
449
+
450
+ end
451
+
452
+ # = Numeric Times
453
+ #
454
+ module Numeric
455
+
456
+ # Calculates time _before_ a given time. Default time is now.
457
+ # Reads best with arguments: 10.days.before( Time.now - 1.day )
458
+ def before(time = ::Time.now)
459
+ time - self
460
+ end
461
+ alias_method :until, :before # Reads best with argument: 10.minutes.until(time)
462
+ alias_method :ago, :before # Reads best without arguments: 10.minutes.ago
463
+
464
+ # Calculates time _after_ a given time. Default time is now.
465
+ # Reads best with argument: 10.minutes.after(time)
466
+ def after(time = ::Time.now)
467
+ time + self
468
+ end
469
+ alias_method :since, :after # Reads best with argument: 10.minutes.since(time)
470
+ alias_method :hence, :after # Reads best with argument: 10.minutes.since(time)
471
+ alias_method :from_now, :after # Reads best without arguments: 10.minutes.from_now
472
+ alias_method :later, :after # Reads best without arguments: 10.minutes.later
473
+
474
+ end#module Numeric
475
+
476
+ end#module Times
477
+
478
+ end#module RichUnits
479
+
480
+ class Numeric #:nodoc:
481
+ include RichUnits::Times::Numeric
482
+ end
483
+
484
+ class Time #:nodoc:
485
+ extend RichUnits::Times::TimeClass
486
+ include RichUnits::Times::Time
487
+
488
+ NEVER = Time.mktime(2038)
489
+ ZERO = Time.mktime(1972)
490
+ end
491
+