days_and_times 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
data.tar.gz.sig ADDED
Binary file
data/History.txt ADDED
@@ -0,0 +1,7 @@
1
+ == 1.0.1 2009-11-20
2
+
3
+ * Published to gemcutter.
4
+
5
+ == 1.0.0 2008-08-06
6
+
7
+ * Initial release
data/License.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 BehindLogic
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest.txt ADDED
@@ -0,0 +1,13 @@
1
+ History.txt
2
+ License.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ lib/days_and_times.rb
7
+ lib/days_and_times/duration.rb
8
+ lib/days_and_times/numeric.rb
9
+ lib/days_and_times/object.rb
10
+ lib/days_and_times/time.rb
11
+ spec/days_and_times/numeric_spec.rb
12
+ spec/days_and_times_spec.rb
13
+ spec/spec_helper.rb
data/README.txt ADDED
@@ -0,0 +1,72 @@
1
+ = days_and_times
2
+
3
+ * Homepage: http://dcparker.github.com/days_and_times
4
+ * Code: http://github.com/dcparker/days_and_times
5
+ * Download: http://gemcutter.com/gems/days_and_times
6
+
7
+ == DESCRIPTION:
8
+
9
+ Natural language method chaining for Time, Durations and the like.
10
+
11
+ == FEATURES:
12
+
13
+ * Singular and plural of the concrete time units (second, minute, hour, day, week) are added to the Numeric object.
14
+ * Use the Duration class to do lots of cool things.
15
+ * Perform mathematical operations between numbers and durations, durations and durations, and durations and numbers, and the 'unit' will be respected as expected in the algebraic rules.
16
+ * Iterate over Duration objects, by the time-unit the duration was instantiated with, or a unit of choice.
17
+ * Durations can be 'anchored' to a begin or end time.
18
+
19
+ == PROBLEMS:
20
+
21
+ * Months are variable and do not lend themselves to concrete mathematics, so as of now they are not implemented.
22
+
23
+ == SYNOPSIS:
24
+
25
+ 1.day #=> A duration of 1 day
26
+ 7.days #=> A duration of 7 days
27
+ 1.week #=> A duration of 1 week
28
+ 1.week - 2.days #=> A duration of 5 days
29
+ 1.week.from(Now()) #=> The time of 1 week from this moment
30
+ 1.week.from(Today()) #=> The time of 1 week from the beginning of today
31
+ 3.minutes.ago.until(7.minutes.from(Now())) #=> duration 3 minutes ago to 7 minutes from now
32
+ 3.minutes.ago.until(7.minutes.from(Now())) - 2.minutes #=> duration 3 minutes ago to 5 minutes from now
33
+ 4.weeks.from(2.days.from(Now())).until(8.weeks.from(Yesterday())) #=> A duration, starting in 4 weeks and 2 days, and ending 8 weeks from yesterday
34
+ 1.week - 1.second #=> A duration of 6 days, 23 hours, 59 minutes, and 59 seconds
35
+ 4.weeks / 2 #=> A duration of 2 weeks
36
+ 4.weeks / 2.weeks #=> The integer 2
37
+ 8.weeks.each {|week| ...} #=> Runs code for each week contained in the duration (of 8 weeks)
38
+ 8.weeks.starting(Now()).each {|week| ...} #=> Runs code for each week in the duration, but each week is also anchored to a starting time, in sequence through the duration.
39
+ 1.week.each {|week| ...} #=> Automatically chooses week as its iterator
40
+ 7.days.each {|day| ...} #=> Automatically chooses day as its iterator
41
+ 1.week.each_day {|day| ...} #=> Forcing the week to iterate through days
42
+ 1.week.each(10.hours) {|ten_hour_segment| ...} #=> Using a custom iterator of 10 hours. There would be 17 of them, but notice that the last iteration will only be 8 hours.
43
+ # ... and more!
44
+
45
+ == INSTALL:
46
+
47
+ gem install days_and_times -s http://gemcutter.com
48
+
49
+ == LICENSE:
50
+
51
+ (The MIT License)
52
+
53
+ Copyright (c) 2008 BehindLogic
54
+
55
+ Permission is hereby granted, free of charge, to any person obtaining
56
+ a copy of this software and associated documentation files (the
57
+ 'Software'), to deal in the Software without restriction, including
58
+ without limitation the rights to use, copy, modify, merge, publish,
59
+ distribute, sublicense, and/or sell copies of the Software, and to
60
+ permit persons to whom the Software is furnished to do so, subject to
61
+ the following conditions:
62
+
63
+ The above copyright notice and this permission notice shall be
64
+ included in all copies or substantial portions of the Software.
65
+
66
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
67
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
68
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
69
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
70
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
71
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
72
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+
6
+ Hoe.spec 'days_and_times' do
7
+ developer 'Daniel Parker', 'gems@behindlogic.com'
8
+ self.url = "http://dcparker.github.com/days_and_times"
9
+ end
@@ -0,0 +1,8 @@
1
+ module DaysAndTimes
2
+ VERSION = '1.0.1'
3
+ end
4
+
5
+ require 'days_and_times/duration'
6
+ require 'days_and_times/numeric'
7
+ require 'days_and_times/time'
8
+ require 'days_and_times/object'
@@ -0,0 +1,367 @@
1
+ require 'time'
2
+ class Duration
3
+ # Length is the length of the time span, in seconds
4
+ # Unit is a length of time (in seconds) to use in collection methods
5
+ # StartTime is an optional attribute that can 'anchor' a duration
6
+ # to a specific real time.
7
+ attr_accessor :length, :unit, :start_time
8
+ def initialize(count=0,unit=1,start_time=nil,auto_klass={})
9
+ if unit.is_a?(Time) || unit.is_a?(DateTime)
10
+ start_time = unit
11
+ unit = 1
12
+ end
13
+ options = {:count => count || 0, :unit => unit || 1, :start_time => start_time}.merge(count.is_a?(Hash) ? count : {})
14
+
15
+ @unit = options[:unit]
16
+ @length = (@unit * options[:count].to_f).round
17
+ @start_time = options[:start_time]
18
+ end
19
+ def self.new(*args)
20
+ a = super
21
+ if self.name == 'Duration' && (args.last.is_a?(Hash) ? args.last[:auto_class] == true : true)
22
+ a.send(:auto_class)
23
+ else
24
+ a
25
+ end
26
+ end
27
+ def self.length
28
+ 1
29
+ end
30
+
31
+ # * * * * * * * * * * * * * * * *
32
+ # A Duration is a LENGTH of Time
33
+ # -This is measured in seconds,
34
+ # but set in terms of the unit
35
+ # currently being used.
36
+ def length=(value)
37
+ if value.respond_to?(:to_f)
38
+ @length = (self.unit * value.to_f).round
39
+ else
40
+ raise TypeError, "Can't set a Duration's length to a #{value.class.name} object."
41
+ end
42
+ end
43
+ def length
44
+ length = @length.to_f / self.unit
45
+ length.to_i == length ? length.to_i : length
46
+ end
47
+ def abs_length=(value)
48
+ if value.respond_to?(:to_i)
49
+ @length = value.to_i
50
+ else
51
+ raise TypeError, "Can't set a Duration's length to a #{value.class.name} object."
52
+ end
53
+ end
54
+ def abs_length
55
+ @length
56
+ end
57
+ def to_i
58
+ self.abs_length.to_i
59
+ end
60
+ def to_f
61
+ self.abs_length.to_f
62
+ end
63
+ def to_s
64
+ "#{self.length} #{self.class.name}"
65
+ end
66
+ def coerce(*args)
67
+ to_f.coerce(*args)
68
+ end
69
+
70
+ def ===(other)
71
+ self.to_f == other.to_f
72
+ end
73
+ def inspect
74
+ "#<#{self.class.name}:#{self.object_id} (length=#{self.length.inspect}) #{self.instance_variables.reject {|d| d=='@length' || self.instance_variable_get(d).nil?}.collect {|iv| "#{iv}=#{self.instance_variable_get(iv).inspect}"}.join(' ')}>"
75
+ end
76
+ # * * * * * * * * * * * * * * * *
77
+
78
+ # * * * * * * * * * * * * * * * * * * * * *
79
+ # A Duration's calculations utilize a UNIT
80
+ # -This is stored as the number of
81
+ # seconds equal to the unit's value.
82
+ def unit=(value)
83
+ if value.respond_to?(:to_i)
84
+ @unit = value.to_i
85
+ else
86
+ raise TypeError, "Can't set a Duration's unit to a #{value.class.name} object."
87
+ end
88
+ end
89
+ def -(value)
90
+ if value.respond_to?(:to_i)
91
+ auto_class(Duration.new(@length - value.to_i))
92
+ else
93
+ raise TypeError, "Can't convert #{value.class.name} to an integer."
94
+ end
95
+ end
96
+ def +(value)
97
+ if value.respond_to?(:to_i)
98
+ auto_class(Duration.new(@length + value.to_i))
99
+ else
100
+ raise TypeError, "Can't convert #{value.class.name} to an integer."
101
+ end
102
+ end
103
+ def *(value)
104
+ if value.is_a?(Duration)
105
+ @length * value.length * value.unit
106
+ elsif value.respond_to?(:to_i)
107
+ auto_class(Duration.new(@length * value))
108
+ else
109
+ raise TypeError, "Can't convert #{value.class.name} to an integer."
110
+ end
111
+ end
112
+ def /(value)
113
+ if value.is_a?(Duration)
114
+ @length / (value.length * value.unit)
115
+ elsif value.respond_to?(:to_i)
116
+ auto_class(Duration.new(@length / value))
117
+ else
118
+ raise TypeError, "Can't convert #{value.class.name} to an integer."
119
+ end
120
+ end
121
+ def in_weeks
122
+ self.unit = Week.length
123
+ auto_class(self)
124
+ end
125
+ def in_days
126
+ self.unit = Day.length
127
+ auto_class(self)
128
+ end
129
+ def in_hours
130
+ self.unit = Hour.length
131
+ auto_class(self)
132
+ end
133
+ def in_minutes
134
+ self.unit = Minute.length
135
+ auto_class(self)
136
+ end
137
+ def in_seconds
138
+ self.unit = Second.length
139
+ auto_class(self)
140
+ end
141
+ def weeks
142
+ @length.to_f / Week.length
143
+ end
144
+ def days
145
+ @length.to_f / Day.length
146
+ end
147
+ def hours
148
+ @length.to_f / Hour.length
149
+ end
150
+ def minutes
151
+ @length.to_f / Minute.length
152
+ end
153
+ def seconds
154
+ @length.to_f / Second.length
155
+ end
156
+ # * * * * * * * * * * * * * * * * * * * * *
157
+
158
+ # * * * * * * * * * * * * * * * * * * * * * * *
159
+ # A Duration can be 'anchored' to a START_TIME
160
+ # -This start_time is a Time object
161
+ def start_time=(value)
162
+ if value.is_a?(Time) || value.is_a?(DateTime)
163
+ @start_time = value.to_time
164
+ else
165
+ raise TypeError, "A Duration's start_time must be a Time or DateTime object."
166
+ end
167
+ end
168
+ def end_time=(value)
169
+ if value.is_a?(Time) || value.is_a?(DateTime)
170
+ @start_time = value.to_time - self #Subtracts this duration from the end_time to get the start_time
171
+ else
172
+ raise TypeError, "A Duration's end_time must be a Time or DateTime object."
173
+ end
174
+ end
175
+ def end_time
176
+ @start_time + self
177
+ end
178
+ def anchored?
179
+ !self.start_time.nil?
180
+ end
181
+ # * * * * * * * * * * * * * * * * * * * * * * *
182
+
183
+ # * * * * * * * * * * * * * * * * * * * * * * * *
184
+ # Calculations using Duration as an intermediate
185
+ def from(time)
186
+ time + @length
187
+ end
188
+ def before(time)
189
+ time - @length
190
+ end
191
+ def from_now
192
+ self.from(Time.now)
193
+ end
194
+ def ago
195
+ self.before(Time.now)
196
+ end
197
+ def starting(time)
198
+ self.start_time = time
199
+ self
200
+ end
201
+ def ending(time)
202
+ self.end_time = time
203
+ self
204
+ end
205
+ # * * * * * * * * * * * * * * * * * * * * * * * *
206
+
207
+ # * * * * * * * * * * * * * * * * * * * * * * * * * * *
208
+ # A Duration can be treated as a 'collection' of units
209
+ def each_week(&block)
210
+ self.each(Week.length,&block)
211
+ end
212
+ def each_day(&block)
213
+ self.each(Day.length,&block)
214
+ end
215
+ def each_hour(&block)
216
+ self.each(Hour.length,&block)
217
+ end
218
+ def each_minute(&block)
219
+ self.each(Minute.length,&block)
220
+ end
221
+ def each_second(&block)
222
+ self.each(Second.length,&block)
223
+ end
224
+ def collect(use_unit=self.class.length,&block)
225
+ ary = []
226
+ self.each(use_unit) do |x|
227
+ ary << (block_given? ? yield(x) : x)
228
+ end
229
+ ary
230
+ end
231
+ def each(use_unit=self.class.length)
232
+ remainder = @length.to_f % use_unit
233
+ ret = []
234
+ if self.start_time.nil?
235
+ (@length.to_f / use_unit).to_i.times do |i|
236
+ ret << Duration.new(1, use_unit)
237
+ yield(ret[-1])
238
+ end
239
+ else
240
+ (@length.to_f / use_unit).to_i.times do |i|
241
+ ret << Duration.new(1, use_unit, (self.start_time + (use_unit * i)))
242
+ yield(ret[-1])
243
+ end
244
+ end
245
+ if remainder > 0
246
+ ret << (self.start_time.nil? ? Duration.new(remainder, 1) : Duration.new(remainder, 1, (self.start_time + @length - remainder)))
247
+ yield(ret[-1])
248
+ end
249
+ ret
250
+ end
251
+ # * * * * * * * * * * * * * * * * * * * * * * * * * * *
252
+
253
+ # * * * * * * * * * * * * * * * * * * * * * * * * * * *
254
+ # Through some ingenious metacoding (see 'def bind_object_method') below,
255
+ # it is possible to create a new method on a Duration object
256
+ # to a method on another object, in order to gather information
257
+ # based on the duration mentioned.
258
+ def create_find_within_method_for(other, method_name, other_method_name)
259
+ self.bind_object_method(other, method_name, other_method_name, [[], ['self.start_time', 'self.end_time']])
260
+ end
261
+ def self.create_find_within_method_for(other, method_name, other_method_name)
262
+ self.bind_class_object_method(other, method_name, other_method_name, [[], ['self.start_time', 'self.end_time']])
263
+ end
264
+ # * * * * * * * * * * * * * * * * * * * * * * * * * * *
265
+
266
+ def method_missing(method_name, *args)
267
+ # Delegate any missing methods to the start_time Time object, if we have a start_time and the method exists there.
268
+ return self.start_time.send(method_name, *args) if self.anchored? && self.start_time.respond_to?(method_name)
269
+ super
270
+ end
271
+
272
+ private
273
+ def auto_class(obj=self)
274
+ new_obj = case obj.unit
275
+ when 1
276
+ obj.class.name == 'Seconds' ? obj : (obj.length == 1 ? Second.new(obj.start_time) : Seconds.new(obj.length,obj.start_time))
277
+ when 60
278
+ obj.class.name == 'Minutes' ? obj : (obj.length == 1 ? Minute.new(obj.start_time) : Minutes.new(obj.length,obj.start_time))
279
+ when 3600
280
+ obj.class.name == 'Hours' ? obj : (obj.length == 1 ? Hour.new(obj.start_time) : Hours.new(obj.length,obj.start_time))
281
+ when 86400
282
+ obj.class.name == 'Days' ? obj : (obj.length == 1 ? Day.new(obj.start_time) : Days.new(obj.length,obj.start_time))
283
+ when 604800
284
+ obj.class.name == 'Weeks' ? obj : (obj.length == 1 ? Week.new(obj.start_time) : Weeks.new(obj.length,obj.start_time))
285
+ else
286
+ obj.class.name == 'Duration' ? obj : Duration.new(obj.length,obj.unit,obj.start_time,{:auto_class => false})
287
+ end
288
+ # Now, auto-transform class if possible:
289
+ case
290
+ when !['Weeks', 'Week'].include?(new_obj.class.name) && new_obj.to_i.remainder(Week.length) == 0
291
+ new_obj.to_i == Week.length ? Week.new(new_obj.start_time) : Weeks.new(new_obj.to_f / Week.length,new_obj.start_time)
292
+ when !['Weeks', 'Week', 'Days', 'Day'].include?(new_obj.class.name) && new_obj.to_i.remainder(Day.length) == 0
293
+ new_obj.to_i == Day.length ? Day.new(new_obj.start_time) : Days.new(new_obj.to_f / Day.length,new_obj.start_time)
294
+ when !['Weeks', 'Week', 'Days', 'Day', 'Hours', 'Hour'].include?(new_obj.class.name) && new_obj.to_i.remainder(Hour.length) == 0
295
+ new_obj.to_i == Hour.length ? Hour.new(new_obj.start_time) : Hours.new(new_obj.to_f / Hour.length,new_obj.start_time)
296
+ when !['Weeks', 'Week', 'Days', 'Day', 'Hours', 'Hour', 'Minutes', 'Minute'].include?(new_obj.class.name) && new_obj.to_f.remainder(Minute.length) == 0
297
+ new_obj.to_i == Minute.length ? Minute.new(new_obj.start_time) : Minutes.new(new_obj.to_f / Minute.length,new_obj.start_time)
298
+ else
299
+ new_obj
300
+ end
301
+ end
302
+ end
303
+ class Weeks < Duration
304
+ def initialize(count=1,start_time=nil)
305
+ super(count,Week.length,start_time)
306
+ end
307
+ def self.length
308
+ 604800
309
+ end
310
+ end
311
+ class Week < Weeks
312
+ def initialize(start_time=nil)
313
+ super(1,start_time)
314
+ end
315
+ end
316
+ class Days < Duration
317
+ def initialize(count=1,start_time=nil)
318
+ super(count,Day.length,start_time)
319
+ end
320
+ def self.length
321
+ 86400
322
+ end
323
+ end
324
+ class Day < Days
325
+ def initialize(start_time=nil)
326
+ super(1,start_time)
327
+ end
328
+ end
329
+ class Hours < Duration
330
+ def initialize(count=1,start_time=nil)
331
+ super(count,Hour.length,start_time)
332
+ end
333
+ def self.length
334
+ 3600
335
+ end
336
+ end
337
+ class Hour < Hours
338
+ def initialize(start_time=nil)
339
+ super(1,start_time)
340
+ end
341
+ end
342
+ class Minutes < Duration
343
+ def initialize(count=1,start_time=nil)
344
+ super(count,Minute.length,start_time)
345
+ end
346
+ def self.length
347
+ 60
348
+ end
349
+ end
350
+ class Minute < Minutes
351
+ def initialize(start_time=nil)
352
+ super(1,start_time)
353
+ end
354
+ end
355
+ class Seconds < Duration
356
+ def initialize(count=1,start_time=nil)
357
+ super(count,Second.length,start_time)
358
+ end
359
+ def self.length
360
+ 1
361
+ end
362
+ end
363
+ class Second < Seconds
364
+ def initialize(start_time=nil)
365
+ super(1,start_time)
366
+ end
367
+ end
@@ -0,0 +1,48 @@
1
+ require 'days_and_times/duration'
2
+ class Numeric
3
+ def weeks
4
+ self == 1 ? Week.new : Weeks.new(self)
5
+ end
6
+ def week
7
+ self.weeks
8
+ end
9
+
10
+ def days
11
+ self == 1 ? Day.new : Days.new(self)
12
+ end
13
+ def day
14
+ self.days
15
+ end
16
+
17
+ def hours
18
+ self == 1 ? Hour.new : Hours.new(self)
19
+ end
20
+ def hour
21
+ self.hours
22
+ end
23
+
24
+ def minutes
25
+ self == 1 ? Minute.new : Minutes.new(self)
26
+ end
27
+ def minute
28
+ self.minutes
29
+ end
30
+
31
+ def seconds
32
+ self == 1 ? Second.new : Seconds.new(self)
33
+ end
34
+ def second
35
+ self.seconds
36
+ end
37
+
38
+ def is_multiple_of?(num)
39
+ self % num == 0
40
+ end
41
+
42
+ def am
43
+ Time.parse("#{self}:00:00")
44
+ end
45
+ def pm
46
+ Time.parse("#{self+12}:00:00")
47
+ end
48
+ end
@@ -0,0 +1,19 @@
1
+ class Object
2
+ def bind_class_object_method(other, self_method_name, other_method_name, args=[[],[]])
3
+ # Since I can't pass the 'other' object into eval as a string, I have to
4
+ # set a class instance variable and copy the contents to a class variable
5
+ # so that the generated method will play nicely with subclasses.
6
+ self.instance_variable_set("@#{self_method_name.to_s}_OBJ", other)
7
+ self.send :eval, "@@#{self_method_name.to_s}_OBJ = @#{self_method_name.to_s}_OBJ
8
+ def #{self_method_name.to_s}(#{args[0].join(', ')})
9
+ @@#{self_method_name.to_s}_OBJ.#{other_method_name.to_s}(#{args[1].join(', ')})
10
+ end"
11
+ self
12
+ end
13
+ def bind_object_method(other, self_method_name, other_method_name, args=[[],[]])
14
+ self.instance_variable_set("@#{self_method_name}_OBJ", other)
15
+ eval "def self.#{self_method_name}(#{args[0].join(', ')})
16
+ @#{self_method_name}_OBJ.#{other_method_name}(#{args[1].join(', ')})
17
+ end"
18
+ end
19
+ end
@@ -0,0 +1,137 @@
1
+ require 'time'
2
+ class Time
3
+ def to_time
4
+ self
5
+ end
6
+
7
+ # Returns a new Time where one or more of the elements have been changed according to the +options+ parameter. The time options
8
+ # (hour, minute, sec, usec) reset cascadingly, so if only the hour is passed, then minute, sec, and usec is set to 0. If the hour and
9
+ # minute is passed, then sec and usec is set to 0.
10
+ def change(options)
11
+ ::Time.send(
12
+ self.utc? ? :utc_time : :local_time,
13
+ options[:year] || self.year,
14
+ options[:month] || self.month,
15
+ options[:day] || options[:mday] || self.day, # mday is deprecated
16
+ options[:hour] || self.hour,
17
+ options[:min] || (options[:hour] ? 0 : self.min),
18
+ options[:sec] || ((options[:hour] || options[:min]) ? 0 : self.sec),
19
+ options[:usec] || ((options[:hour] || options[:min] || options[:sec]) ? 0 : self.usec)
20
+ )
21
+ end
22
+
23
+ def day_name
24
+ self.strftime("%A")
25
+ end
26
+ def month_name
27
+ self.strftime("%B")
28
+ end
29
+
30
+ # Seconds since midnight: Time.now.seconds_since_midnight
31
+ def seconds_since_midnight
32
+ self.to_i - self.change(:hour => 0).to_i + (self.usec/1.0e+6)
33
+ end
34
+
35
+ # Returns a new Time representing the start of the day (0:00)
36
+ def beginning_of_day
37
+ (self - self.seconds_since_midnight).change(:usec => 0)
38
+ end
39
+ alias :midnight :beginning_of_day
40
+ alias :at_midnight :beginning_of_day
41
+ alias :at_beginning_of_day :beginning_of_day
42
+
43
+ # Returns a new Time if requested year can be accomodated by Ruby's Time class
44
+ # (i.e., if year is within either 1970..2038 or 1902..2038, depending on system architecture);
45
+ # otherwise returns a DateTime
46
+ def self.time_with_datetime_fallback(utc_or_local, year, month=1, day=1, hour=0, min=0, sec=0, usec=0)
47
+ ::Time.send(utc_or_local, year, month, day, hour, min, sec, usec)
48
+ rescue
49
+ offset = if utc_or_local.to_sym == :utc then 0 else ::DateTime.now.offset end
50
+ ::DateTime.civil(year, month, day, hour, min, sec, offset, 0)
51
+ end
52
+
53
+ # wraps class method time_with_datetime_fallback with utc_or_local == :utc
54
+ def self.utc_time(*args)
55
+ time_with_datetime_fallback(:utc, *args)
56
+ end
57
+
58
+ # wraps class method time_with_datetime_fallback with utc_or_local == :local
59
+ def self.local_time(*args)
60
+ time_with_datetime_fallback(:local, *args)
61
+ end
62
+
63
+ def self.tomorrow
64
+ Time.now.beginning_of_day + 1.day
65
+ end
66
+ def tomorrow
67
+ self.beginning_of_day + 1.day
68
+ end
69
+ def self.yesterday
70
+ Time.now.beginning_of_day - 1.day
71
+ end
72
+ def yesterday
73
+ self.beginning_of_day - 1.day
74
+ end
75
+ def self.today
76
+ Time.now.beginning_of_day
77
+ end
78
+ def self.next_month
79
+ today.change(:day => 1, :month => today.month + 1)
80
+ end
81
+
82
+ def until(end_time)
83
+ Duration.new(end_time - self, self)
84
+ end
85
+ def through(duration)
86
+ self.until(duration)
87
+ end
88
+ def for(duration)
89
+ raise TypeError, "must be a Duration object." unless duration.is_a?(Duration)
90
+ duration.start_time = self
91
+ duration
92
+ end
93
+ def is_today?
94
+ self.beginning_of_day == Time.today
95
+ end
96
+ def strfsql
97
+ self.strftime("%Y-#{self.strftime("%m").to_i.to_s}-#{self.strftime("%d").to_i.to_s}")
98
+ end
99
+ def self.from_tzid(tzid) #We aren't handling the Time Zone part here...
100
+ if tzid =~ /(\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)(\d\d)Z/ # yyyymmddThhmmss
101
+ Time.xmlschema("#{$1}-#{$2}-#{$3}T#{$4}:#{$5}:#{$6}")
102
+ else
103
+ return nil
104
+ end
105
+ end
106
+ def humanize_time
107
+ self.strftime("%M").to_i > 0 ? self.strftime("#{self.strftime("%I").to_i.to_s}:%M%p").downcase : self.strftime("#{self.strftime("%I").to_i.to_s}%p").downcase
108
+ end
109
+ def humanize_date(length_profile='medium') #There may be decent reason to change how this works entirely...
110
+ case length_profile
111
+ when 'abbr' || 'abbreviated'
112
+ self.strftime("%m/%d/%y")
113
+ when 'short'
114
+ self.strftime("%b #{self.strftime("%d").to_i.to_s}")
115
+ when 'medium'
116
+ self.strftime("%B #{self.strftime("%d").to_i.to_s}")
117
+ when 'long'
118
+ self.strftime("%B #{self.strftime("%d").to_i.to_s}, %Y")
119
+ end
120
+ end
121
+ def humanize_date_time
122
+ self.humanize_date + ' ' + self.humanize_time
123
+ end
124
+ end
125
+
126
+ def Today
127
+ Time.today
128
+ end
129
+ def Yesterday
130
+ Time.yesterday
131
+ end
132
+ def Tomorrow
133
+ Time.tomorrow
134
+ end
135
+ def Now
136
+ Time.now
137
+ end
@@ -0,0 +1,9 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ describe Numeric do
4
+ it "should generate am and pm times" do
5
+ 3.am.to_s.should eql(Time.parse("3:00:00").to_s)
6
+ 3.pm.to_s.should eql(Time.parse("15:00:00").to_s)
7
+ 19.am.to_s.should eql(Time.parse("19:00:00").to_s)
8
+ end
9
+ end
@@ -0,0 +1,80 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe Duration do
4
+ it "should be backward-compatible with previous english time statements" do
5
+ 10.minutes.from_now.inspect.should eql((Time.now + 600).inspect)
6
+ 3.days.ago.inspect.should eql((Time.now - 3*24*60*60).inspect)
7
+ # More english statements need to be written!!
8
+ end
9
+
10
+ it "should store a duration correctly in minutes if told to do so, even when created in seconds" do
11
+ 132.seconds.in_minutes.should be_is_a(Minutes)
12
+ 132.seconds.in_minutes.unit.should eql(60)
13
+ 132.seconds.in_minutes.length.to_s.should eql('2.2')
14
+ end
15
+
16
+ it "should add a duration properly to a Time object" do
17
+ (Time.now+1.day).inspect.should eql((Time.now+86400).inspect)
18
+ end
19
+
20
+ it "should properly represent time in string, depending on the unit" do
21
+ 23.seconds.to_s.should eql("23 Seconds")
22
+ end
23
+
24
+ it "should properly perform mathmatical operations, yet considering the unit" do
25
+ (2.days / 2 === 1.day).should eql(true)
26
+ (2.days / 2).in_days.length.should eql(1.day.length)
27
+ end
28
+
29
+ it "should automatically map to Day, Minute, Hour, etc if the length matches" do
30
+ (1.week - 6.days).class.name.should eql('Day')
31
+ end
32
+
33
+ it "should render durations properly" do
34
+ 1.day.should === Duration.new(1, 86400) #=> A duration of 1 day
35
+ 7.days.should === Duration.new(7, 86400) #=> A duration of 7 days
36
+ 1.week.should === Duration.new(1, 604800) #=> A duration of 1 week
37
+ end
38
+
39
+ it "should perform mathematics on unanchored durations" do
40
+ (1.week - 2.days).should === Duration.new(5, 86400) #=> A duration of 5 days
41
+ end
42
+
43
+ it "should perform mathematics on an anchored duration" do
44
+ (3.minutes.ago.until(7.minutes.from(Now())) - 2.minutes).should === Duration.new(8, 60) #=> duration 3 minutes ago to 5 minutes from now
45
+ end
46
+
47
+ it "should generate durations from numeric and time arguments" do
48
+ 1.week.from(Now()).to_s.should eql((Time.now + 604800).to_s) #=> The time of 1 week from this moment
49
+ 1.week.from(Today()).should eql((Time.now + 604800).beginning_of_day) #=> The time of 1 week from the beginning of today
50
+ 3.minutes.ago.until(7.minutes.from(Now())).should === Duration.new(10, 60) #=> duration 3 minutes ago to 7 minutes from now
51
+ 4.weeks.from(2.days.from(Now())).until(8.weeks.from(Yesterday())) #=> A duration, starting in 4 weeks and 2 days, and ending 8 weeks from yesterday
52
+ end
53
+
54
+ it "should perform cross-unit calculations" do
55
+ 1.week - 1.second #=> A duration of 6 days, 23 hours, 59 minutes, and 59 seconds
56
+ end
57
+
58
+ it "should perform algebraic calculations respecting the unit" do
59
+ 4.weeks / 2 #=> A duration of 2 weeks
60
+ 4.weeks / 2.weeks #=> The integer 2
61
+ end
62
+
63
+ it "should perform iterations respecting the default unit and custom units" do
64
+ 8.weeks.each {|week|
65
+ week.should === Duration.new(1, 604800)
66
+ } #=> Runs code for each week contained in the duration (of 8 weeks)
67
+ 8.weeks.starting(Now()).each {|week| } #=> Runs code for each week in the duration, but each week is also anchored to a starting time, in sequence through the duration.
68
+ 1.week.each {|week| } #=> Automatically chooses week as its iterator
69
+ 7.days.each {|day| } #=> Automatically chooses day as its iterator
70
+ 1.week.each_day {|day| } #=> Forcing the week to iterate through days
71
+ end
72
+
73
+ it "should perform iterations with a remainder that also runs" do
74
+ count = 0
75
+ 1.week.each(10.hours.to_f) {|ten_hour_segment|
76
+ count += 1
77
+ ten_hour_segment.should === Duration.new(10, 3600) unless count == 17
78
+ }[-1].should === Duration.new(8, 3600) #=> Using a custom iterator of 10 hours. There would be 17 of them, but notice that the last iteration will only be 8 hours.
79
+ end
80
+ end
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + '/../lib/days_and_times'
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: days_and_times
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Daniel Parker
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIDNjCCAh6gAwIBAgIBADANBgkqhkiG9w0BAQUFADBBMQ0wCwYDVQQDDARnZW1z
14
+ MRswGQYKCZImiZPyLGQBGRYLYmVoaW5kbG9naWMxEzARBgoJkiaJk/IsZAEZFgNj
15
+ b20wHhcNMDkxMTE5MDQ0NzIzWhcNMTAxMTE5MDQ0NzIzWjBBMQ0wCwYDVQQDDARn
16
+ ZW1zMRswGQYKCZImiZPyLGQBGRYLYmVoaW5kbG9naWMxEzARBgoJkiaJk/IsZAEZ
17
+ FgNjb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDs+2PvSoBC5mCz
18
+ Nm95RXKOb/u+CmPA70DoG9cF0zipCie003Vm0DYL3Rtobcr32eMvHxkWoJ6xoz0I
19
+ h74yNKtHjTTCxj86HWBPsE6xVMxVCftClndjCyKsiMiqvvp1wDNO0FFK+6LijmL3
20
+ 2Xkp4brWq1JO92y9vYct34R7o2X//+nwZs+sss+EYhNdvdUJfWy7tA5dghGdLvRn
21
+ UhJJSAtTefkBCwO7bufLEt+n7wIRbiJJ5dDCwE3NIX4wUSrNeYwXGXA/Ybki+BUl
22
+ 3KJF9IC0XR9fY9DGF0FXBKrkfDlZRrnQOem2aIxeuln0KQLJXXJuDTQPHO+mK3EG
23
+ UPhR7IAHAgMBAAGjOTA3MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQW
24
+ BBQn32ZStKmqwFqs2vuglYzkvDzBZjANBgkqhkiG9w0BAQUFAAOCAQEAe3Z+iMmy
25
+ IX9ChQW4hNNb1HOpgCMc2RL+vUwku9WE95dZ+BE4A6mOYTj5JXdYf4R4Z2vavr+d
26
+ nwJWtXPeIBWxireb8gUU0DwqodYTpsmkj5LD1zIaZ59rXlwDA9O0V4fwE1iRG5MD
27
+ mB7m8fT8WNOeg4AfjH4aSiHI1+HX1RQkc7KFdLotKnCevzYU6Jza5VUbXyJ+yCEH
28
+ DFARN3mkfGI+18MRDEi39nK2O/bBd6Wf0cYPEKsGQjNNAIBtv9belepSMd1KKfQ2
29
+ L7j8CnNDCrsHDe7/251D85wSvTH4Q/41NE5ahdCkkHwzDJeyhXpmNuUSswdn7woz
30
+ teST6sOe8lUhZQ==
31
+ -----END CERTIFICATE-----
32
+
33
+ date: 2009-11-20 00:00:00 -05:00
34
+ default_executable:
35
+ dependencies:
36
+ - !ruby/object:Gem::Dependency
37
+ name: hoe
38
+ type: :development
39
+ version_requirement:
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: 2.3.3
45
+ version:
46
+ description: Natural language method chaining for Time, Durations and the like.
47
+ email:
48
+ - gems@behindlogic.com
49
+ executables: []
50
+
51
+ extensions: []
52
+
53
+ extra_rdoc_files:
54
+ - History.txt
55
+ - License.txt
56
+ - Manifest.txt
57
+ - README.txt
58
+ files:
59
+ - History.txt
60
+ - License.txt
61
+ - Manifest.txt
62
+ - README.txt
63
+ - Rakefile
64
+ - lib/days_and_times.rb
65
+ - lib/days_and_times/duration.rb
66
+ - lib/days_and_times/numeric.rb
67
+ - lib/days_and_times/object.rb
68
+ - lib/days_and_times/time.rb
69
+ - spec/days_and_times/numeric_spec.rb
70
+ - spec/days_and_times_spec.rb
71
+ - spec/spec_helper.rb
72
+ has_rdoc: true
73
+ homepage: http://dcparker.github.com/days_and_times
74
+ licenses: []
75
+
76
+ post_install_message:
77
+ rdoc_options:
78
+ - --main
79
+ - README.txt
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: "0"
87
+ version:
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: "0"
93
+ version:
94
+ requirements: []
95
+
96
+ rubyforge_project: days_and_times
97
+ rubygems_version: 1.3.5
98
+ signing_key:
99
+ specification_version: 3
100
+ summary: Natural language method chaining for Time, Durations and the like.
101
+ test_files: []
102
+
metadata.gz.sig ADDED
Binary file