chronic 0.2.0 → 0.2.3
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/History.txt +19 -4
- data/Manifest.txt +2 -0
- data/Rakefile +3 -25
- data/lib/chronic/chronic.rb +1 -1
- data/lib/chronic/handlers.rb +71 -16
- data/lib/chronic/repeater.rb +1 -0
- data/lib/chronic/repeaters/repeater_day.rb +6 -6
- data/lib/chronic/repeaters/repeater_day_name.rb +2 -2
- data/lib/chronic/repeaters/repeater_day_portion.rb +10 -10
- data/lib/chronic/repeaters/repeater_fortnight.rb +2 -2
- data/lib/chronic/repeaters/repeater_hour.rb +5 -5
- data/lib/chronic/repeaters/repeater_minute.rb +16 -6
- data/lib/chronic/repeaters/repeater_month.rb +14 -10
- data/lib/chronic/repeaters/repeater_month_name.rb +10 -10
- data/lib/chronic/repeaters/repeater_time.rb +10 -7
- data/lib/chronic/repeaters/repeater_year.rb +12 -12
- data/lib/chronic/time_zone.rb +22 -0
- data/lib/chronic.rb +54 -1
- data/test/test_Time.rb +50 -0
- data/test/test_parsing.rb +65 -1
- metadata +15 -9
data/History.txt
CHANGED
|
@@ -1,11 +1,26 @@
|
|
|
1
|
-
= 0.2.
|
|
1
|
+
= 0.2.3
|
|
2
|
+
|
|
3
|
+
* fixed 12am/12pm (by Nicholas Schlueter)
|
|
4
|
+
|
|
5
|
+
= 0.2.2
|
|
6
|
+
|
|
7
|
+
* added missing files (damn you manifest)
|
|
8
|
+
|
|
9
|
+
= 0.2.1
|
|
2
10
|
|
|
3
11
|
* fixed time overflow issue
|
|
4
|
-
* implemented
|
|
12
|
+
* implemented "next" for minute repeater
|
|
13
|
+
* generalized time dealiasing to dealias regardless of day portion and time position
|
|
14
|
+
* added additional token match for cases like "friday evening at 7" and "tomorrow evening at 7"
|
|
15
|
+
* added support for Time#to_s output format: "Mon Apr 02 17:00:00 PDT 2007"
|
|
16
|
+
|
|
17
|
+
= 0.2.0 2007-03-20
|
|
18
|
+
|
|
19
|
+
* implemented numerizer, allowing the use of number words (e.g. five weeks ago) (by shalev)
|
|
5
20
|
|
|
6
21
|
= 0.1.6 2006-01-15
|
|
7
22
|
|
|
8
|
-
* added 'weekend' support (eventualbuddha)
|
|
23
|
+
* added 'weekend' support (by eventualbuddha)
|
|
9
24
|
|
|
10
25
|
= 0.1.5 2006-12-20
|
|
11
26
|
|
|
@@ -20,7 +35,7 @@
|
|
|
20
35
|
|
|
21
36
|
= 0.1.3
|
|
22
37
|
|
|
23
|
-
* improved regexes for word variations (Josh Goebel)
|
|
38
|
+
* improved regexes for word variations (by Josh Goebel)
|
|
24
39
|
* fixed a bug that caused "today at 3am" to return nil if current time is after 3am
|
|
25
40
|
|
|
26
41
|
= 0.1.2
|
data/Manifest.txt
CHANGED
|
@@ -26,6 +26,7 @@ lib/chronic/repeaters/repeater_weekend.rb
|
|
|
26
26
|
lib/chronic/repeaters/repeater_year.rb
|
|
27
27
|
lib/chronic/scalar.rb
|
|
28
28
|
lib/chronic/separator.rb
|
|
29
|
+
lib/chronic/time_zone.rb
|
|
29
30
|
lib/numerizer/numerizer.rb
|
|
30
31
|
test/suite.rb
|
|
31
32
|
test/test_Chronic.rb
|
|
@@ -41,5 +42,6 @@ test/test_RepeaterWeek.rb
|
|
|
41
42
|
test/test_RepeaterWeekend.rb
|
|
42
43
|
test/test_RepeaterYear.rb
|
|
43
44
|
test/test_Span.rb
|
|
45
|
+
test/test_Time.rb
|
|
44
46
|
test/test_Token.rb
|
|
45
47
|
test/test_parsing.rb
|
data/Rakefile
CHANGED
|
@@ -7,6 +7,8 @@ require './lib/chronic.rb'
|
|
|
7
7
|
Hoe.new('chronic', Chronic::VERSION) do |p|
|
|
8
8
|
p.rubyforge_name = 'chronic'
|
|
9
9
|
p.summary = 'A natural language date parser'
|
|
10
|
+
p.author = 'Tom Preston-Werner'
|
|
11
|
+
p.email = 'tom@rubyisawesome.com'
|
|
10
12
|
p.description = p.paragraphs_of('README.txt', 2).join("\n\n")
|
|
11
13
|
p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
|
|
12
14
|
p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
|
|
@@ -14,28 +16,4 @@ Hoe.new('chronic', Chronic::VERSION) do |p|
|
|
|
14
16
|
p.extra_deps = []
|
|
15
17
|
end
|
|
16
18
|
|
|
17
|
-
# vim: syntax=Ruby
|
|
18
|
-
|
|
19
|
-
__END__
|
|
20
|
-
|
|
21
|
-
require 'rubygems'
|
|
22
|
-
|
|
23
|
-
SPEC = Gem::Specification.new do |s|
|
|
24
|
-
s.name = 'chronic'
|
|
25
|
-
s.version = '0.1.5'
|
|
26
|
-
s.author = 'Tom Preston-Werner'
|
|
27
|
-
s.email = 'tom@rubyisawesome.com'
|
|
28
|
-
s.homepage = 'http://chronic.rubyforge.org'
|
|
29
|
-
s.platform = Gem::Platform::RUBY
|
|
30
|
-
s.summary = "A natural language date parser"
|
|
31
|
-
candidates = Dir["{lib,test}/**/*"]
|
|
32
|
-
s.files = candidates.delete_if do |item|
|
|
33
|
-
item.include?('.svn')
|
|
34
|
-
end
|
|
35
|
-
s.require_path = "lib"
|
|
36
|
-
s.autorequire = "chronic"
|
|
37
|
-
s.test_file = "test/suite.rb"
|
|
38
|
-
s.has_rdoc = true
|
|
39
|
-
s.extra_rdoc_files = ['README']
|
|
40
|
-
s.rdoc_options << '--main' << 'README'
|
|
41
|
-
end
|
|
19
|
+
# vim: syntax=Ruby
|
data/lib/chronic/chronic.rb
CHANGED
|
@@ -66,7 +66,7 @@ module Chronic
|
|
|
66
66
|
@tokens = tokenizer.scan(@tokens, options)
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
-
[Grabber, Pointer, Scalar, Ordinal, Separator].each do |tokenizer|
|
|
69
|
+
[Grabber, Pointer, Scalar, Ordinal, Separator, TimeZone].each do |tokenizer|
|
|
70
70
|
@tokens = tokenizer.scan(@tokens)
|
|
71
71
|
end
|
|
72
72
|
|
data/lib/chronic/handlers.rb
CHANGED
|
@@ -6,7 +6,8 @@ module Chronic
|
|
|
6
6
|
@definitions ||=
|
|
7
7
|
{:time => [Handler.new([:repeater_time, :repeater_day_portion?], nil)],
|
|
8
8
|
|
|
9
|
-
:date => [Handler.new([:repeater_month_name, :scalar_day, :scalar_year], :
|
|
9
|
+
:date => [Handler.new([:repeater_day_name, :repeater_month_name, :scalar_day, :repeater_time, :time_zone, :scalar_year], :handle_rdn_rmn_sd_t_tz_sy),
|
|
10
|
+
Handler.new([:repeater_month_name, :scalar_day, :scalar_year], :handle_rmn_sd_sy),
|
|
10
11
|
Handler.new([:repeater_month_name, :scalar_day, :scalar_year, :separator_at?, 'time?'], :handle_rmn_sd_sy),
|
|
11
12
|
Handler.new([:repeater_month_name, :scalar_day, :separator_at?, 'time?'], :handle_rmn_sd),
|
|
12
13
|
Handler.new([:repeater_month_name, :ordinal_day, :separator_at?, 'time?'], :handle_rmn_od),
|
|
@@ -17,13 +18,17 @@ module Chronic
|
|
|
17
18
|
Handler.new([:scalar_year, :separator_slash_or_dash, :scalar_month, :separator_slash_or_dash, :scalar_day, :separator_at?, 'time?'], :handle_sy_sm_sd),
|
|
18
19
|
Handler.new([:scalar_month, :separator_slash_or_dash, :scalar_year], :handle_sm_sy)],
|
|
19
20
|
|
|
21
|
+
# tonight at 7pm
|
|
20
22
|
:anchor => [Handler.new([:grabber?, :repeater, :separator_at?, :repeater?, :repeater?], :handle_r),
|
|
23
|
+
Handler.new([:grabber?, :repeater, :repeater, :separator_at?, :repeater?, :repeater?], :handle_r),
|
|
21
24
|
Handler.new([:repeater, :grabber, :repeater], :handle_r_g_r)],
|
|
22
25
|
|
|
26
|
+
# 3 weeks from now, in 2 months
|
|
23
27
|
:arrow => [Handler.new([:scalar, :repeater, :pointer], :handle_s_r_p),
|
|
24
28
|
Handler.new([:pointer, :scalar, :repeater], :handle_p_s_r),
|
|
25
29
|
Handler.new([:scalar, :repeater, :pointer, 'anchor'], :handle_s_r_p_a)],
|
|
26
30
|
|
|
31
|
+
# 3rd week in march
|
|
27
32
|
:narrow => [Handler.new([:ordinal, :repeater, :separator_in, :repeater], :handle_o_r_s_r),
|
|
28
33
|
Handler.new([:ordinal, :repeater, :grabber, :repeater], :handle_o_r_g_r)]
|
|
29
34
|
}
|
|
@@ -34,6 +39,7 @@ module Chronic
|
|
|
34
39
|
|
|
35
40
|
self.definitions[:date].each do |handler|
|
|
36
41
|
if handler.match(tokens, self.definitions)
|
|
42
|
+
puts "-date" if Chronic.debug
|
|
37
43
|
good_tokens = tokens.select { |o| !o.get_tag Separator }
|
|
38
44
|
return self.send(handler.handler_method, good_tokens, options)
|
|
39
45
|
end
|
|
@@ -43,6 +49,7 @@ module Chronic
|
|
|
43
49
|
|
|
44
50
|
self.definitions[:anchor].each do |handler|
|
|
45
51
|
if handler.match(tokens, self.definitions)
|
|
52
|
+
puts "-anchor" if Chronic.debug
|
|
46
53
|
good_tokens = tokens.select { |o| !o.get_tag Separator }
|
|
47
54
|
return self.send(handler.handler_method, good_tokens, options)
|
|
48
55
|
end
|
|
@@ -52,21 +59,24 @@ module Chronic
|
|
|
52
59
|
|
|
53
60
|
self.definitions[:arrow].each do |handler|
|
|
54
61
|
if handler.match(tokens, self.definitions)
|
|
62
|
+
puts "-arrow" if Chronic.debug
|
|
55
63
|
good_tokens = tokens.reject { |o| o.get_tag(SeparatorAt) || o.get_tag(SeparatorSlashOrDash) || o.get_tag(SeparatorComma) }
|
|
56
64
|
return self.send(handler.handler_method, good_tokens, options)
|
|
57
65
|
end
|
|
58
66
|
end
|
|
59
67
|
|
|
60
|
-
# not an arrow, let's hope it's
|
|
68
|
+
# not an arrow, let's hope it's a narrow
|
|
61
69
|
|
|
62
70
|
self.definitions[:narrow].each do |handler|
|
|
63
71
|
if handler.match(tokens, self.definitions)
|
|
72
|
+
puts "-narrow" if Chronic.debug
|
|
64
73
|
#good_tokens = tokens.select { |o| !o.get_tag Separator }
|
|
65
74
|
return self.send(handler.handler_method, tokens, options)
|
|
66
75
|
end
|
|
67
76
|
end
|
|
68
77
|
|
|
69
78
|
# I guess you're out of luck!
|
|
79
|
+
puts "-none" if Chronic.debug
|
|
70
80
|
return nil
|
|
71
81
|
end
|
|
72
82
|
|
|
@@ -122,6 +132,19 @@ module Chronic
|
|
|
122
132
|
end
|
|
123
133
|
end
|
|
124
134
|
|
|
135
|
+
def handle_rdn_rmn_sd_t_tz_sy(tokens, options) #:nodoc:
|
|
136
|
+
month = tokens[1].get_tag(RepeaterMonthName).index
|
|
137
|
+
day = tokens[2].get_tag(ScalarDay).type
|
|
138
|
+
year = tokens[5].get_tag(ScalarYear).type
|
|
139
|
+
|
|
140
|
+
begin
|
|
141
|
+
day_start = Time.local(year, month, day)
|
|
142
|
+
day_or_time(day_start, [tokens[3]], options)
|
|
143
|
+
rescue ArgumentError
|
|
144
|
+
nil
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
125
148
|
def handle_rmn_sd_sy(tokens, options) #:nodoc:
|
|
126
149
|
month = tokens[0].get_tag(RepeaterMonthName).index
|
|
127
150
|
day = tokens[1].get_tag(ScalarDay).type
|
|
@@ -333,22 +356,54 @@ module Chronic
|
|
|
333
356
|
|
|
334
357
|
def dealias_and_disambiguate_times(tokens, options) #:nodoc:
|
|
335
358
|
# handle aliases of am/pm
|
|
336
|
-
# 5:00 in the morning
|
|
337
|
-
# 7:00 in the evening
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
if
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
359
|
+
# 5:00 in the morning -> 5:00 am
|
|
360
|
+
# 7:00 in the evening -> 7:00 pm
|
|
361
|
+
|
|
362
|
+
day_portion_index = nil
|
|
363
|
+
tokens.each_with_index do |t, i|
|
|
364
|
+
if t.get_tag(RepeaterDayPortion)
|
|
365
|
+
day_portion_index = i
|
|
366
|
+
break
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
time_index = nil
|
|
371
|
+
tokens.each_with_index do |t, i|
|
|
372
|
+
if t.get_tag(RepeaterTime)
|
|
373
|
+
time_index = i
|
|
374
|
+
break
|
|
375
|
+
end
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
if (day_portion_index && time_index)
|
|
379
|
+
t1 = tokens[day_portion_index]
|
|
380
|
+
t1tag = t1.get_tag(RepeaterDayPortion)
|
|
381
|
+
|
|
382
|
+
if [:morning].include?(t1tag.type)
|
|
383
|
+
puts '--morning->am' if Chronic.debug
|
|
384
|
+
t1.untag(RepeaterDayPortion)
|
|
385
|
+
t1.tag(RepeaterDayPortion.new(:am))
|
|
386
|
+
elsif [:afternoon, :evening, :night].include?(t1tag.type)
|
|
387
|
+
puts "--#{t1tag.type}->pm" if Chronic.debug
|
|
388
|
+
t1.untag(RepeaterDayPortion)
|
|
389
|
+
t1.tag(RepeaterDayPortion.new(:pm))
|
|
349
390
|
end
|
|
350
391
|
end
|
|
351
|
-
|
|
392
|
+
|
|
393
|
+
# tokens.each_with_index do |t0, i|
|
|
394
|
+
# t1 = tokens[i + 1]
|
|
395
|
+
# if t1 && (t1tag = t1.get_tag(RepeaterDayPortion)) && t0.get_tag(RepeaterTime)
|
|
396
|
+
# if [:morning].include?(t1tag.type)
|
|
397
|
+
# puts '--morning->am' if Chronic.debug
|
|
398
|
+
# t1.untag(RepeaterDayPortion)
|
|
399
|
+
# t1.tag(RepeaterDayPortion.new(:am))
|
|
400
|
+
# elsif [:afternoon, :evening, :night].include?(t1tag.type)
|
|
401
|
+
# puts "--#{t1tag.type}->pm" if Chronic.debug
|
|
402
|
+
# t1.untag(RepeaterDayPortion)
|
|
403
|
+
# t1.tag(RepeaterDayPortion.new(:pm))
|
|
404
|
+
# end
|
|
405
|
+
# end
|
|
406
|
+
# end
|
|
352
407
|
|
|
353
408
|
# handle ambiguous times if :ambiguous_time_range is specified
|
|
354
409
|
if options[:ambiguous_time_range] != :none
|
data/lib/chronic/repeater.rb
CHANGED
|
@@ -33,6 +33,7 @@ class Chronic::Repeater < Chronic::Tag #:nodoc:
|
|
|
33
33
|
def self.scan_for_day_names(token)
|
|
34
34
|
scanner = {/^m[ou]n(day)?$/ => :monday,
|
|
35
35
|
/^t(ue|eu|oo|u|)s(day)?$/ => :tuesday,
|
|
36
|
+
/^tue$/ => :tuesday,
|
|
36
37
|
/^we(dnes|nds|nns)day$/ => :wednesday,
|
|
37
38
|
/^wed$/ => :wednesday,
|
|
38
39
|
/^th(urs|ers)day$/ => :thursday,
|
|
@@ -19,14 +19,14 @@ class Chronic::RepeaterDay < Chronic::Repeater #:nodoc:
|
|
|
19
19
|
|
|
20
20
|
case pointer
|
|
21
21
|
when :future
|
|
22
|
-
day_begin = Time.
|
|
23
|
-
day_end = Time.
|
|
22
|
+
day_begin = Time.construct(@now.year, @now.month, @now.day, @now.hour + 1)
|
|
23
|
+
day_end = Time.construct(@now.year, @now.month, @now.day) + DAY_SECONDS
|
|
24
24
|
when :past
|
|
25
|
-
day_begin = Time.
|
|
26
|
-
day_end = Time.
|
|
25
|
+
day_begin = Time.construct(@now.year, @now.month, @now.day)
|
|
26
|
+
day_end = Time.construct(@now.year, @now.month, @now.day, @now.hour)
|
|
27
27
|
when :none
|
|
28
|
-
day_begin = Time.
|
|
29
|
-
day_end = Time.
|
|
28
|
+
day_begin = Time.construct(@now.year, @now.month, @now.day)
|
|
29
|
+
day_end = Time.construct(@now.year, @now.month, @now.day) + DAY_SECONDS
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
Chronic::Span.new(day_begin, day_end)
|
|
@@ -7,7 +7,7 @@ class Chronic::RepeaterDayName < Chronic::Repeater #:nodoc:
|
|
|
7
7
|
direction = pointer == :future ? 1 : -1
|
|
8
8
|
|
|
9
9
|
if !@current_day_start
|
|
10
|
-
@current_day_start = Time.
|
|
10
|
+
@current_day_start = Time.construct(@now.year, @now.month, @now.day)
|
|
11
11
|
@current_day_start += direction * DAY_SECONDS
|
|
12
12
|
|
|
13
13
|
day_num = symbol_to_number(@type)
|
|
@@ -34,7 +34,7 @@ class Chronic::RepeaterDayName < Chronic::Repeater #:nodoc:
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def to_s
|
|
37
|
-
super << '-
|
|
37
|
+
super << '-dayname-' << @type.to_s
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
private
|
|
@@ -10,8 +10,8 @@ class Chronic::RepeaterDayPortion < Chronic::Repeater #:nodoc:
|
|
|
10
10
|
if type.kind_of? Integer
|
|
11
11
|
@range = (@type * 60 * 60)..((@type + 12) * 60 * 60)
|
|
12
12
|
else
|
|
13
|
-
lookup = {:am =>
|
|
14
|
-
:pm => (12 * 60 * 60)..(24 * 60 * 60),
|
|
13
|
+
lookup = {:am => 0..(12 * 60 * 60 - 1),
|
|
14
|
+
:pm => (12 * 60 * 60)..(24 * 60 * 60 - 1),
|
|
15
15
|
:morning => @@morning,
|
|
16
16
|
:afternoon => @@afternoon,
|
|
17
17
|
:evening => @@evening,
|
|
@@ -28,27 +28,27 @@ class Chronic::RepeaterDayPortion < Chronic::Repeater #:nodoc:
|
|
|
28
28
|
full_day = 60 * 60 * 24
|
|
29
29
|
|
|
30
30
|
if !@current_span
|
|
31
|
-
now_seconds = @now - Time.
|
|
31
|
+
now_seconds = @now - Time.construct(@now.year, @now.month, @now.day)
|
|
32
32
|
if now_seconds < @range.begin
|
|
33
33
|
case pointer
|
|
34
34
|
when :future
|
|
35
|
-
range_start = Time.
|
|
35
|
+
range_start = Time.construct(@now.year, @now.month, @now.day) + @range.begin
|
|
36
36
|
when :past
|
|
37
|
-
range_start = Time.
|
|
37
|
+
range_start = Time.construct(@now.year, @now.month, @now.day) - full_day + @range.begin
|
|
38
38
|
end
|
|
39
39
|
elsif now_seconds > @range.end
|
|
40
40
|
case pointer
|
|
41
41
|
when :future
|
|
42
|
-
range_start = Time.
|
|
42
|
+
range_start = Time.construct(@now.year, @now.month, @now.day) + full_day + @range.begin
|
|
43
43
|
when :past
|
|
44
|
-
range_start = Time.
|
|
44
|
+
range_start = Time.construct(@now.year, @now.month, @now.day) + @range.begin
|
|
45
45
|
end
|
|
46
46
|
else
|
|
47
47
|
case pointer
|
|
48
48
|
when :future
|
|
49
|
-
range_start = Time.
|
|
49
|
+
range_start = Time.construct(@now.year, @now.month, @now.day) + full_day + @range.begin
|
|
50
50
|
when :past
|
|
51
|
-
range_start = Time.
|
|
51
|
+
range_start = Time.construct(@now.year, @now.month, @now.day) - full_day + @range.begin
|
|
52
52
|
end
|
|
53
53
|
end
|
|
54
54
|
|
|
@@ -66,7 +66,7 @@ class Chronic::RepeaterDayPortion < Chronic::Repeater #:nodoc:
|
|
|
66
66
|
def this(context = :future)
|
|
67
67
|
super
|
|
68
68
|
|
|
69
|
-
range_start = Time.
|
|
69
|
+
range_start = Time.construct(@now.year, @now.month, @now.day) + @range.begin
|
|
70
70
|
@current_span = Chronic::Span.new(range_start, range_start + (@range.end - @range.begin))
|
|
71
71
|
end
|
|
72
72
|
|
|
@@ -33,7 +33,7 @@ class Chronic::RepeaterFortnight < Chronic::Repeater #:nodoc:
|
|
|
33
33
|
|
|
34
34
|
case pointer
|
|
35
35
|
when :future
|
|
36
|
-
this_fortnight_start = Time.
|
|
36
|
+
this_fortnight_start = Time.construct(@now.year, @now.month, @now.day, @now.hour) + Chronic::RepeaterHour::HOUR_SECONDS
|
|
37
37
|
sunday_repeater = Chronic::RepeaterDayName.new(:sunday)
|
|
38
38
|
sunday_repeater.start = @now
|
|
39
39
|
sunday_repeater.this(:future)
|
|
@@ -41,7 +41,7 @@ class Chronic::RepeaterFortnight < Chronic::Repeater #:nodoc:
|
|
|
41
41
|
this_fortnight_end = this_sunday_span.begin
|
|
42
42
|
Chronic::Span.new(this_fortnight_start, this_fortnight_end)
|
|
43
43
|
when :past
|
|
44
|
-
this_fortnight_end = Time.
|
|
44
|
+
this_fortnight_end = Time.construct(@now.year, @now.month, @now.day, @now.hour)
|
|
45
45
|
sunday_repeater = Chronic::RepeaterDayName.new(:sunday)
|
|
46
46
|
sunday_repeater.start = @now
|
|
47
47
|
last_sunday_span = sunday_repeater.next(:past)
|
|
@@ -7,9 +7,9 @@ class Chronic::RepeaterHour < Chronic::Repeater #:nodoc:
|
|
|
7
7
|
if !@current_hour_start
|
|
8
8
|
case pointer
|
|
9
9
|
when :future
|
|
10
|
-
@current_hour_start = Time.
|
|
10
|
+
@current_hour_start = Time.construct(@now.year, @now.month, @now.day, @now.hour + 1)
|
|
11
11
|
when :past
|
|
12
|
-
@current_hour_start = Time.
|
|
12
|
+
@current_hour_start = Time.construct(@now.year, @now.month, @now.day, @now.hour - 1)
|
|
13
13
|
end
|
|
14
14
|
else
|
|
15
15
|
direction = pointer == :future ? 1 : -1
|
|
@@ -27,10 +27,10 @@ class Chronic::RepeaterHour < Chronic::Repeater #:nodoc:
|
|
|
27
27
|
hour_start = Time.construct(@now.year, @now.month, @now.day, @now.hour, @now.min + 1)
|
|
28
28
|
hour_end = Time.construct(@now.year, @now.month, @now.day, @now.hour + 1)
|
|
29
29
|
when :past
|
|
30
|
-
hour_start = Time.
|
|
31
|
-
hour_end = Time.
|
|
30
|
+
hour_start = Time.construct(@now.year, @now.month, @now.day, @now.hour)
|
|
31
|
+
hour_end = Time.construct(@now.year, @now.month, @now.day, @now.hour, @now.min)
|
|
32
32
|
when :none
|
|
33
|
-
hour_start = Time.
|
|
33
|
+
hour_start = Time.construct(@now.year, @now.month, @now.day, @now.hour)
|
|
34
34
|
hour_end = hour_begin + HOUR_SECONDS
|
|
35
35
|
end
|
|
36
36
|
|
|
@@ -4,9 +4,19 @@ class Chronic::RepeaterMinute < Chronic::Repeater #:nodoc:
|
|
|
4
4
|
def next(pointer = :future)
|
|
5
5
|
super
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
if !@current_minute_start
|
|
8
|
+
case pointer
|
|
9
|
+
when :future
|
|
10
|
+
@current_minute_start = Time.construct(@now.year, @now.month, @now.day, @now.hour, @now.min + 1)
|
|
11
|
+
when :past
|
|
12
|
+
@current_minute_start = Time.construct(@now.year, @now.month, @now.day, @now.hour, @now.min - 1)
|
|
13
|
+
end
|
|
14
|
+
else
|
|
15
|
+
direction = pointer == :future ? 1 : -1
|
|
16
|
+
@current_minute_start += direction * MINUTE_SECONDS
|
|
17
|
+
end
|
|
8
18
|
|
|
9
|
-
|
|
19
|
+
Chronic::Span.new(@current_minute_start, @current_minute_start + MINUTE_SECONDS)
|
|
10
20
|
end
|
|
11
21
|
|
|
12
22
|
def this(pointer = :future)
|
|
@@ -15,13 +25,13 @@ class Chronic::RepeaterMinute < Chronic::Repeater #:nodoc:
|
|
|
15
25
|
case pointer
|
|
16
26
|
when :future
|
|
17
27
|
minute_begin = @now
|
|
18
|
-
minute_end = Time.
|
|
28
|
+
minute_end = Time.construct(@now.year, @now.month, @now.day, @now.hour, @now.min)
|
|
19
29
|
when :past
|
|
20
|
-
minute_begin = Time.
|
|
30
|
+
minute_begin = Time.construct(@now.year, @now.month, @now.day, @now.hour, @now.min)
|
|
21
31
|
minute_end = @now
|
|
22
32
|
when :none
|
|
23
|
-
minute_begin = Time.
|
|
24
|
-
minute_end = Time.
|
|
33
|
+
minute_begin = Time.construct(@now.year, @now.month, @now.day, @now.hour, @now.min)
|
|
34
|
+
minute_end = Time.construct(@now.year, @now.month, @now.day, @now.hour, @now.min) + MINUTE_SECONDS
|
|
25
35
|
end
|
|
26
36
|
|
|
27
37
|
Chronic::Span.new(minute_begin, minute_end)
|
|
@@ -6,12 +6,12 @@ class Chronic::RepeaterMonth < Chronic::Repeater #:nodoc:
|
|
|
6
6
|
super
|
|
7
7
|
|
|
8
8
|
if !@current_month_start
|
|
9
|
-
@current_month_start = offset_by(Time.
|
|
9
|
+
@current_month_start = offset_by(Time.construct(@now.year, @now.month), 1, pointer)
|
|
10
10
|
else
|
|
11
|
-
@current_month_start = offset_by(Time.
|
|
11
|
+
@current_month_start = offset_by(Time.construct(@current_month_start.year, @current_month_start.month), 1, pointer)
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
Chronic::Span.new(@current_month_start, Time.
|
|
14
|
+
Chronic::Span.new(@current_month_start, Time.construct(@current_month_start.year, @current_month_start.month + 1))
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def this(pointer = :future)
|
|
@@ -19,14 +19,14 @@ class Chronic::RepeaterMonth < Chronic::Repeater #:nodoc:
|
|
|
19
19
|
|
|
20
20
|
case pointer
|
|
21
21
|
when :future
|
|
22
|
-
month_start = Time.
|
|
23
|
-
month_end = self.offset_by(Time.
|
|
22
|
+
month_start = Time.construct(@now.year, @now.month, @now.day + 1)
|
|
23
|
+
month_end = self.offset_by(Time.construct(@now.year, @now.month), 1, :future)
|
|
24
24
|
when :past
|
|
25
|
-
month_start = Time.
|
|
26
|
-
month_end = Time.
|
|
25
|
+
month_start = Time.construct(@now.year, @now.month)
|
|
26
|
+
month_end = Time.construct(@now.year, @now.month, @now.day)
|
|
27
27
|
when :none
|
|
28
|
-
month_start = Time.
|
|
29
|
-
month_end = self.offset_by(Time.
|
|
28
|
+
month_start = Time.construct(@now.year, @now.month)
|
|
29
|
+
month_end = self.offset_by(Time.construct(@now.year, @now.month), 1, :future)
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
Chronic::Span.new(month_start, month_end)
|
|
@@ -48,10 +48,14 @@ class Chronic::RepeaterMonth < Chronic::Repeater #:nodoc:
|
|
|
48
48
|
new_year += 1
|
|
49
49
|
new_month -= YEAR_MONTHS
|
|
50
50
|
end
|
|
51
|
-
Time.
|
|
51
|
+
Time.construct(new_year, new_month, time.day, time.hour, time.min, time.sec)
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
def width
|
|
55
55
|
MONTH_SECONDS
|
|
56
56
|
end
|
|
57
|
+
|
|
58
|
+
def to_s
|
|
59
|
+
super << '-month'
|
|
60
|
+
end
|
|
57
61
|
end
|
|
@@ -9,30 +9,30 @@ class Chronic::RepeaterMonthName < Chronic::Repeater #:nodoc:
|
|
|
9
9
|
case pointer
|
|
10
10
|
when :future
|
|
11
11
|
if @now.month < target_month
|
|
12
|
-
@current_month_begin = Time.
|
|
12
|
+
@current_month_begin = Time.construct(@now.year, target_month)
|
|
13
13
|
else @now.month > target_month
|
|
14
|
-
@current_month_begin = Time.
|
|
14
|
+
@current_month_begin = Time.construct(@now.year + 1, target_month)
|
|
15
15
|
end
|
|
16
16
|
when :none
|
|
17
17
|
if @now.month <= target_month
|
|
18
|
-
@current_month_begin = Time.
|
|
18
|
+
@current_month_begin = Time.construct(@now.year, target_month)
|
|
19
19
|
else @now.month > target_month
|
|
20
|
-
@current_month_begin = Time.
|
|
20
|
+
@current_month_begin = Time.construct(@now.year + 1, target_month)
|
|
21
21
|
end
|
|
22
22
|
when :past
|
|
23
23
|
if @now.month > target_month
|
|
24
|
-
@current_month_begin = Time.
|
|
24
|
+
@current_month_begin = Time.construct(@now.year, target_month)
|
|
25
25
|
else @now.month < target_month
|
|
26
|
-
@current_month_begin = Time.
|
|
26
|
+
@current_month_begin = Time.construct(@now.year - 1, target_month)
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
@current_month_begin || raise("Current month should be set by now")
|
|
30
30
|
else
|
|
31
31
|
case pointer
|
|
32
32
|
when :future
|
|
33
|
-
@current_month_begin = Time.
|
|
33
|
+
@current_month_begin = Time.construct(@current_month_begin.year + 1, @current_month_begin.month)
|
|
34
34
|
when :past
|
|
35
|
-
@current_month_begin = Time.
|
|
35
|
+
@current_month_begin = Time.construct(@current_month_begin.year - 1, @current_month_begin.month)
|
|
36
36
|
end
|
|
37
37
|
end
|
|
38
38
|
|
|
@@ -47,7 +47,7 @@ class Chronic::RepeaterMonthName < Chronic::Repeater #:nodoc:
|
|
|
47
47
|
next_month_month = cur_month_month + 1
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
-
Chronic::Span.new(@current_month_begin, Time.
|
|
50
|
+
Chronic::Span.new(@current_month_begin, Time.construct(next_month_year, next_month_month))
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
def this(pointer = :future)
|
|
@@ -70,7 +70,7 @@ class Chronic::RepeaterMonthName < Chronic::Repeater #:nodoc:
|
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
def to_s
|
|
73
|
-
super << '-
|
|
73
|
+
super << '-monthname-' << @type.to_s
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
private
|
|
@@ -28,17 +28,20 @@ class Chronic::RepeaterTime < Chronic::Repeater #:nodoc:
|
|
|
28
28
|
t = time.gsub(/\:/, '')
|
|
29
29
|
@type =
|
|
30
30
|
if (1..2) === t.size
|
|
31
|
-
|
|
31
|
+
hours = t.to_i
|
|
32
|
+
hours == 12 ? Tick.new(0 * 60 * 60, true) : Tick.new(hours * 60 * 60, true)
|
|
32
33
|
elsif t.size == 3
|
|
33
34
|
Tick.new((t[0..0].to_i * 60 * 60) + (t[1..2].to_i * 60), true)
|
|
34
35
|
elsif t.size == 4
|
|
35
36
|
ambiguous = time =~ /:/ && t[0..0].to_i != 0 && t[0..1].to_i <= 12
|
|
36
|
-
|
|
37
|
+
hours = t[0..1].to_i
|
|
38
|
+
hours == 12 ? Tick.new(0 * 60 * 60 + t[2..3].to_i * 60, ambiguous) : Tick.new(hours * 60 * 60 + t[2..3].to_i * 60, ambiguous)
|
|
37
39
|
elsif t.size == 5
|
|
38
40
|
Tick.new(t[0..0].to_i * 60 * 60 + t[1..2].to_i * 60 + t[3..4].to_i, true)
|
|
39
41
|
elsif t.size == 6
|
|
40
42
|
ambiguous = time =~ /:/ && t[0..0].to_i != 0 && t[0..1].to_i <= 12
|
|
41
|
-
|
|
43
|
+
hours = t[0..1].to_i
|
|
44
|
+
hours == 12 ? Tick.new(0 * 60 * 60 + t[2..3].to_i * 60 + t[4..5].to_i, ambiguous) : Tick.new(hours * 60 * 60 + t[2..3].to_i * 60 + t[4..5].to_i, ambiguous)
|
|
42
45
|
else
|
|
43
46
|
raise("Time cannot exceed six digits")
|
|
44
47
|
end
|
|
@@ -65,21 +68,21 @@ class Chronic::RepeaterTime < Chronic::Repeater #:nodoc:
|
|
|
65
68
|
if pointer == :future
|
|
66
69
|
if @type.ambiguous?
|
|
67
70
|
[midnight + @type, midnight + half_day + @type, tomorrow_midnight + @type].each do |t|
|
|
68
|
-
(@current_time = t; throw :done) if t
|
|
71
|
+
(@current_time = t; throw :done) if t >= @now
|
|
69
72
|
end
|
|
70
73
|
else
|
|
71
74
|
[midnight + @type, tomorrow_midnight + @type].each do |t|
|
|
72
|
-
(@current_time = t; throw :done) if t
|
|
75
|
+
(@current_time = t; throw :done) if t >= @now
|
|
73
76
|
end
|
|
74
77
|
end
|
|
75
78
|
else # pointer == :past
|
|
76
79
|
if @type.ambiguous?
|
|
77
80
|
[midnight + half_day + @type, midnight + @type, yesterday_midnight + @type * 2].each do |t|
|
|
78
|
-
(@current_time = t; throw :done) if t
|
|
81
|
+
(@current_time = t; throw :done) if t <= @now
|
|
79
82
|
end
|
|
80
83
|
else
|
|
81
84
|
[midnight + @type, yesterday_midnight + @type].each do |t|
|
|
82
|
-
(@current_time = t; throw :done) if t
|
|
85
|
+
(@current_time = t; throw :done) if t <= @now
|
|
83
86
|
end
|
|
84
87
|
end
|
|
85
88
|
end
|
|
@@ -6,16 +6,16 @@ class Chronic::RepeaterYear < Chronic::Repeater #:nodoc:
|
|
|
6
6
|
if !@current_year_start
|
|
7
7
|
case pointer
|
|
8
8
|
when :future
|
|
9
|
-
@current_year_start = Time.
|
|
9
|
+
@current_year_start = Time.construct(@now.year + 1)
|
|
10
10
|
when :past
|
|
11
|
-
@current_year_start = Time.
|
|
11
|
+
@current_year_start = Time.construct(@now.year - 1)
|
|
12
12
|
end
|
|
13
13
|
else
|
|
14
14
|
diff = pointer == :future ? 1 : -1
|
|
15
|
-
@current_year_start = Time.
|
|
15
|
+
@current_year_start = Time.construct(@current_year_start.year + diff)
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
Chronic::Span.new(@current_year_start, Time.
|
|
18
|
+
Chronic::Span.new(@current_year_start, Time.construct(@current_year_start.year + 1))
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def this(pointer = :future)
|
|
@@ -23,14 +23,14 @@ class Chronic::RepeaterYear < Chronic::Repeater #:nodoc:
|
|
|
23
23
|
|
|
24
24
|
case pointer
|
|
25
25
|
when :future
|
|
26
|
-
this_year_start = Time.
|
|
27
|
-
this_year_end = Time.
|
|
26
|
+
this_year_start = Time.construct(@now.year, @now.month, @now.day) + Chronic::RepeaterDay::DAY_SECONDS
|
|
27
|
+
this_year_end = Time.construct(@now.year + 1, 1, 1)
|
|
28
28
|
when :past
|
|
29
|
-
this_year_start = Time.
|
|
30
|
-
this_year_end = Time.
|
|
29
|
+
this_year_start = Time.construct(@now.year, 1, 1)
|
|
30
|
+
this_year_end = Time.construct(@now.year, @now.month, @now.day)
|
|
31
31
|
when :none
|
|
32
|
-
this_year_start = Time.
|
|
33
|
-
this_year_end = Time.
|
|
32
|
+
this_year_start = Time.construct(@now.year, 1, 1)
|
|
33
|
+
this_year_end = Time.construct(@now.year + 1, 1, 1)
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
Chronic::Span.new(this_year_start, this_year_end)
|
|
@@ -40,10 +40,10 @@ class Chronic::RepeaterYear < Chronic::Repeater #:nodoc:
|
|
|
40
40
|
direction = pointer == :future ? 1 : -1
|
|
41
41
|
|
|
42
42
|
sb = span.begin
|
|
43
|
-
new_begin = Time.
|
|
43
|
+
new_begin = Time.construct(sb.year + (amount * direction), sb.month, sb.day, sb.hour, sb.min, sb.sec)
|
|
44
44
|
|
|
45
45
|
se = span.end
|
|
46
|
-
new_end = Time.
|
|
46
|
+
new_end = Time.construct(se.year + (amount * direction), se.month, se.day, se.hour, se.min, se.sec)
|
|
47
47
|
|
|
48
48
|
Chronic::Span.new(new_begin, new_end)
|
|
49
49
|
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Chronic
|
|
2
|
+
class TimeZone < Tag #:nodoc:
|
|
3
|
+
def self.scan(tokens)
|
|
4
|
+
tokens.each_index do |i|
|
|
5
|
+
if t = self.scan_for_all(tokens[i]) then tokens[i].tag(t); next end
|
|
6
|
+
end
|
|
7
|
+
tokens
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.scan_for_all(token)
|
|
11
|
+
scanner = {/[PMCE][DS]T/i => :tz}
|
|
12
|
+
scanner.keys.each do |scanner_item|
|
|
13
|
+
return self.new(scanner[scanner_item]) if scanner_item =~ token.word
|
|
14
|
+
end
|
|
15
|
+
return nil
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def to_s
|
|
19
|
+
'timezone'
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
data/lib/chronic.rb
CHANGED
|
@@ -34,11 +34,12 @@ require 'chronic/pointer'
|
|
|
34
34
|
require 'chronic/scalar'
|
|
35
35
|
require 'chronic/ordinal'
|
|
36
36
|
require 'chronic/separator'
|
|
37
|
+
require 'chronic/time_zone'
|
|
37
38
|
|
|
38
39
|
require 'numerizer/numerizer'
|
|
39
40
|
|
|
40
41
|
module Chronic
|
|
41
|
-
VERSION = "0.2.
|
|
42
|
+
VERSION = "0.2.3"
|
|
42
43
|
|
|
43
44
|
def self.debug; false; end
|
|
44
45
|
end
|
|
@@ -50,6 +51,33 @@ def p(val)
|
|
|
50
51
|
puts
|
|
51
52
|
end
|
|
52
53
|
|
|
54
|
+
# class Time
|
|
55
|
+
# def self.construct(year, month = 1, day = 1, hour = 0, minute = 0, second = 0)
|
|
56
|
+
# # extra_seconds = second > 60 ? second - 60 : 0
|
|
57
|
+
# # extra_minutes = minute > 59 ? minute - 59 : 0
|
|
58
|
+
# # extra_hours = hour > 23 ? hour - 23 : 0
|
|
59
|
+
# # extra_days = day >
|
|
60
|
+
#
|
|
61
|
+
# if month > 12
|
|
62
|
+
# if month % 12 == 0
|
|
63
|
+
# year += (month - 12) / 12
|
|
64
|
+
# month = 12
|
|
65
|
+
# else
|
|
66
|
+
# year += month / 12
|
|
67
|
+
# month = month % 12
|
|
68
|
+
# end
|
|
69
|
+
# end
|
|
70
|
+
#
|
|
71
|
+
# base = Time.local(year, month)
|
|
72
|
+
# puts base
|
|
73
|
+
# offset = ((day - 1) * 24 * 60 * 60) + (hour * 60 * 60) + (minute * 60) + second
|
|
74
|
+
# puts offset.to_s
|
|
75
|
+
# date = base + offset
|
|
76
|
+
# puts date
|
|
77
|
+
# date
|
|
78
|
+
# end
|
|
79
|
+
# end
|
|
80
|
+
|
|
53
81
|
class Time
|
|
54
82
|
def self.construct(year, month = 1, day = 1, hour = 0, minute = 0, second = 0)
|
|
55
83
|
if second >= 60
|
|
@@ -67,6 +95,31 @@ class Time
|
|
|
67
95
|
hour = hour % 24
|
|
68
96
|
end
|
|
69
97
|
|
|
98
|
+
# determine if there is a day overflow. this is complicated by our crappy calendar
|
|
99
|
+
# system (non-constant number of days per month)
|
|
100
|
+
day <= 56 || raise("day must be no more than 56 (makes month resolution easier)")
|
|
101
|
+
if day > 28
|
|
102
|
+
# no month ever has fewer than 28 days, so only do this if necessary
|
|
103
|
+
leap_year = (year % 4 == 0) && !(year % 100 == 0)
|
|
104
|
+
leap_year_month_days = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
|
105
|
+
common_year_month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
|
106
|
+
days_this_month = leap_year ? leap_year_month_days[month - 1] : common_year_month_days[month - 1]
|
|
107
|
+
if day > days_this_month
|
|
108
|
+
month += day / days_this_month
|
|
109
|
+
day = day % days_this_month
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
if month > 12
|
|
114
|
+
if month % 12 == 0
|
|
115
|
+
year += (month - 12) / 12
|
|
116
|
+
month = 12
|
|
117
|
+
else
|
|
118
|
+
year += month / 12
|
|
119
|
+
month = month % 12
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
70
123
|
Time.local(year, month, day, hour, minute, second)
|
|
71
124
|
end
|
|
72
125
|
end
|
data/test/test_Time.rb
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require 'chronic'
|
|
2
|
+
require 'test/unit'
|
|
3
|
+
|
|
4
|
+
class TestTime < Test::Unit::TestCase
|
|
5
|
+
|
|
6
|
+
def setup
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def test_normal
|
|
10
|
+
assert_equal Time.local(2006, 1, 2, 0, 0, 0), Time.construct(2006, 1, 2, 0, 0, 0)
|
|
11
|
+
assert_equal Time.local(2006, 1, 2, 3, 0, 0), Time.construct(2006, 1, 2, 3, 0, 0)
|
|
12
|
+
assert_equal Time.local(2006, 1, 2, 3, 4, 0), Time.construct(2006, 1, 2, 3, 4, 0)
|
|
13
|
+
assert_equal Time.local(2006, 1, 2, 3, 4, 5), Time.construct(2006, 1, 2, 3, 4, 5)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def test_second_overflow
|
|
17
|
+
assert_equal Time.local(2006, 1, 1, 0, 1, 30), Time.construct(2006, 1, 1, 0, 0, 90)
|
|
18
|
+
assert_equal Time.local(2006, 1, 1, 0, 5, 0), Time.construct(2006, 1, 1, 0, 0, 300)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_minute_overflow
|
|
22
|
+
assert_equal Time.local(2006, 1, 1, 1, 30), Time.construct(2006, 1, 1, 0, 90)
|
|
23
|
+
assert_equal Time.local(2006, 1, 1, 5), Time.construct(2006, 1, 1, 0, 300)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_hour_overflow
|
|
27
|
+
assert_equal Time.local(2006, 1, 2, 12), Time.construct(2006, 1, 1, 36)
|
|
28
|
+
assert_equal Time.local(2006, 1, 7), Time.construct(2006, 1, 1, 144)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def test_day_overflow
|
|
32
|
+
assert_equal Time.local(2006, 2, 1), Time.construct(2006, 1, 32)
|
|
33
|
+
assert_equal Time.local(2006, 3, 5), Time.construct(2006, 2, 33)
|
|
34
|
+
assert_equal Time.local(2004, 3, 4), Time.construct(2004, 2, 33)
|
|
35
|
+
assert_equal Time.local(2000, 3, 5), Time.construct(2000, 2, 33)
|
|
36
|
+
|
|
37
|
+
assert_nothing_raised do
|
|
38
|
+
Time.construct(2006, 1, 56)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
assert_raise(RuntimeError) do
|
|
42
|
+
Time.construct(2006, 1, 57)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def test_month_overflow
|
|
47
|
+
assert_equal Time.local(2006, 1), Time.construct(2005, 13)
|
|
48
|
+
assert_equal Time.local(2005, 12), Time.construct(2000, 72)
|
|
49
|
+
end
|
|
50
|
+
end
|
data/test/test_parsing.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'chronic'
|
|
2
|
+
require 'time'
|
|
2
3
|
require 'test/unit'
|
|
3
4
|
|
|
4
5
|
class TestParsing < Test::Unit::TestCase
|
|
@@ -136,6 +137,15 @@ class TestParsing < Test::Unit::TestCase
|
|
|
136
137
|
time = parse_now("2006-08-20 15:30.30")
|
|
137
138
|
assert_equal Time.local(2006, 8, 20, 15, 30, 30), time
|
|
138
139
|
|
|
140
|
+
# rdn_rm_rd_rt_rtz_ry
|
|
141
|
+
|
|
142
|
+
time = parse_now("Mon Apr 02 17:00:00 PDT 2007")
|
|
143
|
+
assert_equal Time.local(2007, 4, 2, 17), time
|
|
144
|
+
|
|
145
|
+
now = Time.now
|
|
146
|
+
time = parse_now(now.to_s)
|
|
147
|
+
assert_equal now.to_s, time.to_s
|
|
148
|
+
|
|
139
149
|
# rm_sd_rt
|
|
140
150
|
|
|
141
151
|
#time = parse_now("jan 5 13:00")
|
|
@@ -152,11 +162,18 @@ class TestParsing < Test::Unit::TestCase
|
|
|
152
162
|
time = parse_now("1800-08-20")
|
|
153
163
|
assert_equal nil, time
|
|
154
164
|
end
|
|
165
|
+
|
|
166
|
+
def test_foo
|
|
167
|
+
Chronic.parse('two months ago this friday')
|
|
168
|
+
end
|
|
155
169
|
|
|
156
170
|
def test_parse_guess_r
|
|
157
171
|
time = parse_now("friday")
|
|
158
172
|
assert_equal Time.local(2006, 8, 18, 12), time
|
|
159
173
|
|
|
174
|
+
time = parse_now("tue")
|
|
175
|
+
assert_equal Time.local(2006, 8, 22, 12), time
|
|
176
|
+
|
|
160
177
|
time = parse_now("5")
|
|
161
178
|
assert_equal Time.local(2006, 8, 16, 17), time
|
|
162
179
|
|
|
@@ -192,6 +209,18 @@ class TestParsing < Test::Unit::TestCase
|
|
|
192
209
|
time = parse_now("4 am", :ambiguous_time_range => :none)
|
|
193
210
|
assert_equal Time.local(2006, 8, 16, 4), time
|
|
194
211
|
|
|
212
|
+
time = parse_now("12 pm")
|
|
213
|
+
assert_equal Time.local(2006, 8, 16, 12), time
|
|
214
|
+
|
|
215
|
+
time = parse_now("12:01 pm")
|
|
216
|
+
assert_equal Time.local(2006, 8, 16, 12, 1), time
|
|
217
|
+
|
|
218
|
+
time = parse_now("12:01 am")
|
|
219
|
+
assert_equal Time.local(2006, 8, 16, 0, 1), time
|
|
220
|
+
|
|
221
|
+
time = parse_now("12 am")
|
|
222
|
+
assert_equal Time.local(2006, 8, 16), time
|
|
223
|
+
|
|
195
224
|
time = parse_now("4:00 in the morning")
|
|
196
225
|
assert_equal Time.local(2006, 8, 16, 4), time
|
|
197
226
|
|
|
@@ -214,6 +243,9 @@ class TestParsing < Test::Unit::TestCase
|
|
|
214
243
|
|
|
215
244
|
time = parse_now("sunday 6am")
|
|
216
245
|
assert_equal Time.local(2006, 8, 20, 6), time
|
|
246
|
+
|
|
247
|
+
time = parse_now("friday evening at 7")
|
|
248
|
+
assert_equal Time.local(2006, 8, 18, 19), time
|
|
217
249
|
end
|
|
218
250
|
|
|
219
251
|
def test_parse_guess_gr
|
|
@@ -233,6 +265,9 @@ class TestParsing < Test::Unit::TestCase
|
|
|
233
265
|
time = parse_now("this month", :context => :past)
|
|
234
266
|
assert_equal Time.local(2006, 8, 8, 12), time
|
|
235
267
|
|
|
268
|
+
time = Chronic.parse("next month", :now => Time.local(2006, 11, 15))
|
|
269
|
+
assert_equal Time.local(2006, 12, 16, 12), time
|
|
270
|
+
|
|
236
271
|
# month name
|
|
237
272
|
|
|
238
273
|
time = parse_now("last november")
|
|
@@ -310,6 +345,11 @@ class TestParsing < Test::Unit::TestCase
|
|
|
310
345
|
time = parse_now("tonight")
|
|
311
346
|
assert_equal Time.local(2006, 8, 16, 22), time
|
|
312
347
|
|
|
348
|
+
# minute
|
|
349
|
+
|
|
350
|
+
time = parse_now("next minute")
|
|
351
|
+
assert_equal Time.local(2006, 8, 16, 14, 1, 30), time
|
|
352
|
+
|
|
313
353
|
# second
|
|
314
354
|
|
|
315
355
|
time = parse_now("this second")
|
|
@@ -355,8 +395,17 @@ class TestParsing < Test::Unit::TestCase
|
|
|
355
395
|
|
|
356
396
|
time = parse_now("last week tuesday")
|
|
357
397
|
assert_equal Time.local(2006, 8, 8, 12), time
|
|
398
|
+
|
|
399
|
+
time = parse_now("tonight at 7")
|
|
400
|
+
assert_equal Time.local(2006, 8, 16, 19), time
|
|
401
|
+
|
|
402
|
+
time = parse_now("tonight 7")
|
|
403
|
+
assert_equal Time.local(2006, 8, 16, 19), time
|
|
404
|
+
|
|
405
|
+
time = parse_now("7 tonight")
|
|
406
|
+
assert_equal Time.local(2006, 8, 16, 19), time
|
|
358
407
|
end
|
|
359
|
-
|
|
408
|
+
|
|
360
409
|
def test_parse_guess_grrr
|
|
361
410
|
time = parse_now("today at 6:00pm")
|
|
362
411
|
assert_equal Time.local(2006, 8, 16, 18), time
|
|
@@ -369,6 +418,18 @@ class TestParsing < Test::Unit::TestCase
|
|
|
369
418
|
|
|
370
419
|
time = parse_now("yesterday at 4:00pm")
|
|
371
420
|
assert_equal Time.local(2006, 8, 15, 16), time
|
|
421
|
+
|
|
422
|
+
time = parse_now("tomorrow evening at 7")
|
|
423
|
+
assert_equal Time.local(2006, 8, 17, 19), time
|
|
424
|
+
|
|
425
|
+
time = parse_now("tomorrow morning at 5:30")
|
|
426
|
+
assert_equal Time.local(2006, 8, 17, 5, 30), time
|
|
427
|
+
|
|
428
|
+
time = parse_now("next monday at 12:01 am")
|
|
429
|
+
assert_equal Time.local(2006, 8, 21, 00, 1), time
|
|
430
|
+
|
|
431
|
+
time = parse_now("next monday at 12:01 pm")
|
|
432
|
+
assert_equal Time.local(2006, 8, 21, 12, 1), time
|
|
372
433
|
end
|
|
373
434
|
|
|
374
435
|
def test_parse_guess_rgr
|
|
@@ -489,6 +550,9 @@ class TestParsing < Test::Unit::TestCase
|
|
|
489
550
|
|
|
490
551
|
time = parse_now("10th wednesday in november")
|
|
491
552
|
assert_equal nil, time
|
|
553
|
+
|
|
554
|
+
# time = parse_now("3rd wednesday in 2007")
|
|
555
|
+
# assert_equal Time.local(2007, 1, 20, 12), time
|
|
492
556
|
end
|
|
493
557
|
|
|
494
558
|
def test_parse_guess_o_r_g_r
|
metadata
CHANGED
|
@@ -3,12 +3,12 @@ rubygems_version: 0.9.0
|
|
|
3
3
|
specification_version: 1
|
|
4
4
|
name: chronic
|
|
5
5
|
version: !ruby/object:Gem::Version
|
|
6
|
-
version: 0.2.
|
|
7
|
-
date: 2007-
|
|
6
|
+
version: 0.2.3
|
|
7
|
+
date: 2007-07-02 00:00:00 -07:00
|
|
8
8
|
summary: A natural language date parser
|
|
9
9
|
require_paths:
|
|
10
10
|
- lib
|
|
11
|
-
email:
|
|
11
|
+
email: tom@rubyisawesome.com
|
|
12
12
|
homepage: "\thttp://chronic.rubyforge.org/"
|
|
13
13
|
rubyforge_project: chronic
|
|
14
14
|
description: Chronic is a natural language date/time parser written in pure Ruby. See below for the wide variety of formats Chronic will parse.
|
|
@@ -27,7 +27,7 @@ signing_key:
|
|
|
27
27
|
cert_chain:
|
|
28
28
|
post_install_message:
|
|
29
29
|
authors:
|
|
30
|
-
-
|
|
30
|
+
- Tom Preston-Werner
|
|
31
31
|
files:
|
|
32
32
|
- History.txt
|
|
33
33
|
- Manifest.txt
|
|
@@ -57,6 +57,7 @@ files:
|
|
|
57
57
|
- lib/chronic/repeaters/repeater_year.rb
|
|
58
58
|
- lib/chronic/scalar.rb
|
|
59
59
|
- lib/chronic/separator.rb
|
|
60
|
+
- lib/chronic/time_zone.rb
|
|
60
61
|
- lib/numerizer/numerizer.rb
|
|
61
62
|
- test/suite.rb
|
|
62
63
|
- test/test_Chronic.rb
|
|
@@ -72,6 +73,7 @@ files:
|
|
|
72
73
|
- test/test_RepeaterWeekend.rb
|
|
73
74
|
- test/test_RepeaterYear.rb
|
|
74
75
|
- test/test_Span.rb
|
|
76
|
+
- test/test_Time.rb
|
|
75
77
|
- test/test_Token.rb
|
|
76
78
|
- test/test_parsing.rb
|
|
77
79
|
test_files:
|
|
@@ -89,11 +91,15 @@ test_files:
|
|
|
89
91
|
- test/test_RepeaterWeekend.rb
|
|
90
92
|
- test/test_RepeaterYear.rb
|
|
91
93
|
- test/test_Span.rb
|
|
94
|
+
- test/test_Time.rb
|
|
92
95
|
- test/test_Token.rb
|
|
93
|
-
rdoc_options:
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
rdoc_options:
|
|
97
|
+
- --main
|
|
98
|
+
- README.txt
|
|
99
|
+
extra_rdoc_files:
|
|
100
|
+
- History.txt
|
|
101
|
+
- Manifest.txt
|
|
102
|
+
- README.txt
|
|
97
103
|
executables: []
|
|
98
104
|
|
|
99
105
|
extensions: []
|
|
@@ -108,5 +114,5 @@ dependencies:
|
|
|
108
114
|
requirements:
|
|
109
115
|
- - ">="
|
|
110
116
|
- !ruby/object:Gem::Version
|
|
111
|
-
version: 1.2.
|
|
117
|
+
version: 1.2.1
|
|
112
118
|
version:
|