richunits 0.2.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+