days_and_times 1.0.1

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