timetastic 0.1.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/lib/timetastic.rb +133 -0
- data/spec/timetastic_spec.rb +320 -0
- metadata +64 -0
data/lib/timetastic.rb
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
module Timetastic
|
5
|
+
Domains = [ :hours, :weeks, :days, :months, :years ]
|
6
|
+
|
7
|
+
class << self
|
8
|
+
attr_accessor :fixed_time # see Timetastic#fixate() below
|
9
|
+
|
10
|
+
def last(delta = 1, relative_to = nil)
|
11
|
+
Traveller.new(-1, delta, relative_to)
|
12
|
+
end
|
13
|
+
|
14
|
+
alias_method :past, :last
|
15
|
+
|
16
|
+
def next(delta = 1, relative_to = nil)
|
17
|
+
Traveller.new(1, delta, relative_to)
|
18
|
+
end
|
19
|
+
|
20
|
+
alias_method :coming, :next
|
21
|
+
|
22
|
+
# Fixes the relative time by which all of Timetastic operations
|
23
|
+
# are carried out for the duration of the given block.
|
24
|
+
#
|
25
|
+
# Mainly used for tests and gem development.
|
26
|
+
#
|
27
|
+
# An alternative way to 'fix' the time is to directly use the exposed
|
28
|
+
# attribute Timetastic#fixed_time, ie:
|
29
|
+
#
|
30
|
+
# => Timetastic.fixed_time = Time.new(Time.now.year, 6, 1)
|
31
|
+
#
|
32
|
+
# You can simply set the attribute to nil to reset the time back to Time.now
|
33
|
+
def fixate(y, m = 1, d = 1, h = 0, mi = 0, s = 0, &block)
|
34
|
+
if y.is_a?(Time)
|
35
|
+
@fixed_time = y
|
36
|
+
else
|
37
|
+
@fixed_time = Time.new(y,m,d,h,mi,s)
|
38
|
+
end
|
39
|
+
block.call(@fixed_time)
|
40
|
+
@fixed_time = nil
|
41
|
+
end
|
42
|
+
|
43
|
+
# Snippet that calculates the number of days in any given month
|
44
|
+
# taking into account leap years.
|
45
|
+
#
|
46
|
+
# Credit goes to this SO thread: bit.ly/4GjMor
|
47
|
+
def days_in_month(year, month)
|
48
|
+
(Date.new(year, 12, 31) << (12-month)).day
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns the relative time by which operations are carried out
|
52
|
+
def now()
|
53
|
+
@fixed_time || Time.now
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
class Traveller
|
60
|
+
attr_reader :direction, :distance
|
61
|
+
|
62
|
+
def initialize(direction, distance, relative_to = nil)
|
63
|
+
@distance = distance.to_i
|
64
|
+
@relative_to = relative_to
|
65
|
+
@direction = case direction
|
66
|
+
when -1; :ago
|
67
|
+
when 1; :hence
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
Domains.each { |domain|
|
72
|
+
define_method(domain) { relative_anchored_time(domain) }
|
73
|
+
alias_method :"#{domain[0..-2]}", domain
|
74
|
+
}
|
75
|
+
|
76
|
+
protected
|
77
|
+
|
78
|
+
def relative_anchored_time(domain)
|
79
|
+
t = @distance.send(domain).send(@direction, @relative_to)
|
80
|
+
|
81
|
+
case domain
|
82
|
+
when :days, :weeks;
|
83
|
+
Time.new(t.year, t.month, t.day)
|
84
|
+
when :months; Time.new(t.year, t.month, 1)
|
85
|
+
when :years; Time.new(t.year, 1, 1)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end # Timetastic::Traveller
|
89
|
+
|
90
|
+
end # end of Module#Timetastic
|
91
|
+
|
92
|
+
class Fixnum
|
93
|
+
Timetastic::Domains.each do |domain|
|
94
|
+
define_method(domain) { @time_offset_domain = domain; self }
|
95
|
+
|
96
|
+
# an alias for the singular version of the domain
|
97
|
+
alias_method :"#{domain[0..-2]}", domain
|
98
|
+
end
|
99
|
+
|
100
|
+
def ago(relative_to = nil)
|
101
|
+
relative_time(-1, relative_to)
|
102
|
+
end
|
103
|
+
|
104
|
+
def hence(relative_to = nil)
|
105
|
+
relative_time(1, relative_to)
|
106
|
+
end
|
107
|
+
|
108
|
+
alias_method :ahead, :hence
|
109
|
+
alias_method :from_now, :hence
|
110
|
+
|
111
|
+
protected
|
112
|
+
|
113
|
+
attr_reader :time_offset_domain
|
114
|
+
|
115
|
+
def relative_time(coef, relative_to = nil)
|
116
|
+
d = (coef.to_i) / (coef.to_i.abs) * self # delta
|
117
|
+
n = relative_to || Timetastic.now
|
118
|
+
|
119
|
+
case @time_offset_domain
|
120
|
+
when :hours
|
121
|
+
Time.at(n.to_i + d * 3600)
|
122
|
+
when :days
|
123
|
+
Time.at(n.to_i + d * 86400)
|
124
|
+
when :weeks
|
125
|
+
Time.at(n.to_i + d * 604800)
|
126
|
+
when :months
|
127
|
+
Time.at(n.to_i + d * 2592000)
|
128
|
+
when :years
|
129
|
+
Time.new(n.year + d, n.month, n.day, n.hour, n.min, n.sec)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
@@ -0,0 +1,320 @@
|
|
1
|
+
describe Timetastic do
|
2
|
+
tt = Timetastic # alias it to reduce clutter
|
3
|
+
|
4
|
+
before do
|
5
|
+
end
|
6
|
+
|
7
|
+
after do
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "Past features" do
|
11
|
+
|
12
|
+
it "should locate the last year from today" do
|
13
|
+
tt.fixate(2012, 8, 1) { |t|
|
14
|
+
1.year.ago.should == Time.new(2011, 8, 1)
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should locate the last month from today" do
|
19
|
+
tt.fixate(2012, 12, 26) { |t|
|
20
|
+
1.month.ago.should == Time.new(2012, 11, 26)
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should locate the last week from today" do
|
25
|
+
tt.fixate(2012, 12, 26) {
|
26
|
+
1.week.ago.should == Time.new(2012, 12, 19)
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should locate the last day from today" do
|
31
|
+
tt.fixate(2012, 12, 26) {
|
32
|
+
1.day.ago.should == Time.new(2012, 12, 25)
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should locate the last hour from now" do
|
37
|
+
tt.fixate(2012, 12, 26, 14) {
|
38
|
+
1.hour.ago.should == Time.new(2012, 12, 26, 13)
|
39
|
+
12.hours.ago.should == Time.new(2012, 12, 26, 2)
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
end # Past locators
|
44
|
+
|
45
|
+
describe "Future locators" do
|
46
|
+
it "should locate the coming year from today" do
|
47
|
+
tt.fixate(2012, 12, 26) {
|
48
|
+
1.year.hence.should == Time.new(2013, 12, 26)
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should locate the coming month from today" do
|
53
|
+
tt.fixate(2012, 10, 26) {
|
54
|
+
1.month.hence.should == Time.new(2012, 11, 25)
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should locate the coming week from today" do
|
59
|
+
tt.fixate(2012, 12, 20) {
|
60
|
+
1.week.ahead.should == Time.new(2012, 12, 27)
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should locate the coming day from today" do
|
65
|
+
tt.fixate(2012, 12, 26) {
|
66
|
+
1.day.hence.should == Time.new(2012, 12, 27)
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
end # Future locators
|
71
|
+
|
72
|
+
describe "Anchored past locators" do
|
73
|
+
it "should point to the start of last year" do
|
74
|
+
tt.fixate(2012, 3, 3, 5, 32, 16) {
|
75
|
+
tt.last.year.should == Time.new(2011, 1, 1)
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should point to the start of last month" do
|
80
|
+
tt.fixate(2012, 4, 16, 5, 32, 16) {
|
81
|
+
tt.last.month.should == Time.new(2012, 3, 1)
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should point to the start of last day" do
|
86
|
+
tt.fixate(2012, 4, 16, 5, 32, 16) {
|
87
|
+
tt.last.day.should == Time.new(2012, 4, 15)
|
88
|
+
}
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should point to the start of the year before last year" do
|
92
|
+
tt.fixate(2012, 4, 16, 5, 32, 16) {
|
93
|
+
tt.last(2).years.should == Time.new(2010, 1, 1)
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should point to the start of the month before last month" do
|
98
|
+
tt.fixate(2012, 4, 16, 5, 32, 16) {
|
99
|
+
tt.last(2).months.should == Time.new(2012, 2, 1)
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should point to the start of the day before last day" do
|
104
|
+
tt.fixate(2012, 4, 16, 5, 32, 16) {
|
105
|
+
tt.last(2).days.should == Time.new(2012, 4, 14)
|
106
|
+
}
|
107
|
+
end
|
108
|
+
end # Anchored past locators
|
109
|
+
|
110
|
+
describe "Anchored future locators" do
|
111
|
+
it "should point to the start of next year" do
|
112
|
+
tt.fixate(2012, 4, 16, 5, 32, 16) {
|
113
|
+
tt.next.year.should == Time.new(2013, 1, 1)
|
114
|
+
}
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should point to the start of next month" do
|
118
|
+
tt.fixate(2012, 4, 16, 5, 32, 16) {
|
119
|
+
tt.next.month.should == Time.new(2012, 5, 1)
|
120
|
+
}
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should point to the start of next day" do
|
124
|
+
tt.fixate(2012, 4, 16, 5, 32, 16) {
|
125
|
+
tt.next.day.should == Time.new(2012, 4, 17)
|
126
|
+
}
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should point to the start of the year after the coming year" do
|
130
|
+
tt.fixate(2012, 4, 16, 5, 32, 16) {
|
131
|
+
tt.next(2).years.should == Time.new(2014, 1, 1)
|
132
|
+
}
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should point to the start of the month after the coming month" do
|
136
|
+
tt.fixate(2012, 4, 16, 5, 32, 16) {
|
137
|
+
tt.next(2).months.should == Time.new(2012, 6, 1)
|
138
|
+
}
|
139
|
+
end
|
140
|
+
|
141
|
+
it "should point to the start of the day after tomorrow" do
|
142
|
+
tt.fixate(2012, 4, 16, 5, 32, 16) {
|
143
|
+
tt.next(2).days.should == Time.new(2012, 4, 18)
|
144
|
+
}
|
145
|
+
end
|
146
|
+
end # Anchored future locators
|
147
|
+
|
148
|
+
describe "Forward wrapping" do
|
149
|
+
|
150
|
+
it "should wrap forward a day to locate the hour" do
|
151
|
+
tt.fixate(2012, 6, 26, 21) {
|
152
|
+
# this shouldn't wrap
|
153
|
+
1.hour.ahead.should == Time.new(2012, 6, 26, 22)
|
154
|
+
# but this should
|
155
|
+
4.hour.ahead.should == Time.new(2012, 6, 27, 1)
|
156
|
+
}
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should wrap forward a month to locate the day" do
|
160
|
+
tt.fixate(2012, 6, 29) {
|
161
|
+
# this should wrap
|
162
|
+
4.days.ahead.should == Time.new(2012, 7, 3)
|
163
|
+
# and this shouldn't
|
164
|
+
1.days.ahead.should == Time.new(2012, 6, 30)
|
165
|
+
}
|
166
|
+
end
|
167
|
+
|
168
|
+
# we know that 2011 is a leap (gregorian) year, and 2012 isn't
|
169
|
+
# nr of days in february in a leap year is 28, otherwise it's 29
|
170
|
+
it "should wrap forward a month to locate the day in a leap year" do
|
171
|
+
tt.fixate(2011, 2, 28) {
|
172
|
+
2.days.ahead.should == Time.new(2011, 3, 2)
|
173
|
+
1.days.ahead.should == Time.new(2011, 3, 1)
|
174
|
+
0.days.ahead.should == Time.new(2011, 2, 28)
|
175
|
+
-1.days.ahead.should == Time.new(2011, 2, 27)
|
176
|
+
}
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should wrap forward a month to locate the week" do
|
180
|
+
tt.fixate(2012, 6, 20) {
|
181
|
+
# shouldn't wrap
|
182
|
+
1.week.ahead.should == Time.new(2012, 6, 27)
|
183
|
+
# this should wrap
|
184
|
+
2.weeks.ahead.should == Time.new(2012, 7, 4)
|
185
|
+
}
|
186
|
+
|
187
|
+
|
188
|
+
# february has 29 on non-leap
|
189
|
+
tt.fixate(2012, 2, 22) {
|
190
|
+
1.week.ahead.should == Time.new(2012, 2, 29)
|
191
|
+
}
|
192
|
+
|
193
|
+
# 28 on leap year
|
194
|
+
tt.fixate(2011, 2, 22) {
|
195
|
+
1.week.ahead.should == Time.new(2011, 3, 1)
|
196
|
+
}
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should wrap forward a year to locate a month" do
|
200
|
+
tt.fixate(2011, 12, 5) {
|
201
|
+
1.month.ahead.should == Time.new(2012, 1, 4)
|
202
|
+
}
|
203
|
+
end
|
204
|
+
end # Forward wrapping
|
205
|
+
|
206
|
+
describe "Backwards wrapping" do
|
207
|
+
it "should wrap backwards a day to locate the hour" do
|
208
|
+
tt.fixate(2012, 6, 26, 1) {
|
209
|
+
# this shouldn't wrap
|
210
|
+
1.hour.ago.should == Time.new(2012, 6, 26, 0)
|
211
|
+
# but these should
|
212
|
+
2.hours.ago.should == Time.new(2012, 6, 25, 23)
|
213
|
+
4.hour.ago.should == Time.new(2012, 6, 25, 21)
|
214
|
+
}
|
215
|
+
end
|
216
|
+
|
217
|
+
it "should wrap backwards a month to locate the day" do
|
218
|
+
tt.fixate(2012, 3, 2) {
|
219
|
+
4.days.ago.should == Time.new(2012, 2, 27)
|
220
|
+
2.days.ago.should == Time.new(2012, 2, 29)
|
221
|
+
tt.last(2).days.should == Time.new(2012, 2, 29)
|
222
|
+
tt.last(4).days.should == Time.new(2012, 2, 27)
|
223
|
+
}
|
224
|
+
end
|
225
|
+
|
226
|
+
it "should wrap backwards a month to locate the day in a leap year" do
|
227
|
+
tt.fixate(2011, 3, 2) {
|
228
|
+
2.days.ago.should == Time.new(2011, 2, 28)
|
229
|
+
4.days.ago.should == Time.new(2011, 2, 26)
|
230
|
+
tt.last(2).days.should == Time.new(2011, 2, 28)
|
231
|
+
tt.last(4).days.should == Time.new(2011, 2, 26)
|
232
|
+
}
|
233
|
+
end
|
234
|
+
|
235
|
+
it "should wrap backwards more than a month to locate a day" do
|
236
|
+
Timetastic.fixate(2012, 6, 15) {
|
237
|
+
2.days.ago.should == Time.new(2012, 6, 13)
|
238
|
+
15.days.ago.should == Time.new(2012, 5, 31)
|
239
|
+
17.days.ago.should == Time.new(2012, 5, 29)
|
240
|
+
}
|
241
|
+
Timetastic.fixate(2012, 6, 10) {
|
242
|
+
29.days.ago.should == Time.new(2012, 5, 12)
|
243
|
+
30.days.ago.should == Time.new(2012, 5, 11)
|
244
|
+
32.days.ago.should == Time.new(2012, 5, 9)
|
245
|
+
64.days.ago.should == Time.new(2012, 4, 7)
|
246
|
+
}
|
247
|
+
end
|
248
|
+
|
249
|
+
it "should wrap backwards a month to locate the week" do
|
250
|
+
tt.fixate(2012, 6, 8) {
|
251
|
+
1.week.ago.should == Time.new(2012, 6, 1)
|
252
|
+
# this should wrap
|
253
|
+
2.weeks.ago.should == Time.new(2012, 5, 25)
|
254
|
+
tt.last(2).weeks.should == Time.new(2012, 5, 25)
|
255
|
+
}
|
256
|
+
end
|
257
|
+
|
258
|
+
it "should wrap backwards a year to locate a month" do
|
259
|
+
tt.fixate(2011, 3, 5) {
|
260
|
+
# this should wrap
|
261
|
+
6.month.ago.should == Time.new(2010, 9, 6, 1)
|
262
|
+
3.month.ago.should == Time.new(2010, 12, 5)
|
263
|
+
# these shouldn't
|
264
|
+
1.month.ago.should == Time.new(2011, 2, 3)
|
265
|
+
2.month.ago.should == Time.new(2011, 1, 4)
|
266
|
+
}
|
267
|
+
end
|
268
|
+
|
269
|
+
end # Backwards wrapping
|
270
|
+
|
271
|
+
describe "Far-off, OOR wrapping" do
|
272
|
+
it "should wrap backwards more than a year to locate a month" do
|
273
|
+
Timetastic.fixate(2012, 6, 1) {
|
274
|
+
10.months.ago.should == Time.new(2011, 8, 6)
|
275
|
+
12.months.ago.should == Time.new(2011, 6, 7)
|
276
|
+
13.months.ago.should == Time.new(2011, 5, 8)
|
277
|
+
18.months.ago.should == Time.new(2010, 12, 8, 23)
|
278
|
+
20.months.ago.should == Time.new(2010, 10, 10)
|
279
|
+
36.months.ago.should == Time.new(2009, 6, 17)
|
280
|
+
48.months.ago.should == Time.new(2008, 6, 22)
|
281
|
+
60.months.ago.should == Time.new(2007, 6, 28)
|
282
|
+
66.months.ago.should == Time.new(2006, 12, 29, 23)
|
283
|
+
}
|
284
|
+
|
285
|
+
Timetastic.fixate(2007, 8, 15) { |t|
|
286
|
+
8.months.ago.should == Time.new(2006,12,17, 23)
|
287
|
+
19.months.ago.should == Time.new(2006,1,21, 23)
|
288
|
+
20.months.ago.should == Time.new(2005,12,22, 23)
|
289
|
+
}
|
290
|
+
end
|
291
|
+
|
292
|
+
it "should wrap forward more than a month to locate a day" do
|
293
|
+
Timetastic.fixate(2012, 6, 15) {
|
294
|
+
32.days.ahead.should == Time.new(2012, 7, 17)
|
295
|
+
64.days.ahead.should == Time.new(2012, 8, 18)
|
296
|
+
65.days.ahead.should == Time.new(2012, 8, 19)
|
297
|
+
15.days.ahead.should == Time.new(2012, 6, 30)
|
298
|
+
16.days.ahead.should == Time.new(2012, 7, 1)
|
299
|
+
90.days.ahead.should == Time.new(2012, 9, 13)
|
300
|
+
365.days.ahead.should == Time.new(2013, 6, 15)
|
301
|
+
2700.days.ahead.should == Time.new(2019, 11, 5, 23)
|
302
|
+
}
|
303
|
+
end
|
304
|
+
|
305
|
+
it "should wrap forwards more than a year to locate a month" do
|
306
|
+
Timetastic.fixate(2012, 6, 1) {
|
307
|
+
6.months.ahead.should == Time.new(2012, 11, 28)
|
308
|
+
18.months.ahead.should == Time.new(2013, 11, 22, 23)
|
309
|
+
23.months.ahead.should == Time.new(2014, 4, 22)
|
310
|
+
}
|
311
|
+
|
312
|
+
Timetastic.fixate(2020, 7, 30) {
|
313
|
+
1.months.ahead.should == Time.new(2020, 8, 29)
|
314
|
+
14.months.ahead.should == Time.new(2021, 9, 23)
|
315
|
+
25.months.ahead.should == Time.new(2022, 8, 19)
|
316
|
+
}
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
end
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: timetastic
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Ahmad Amireh
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-12-28 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
description: Pure Ruby date selection using an easy and readable interface. Calculation
|
31
|
+
of dates accounts for wrapping across days, months, and years.
|
32
|
+
email: ahmad@algollabs.com
|
33
|
+
executables: []
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- lib/timetastic.rb
|
38
|
+
- spec/timetastic_spec.rb
|
39
|
+
homepage: https://github.com/amireh/timetastic
|
40
|
+
licenses: []
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options: []
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
none: false
|
47
|
+
requirements:
|
48
|
+
- - ! '>='
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '0'
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ! '>='
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
requirements: []
|
58
|
+
rubyforge_project:
|
59
|
+
rubygems_version: 1.8.23
|
60
|
+
signing_key:
|
61
|
+
specification_version: 3
|
62
|
+
summary: Utility collection of relative date selectors similar to ActiveRecord's.
|
63
|
+
test_files:
|
64
|
+
- spec/timetastic_spec.rb
|