public
Rubygem
Description: Chronic is a pure Ruby natural language date parser.
Homepage: http://chronic.rubyforge.org
Clone URL: git://github.com/mojombo/chronic.git
svrc updates
mojombo (author)
Sat Jun 16 14:15:00 -0700 2007
commit  f4fef51a3a315e08132366506cd76a9a893689e5
tree    d2fb7d133c63c6953e765610248082d2eedc4fe7
parent  044fbd787e3de2509dd69d386df78aa6c2acfddb
...
1
 
 
 
 
 
2
3
4
 
 
 
 
 
 
 
 
5
6
7
8
 
9
10
11
...
20
21
22
23
 
24
25
26
...
 
1
2
3
4
5
6
7
 
8
9
10
11
12
13
14
15
16
17
18
 
19
20
21
22
...
31
32
33
 
34
35
36
37
0
@@ -1,11 +1,22 @@
0
-= 0.2.0 2007-03-20
0
+= 0.2.2
0
+
0
+* added missing files (damn you manifest
0
+
0
+= 0.2.1
0
 
0
 * fixed time overflow issue
0
-* implemented numerizer, allowing the use of number words (e.g. five weeks ago) (thanks shalev!)
0
+* implemented "next" for minute repeater
0
+* generalized time dealiasing to dealias regardless of day portion and time position
0
+* added additional token match for cases like "friday evening at 7" and "tomorrow evening at 7"
0
+* added support for Time#to_s output format: "Mon Apr 02 17:00:00 PDT 2007"
0
+
0
+= 0.2.0 2007-03-20
0
+
0
+* implemented numerizer, allowing the use of number words (e.g. five weeks ago) (by shalev)
0
 
0
 = 0.1.6 2006-01-15
0
 
0
-* added 'weekend' support (eventualbuddha)
0
+* added 'weekend' support (by eventualbuddha)
0
 
0
 = 0.1.5 2006-12-20
0
 
0
@@ -20,7 +31,7 @@
0
 
0
 = 0.1.3
0
 
0
-* improved regexes for word variations (Josh Goebel)
0
+* improved regexes for word variations (by Josh Goebel)
0
 * fixed a bug that caused "today at 3am" to return nil if current time is after 3am
0
 
0
 = 0.1.2
...
26
27
28
 
29
30
31
...
41
42
43
 
44
45
...
26
27
28
29
30
31
32
...
42
43
44
45
46
47
0
@@ -26,6 +26,7 @@ lib/chronic/repeaters/repeater_weekend.rb
0
 lib/chronic/repeaters/repeater_year.rb
0
 lib/chronic/scalar.rb
0
 lib/chronic/separator.rb
0
+lib/chronic/time_zone.rb
0
 lib/numerizer/numerizer.rb
0
 test/suite.rb
0
 test/test_Chronic.rb
0
@@ -41,5 +42,6 @@ test/test_RepeaterWeek.rb
0
 test/test_RepeaterWeekend.rb
0
 test/test_RepeaterYear.rb
0
 test/test_Span.rb
0
+test/test_Time.rb
0
 test/test_Token.rb
0
 test/test_parsing.rb
...
34
35
36
 
37
38
39
40
41
 
42
43
44
...
50
51
52
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
54
55
...
34
35
36
37
38
39
40
41
 
42
43
44
45
...
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
0
@@ -34,11 +34,12 @@ require 'chronic/pointer'
0
 require 'chronic/scalar'
0
 require 'chronic/ordinal'
0
 require 'chronic/separator'
0
+require 'chronic/time_zone'
0
 
0
 require 'numerizer/numerizer'
0
 
0
 module Chronic
0
- VERSION = "0.2.0"
0
+ VERSION = "0.2.2"
0
   
0
   def self.debug; false; end
0
 end
0
@@ -50,6 +51,33 @@ def p(val)
0
   puts
0
 end
0
 
0
+# class Time
0
+# def self.construct(year, month = 1, day = 1, hour = 0, minute = 0, second = 0)
0
+# # extra_seconds = second > 60 ? second - 60 : 0
0
+# # extra_minutes = minute > 59 ? minute - 59 : 0
0
+# # extra_hours = hour > 23 ? hour - 23 : 0
0
+# # extra_days = day >
0
+#
0
+# if month > 12
0
+# if month % 12 == 0
0
+# year += (month - 12) / 12
0
+# month = 12
0
+# else
0
+# year += month / 12
0
+# month = month % 12
0
+# end
0
+# end
0
+#
0
+# base = Time.local(year, month)
0
+# puts base
0
+# offset = ((day - 1) * 24 * 60 * 60) + (hour * 60 * 60) + (minute * 60) + second
0
+# puts offset.to_s
0
+# date = base + offset
0
+# puts date
0
+# date
0
+# end
0
+# end
0
+
0
 class Time
0
   def self.construct(year, month = 1, day = 1, hour = 0, minute = 0, second = 0)
0
     if second >= 60
...
66
67
68
69
 
70
71
72
...
66
67
68
 
69
70
71
72
0
@@ -66,7 +66,7 @@ module Chronic
0
         @tokens = tokenizer.scan(@tokens, options)
0
       end
0
       
0
- [Grabber, Pointer, Scalar, Ordinal, Separator].each do |tokenizer|
0
+ [Grabber, Pointer, Scalar, Ordinal, Separator, TimeZone].each do |tokenizer|
0
         @tokens = tokenizer.scan(@tokens)
0
       end
0
       
...
6
7
8
9
 
 
10
11
12
...
17
18
19
 
20
 
21
22
 
23
24
25
26
 
27
28
29
...
34
35
36
 
37
38
39
...
43
44
45
 
46
47
48
...
52
53
54
 
55
56
57
58
59
60
 
61
62
63
 
64
65
66
67
68
69
 
70
71
72
...
122
123
124
 
 
 
 
 
 
 
 
 
 
 
 
 
125
126
127
...
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
350
351
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
352
353
354
...
6
7
8
 
9
10
11
12
13
...
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
...
39
40
41
42
43
44
45
...
49
50
51
52
53
54
55
...
59
60
61
62
63
64
65
66
67
 
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
...
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
...
356
357
358
 
 
 
 
 
 
 
 
 
 
 
 
 
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
 
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
0
@@ -6,7 +6,8 @@ module Chronic
0
    @definitions ||=
0
       {:time => [Handler.new([:repeater_time, :repeater_day_portion?], nil)],
0
         
0
- :date => [Handler.new([:repeater_month_name, :scalar_day, :scalar_year], :handle_rmn_sd_sy),
0
+ :date => [Handler.new([:repeater_day_name, :repeater_month_name, :scalar_day, :repeater_time, :time_zone, :scalar_year], :handle_rdn_rmn_sd_t_tz_sy),
0
+ Handler.new([:repeater_month_name, :scalar_day, :scalar_year], :handle_rmn_sd_sy),
0
                  Handler.new([:repeater_month_name, :scalar_day, :scalar_year, :separator_at?, 'time?'], :handle_rmn_sd_sy),
0
                  Handler.new([:repeater_month_name, :scalar_day, :separator_at?, 'time?'], :handle_rmn_sd),
0
                  Handler.new([:repeater_month_name, :ordinal_day, :separator_at?, 'time?'], :handle_rmn_od),
0
@@ -17,13 +18,17 @@ module Chronic
0
                  Handler.new([:scalar_year, :separator_slash_or_dash, :scalar_month, :separator_slash_or_dash, :scalar_day, :separator_at?, 'time?'], :handle_sy_sm_sd),
0
                  Handler.new([:scalar_month, :separator_slash_or_dash, :scalar_year], :handle_sm_sy)],
0
                  
0
+ # tonight at 7pm
0
        :anchor => [Handler.new([:grabber?, :repeater, :separator_at?, :repeater?, :repeater?], :handle_r),
0
+ Handler.new([:grabber?, :repeater, :repeater, :separator_at?, :repeater?, :repeater?], :handle_r),
0
                    Handler.new([:repeater, :grabber, :repeater], :handle_r_g_r)],
0
                    
0
+ # 3 weeks from now, in 2 months
0
        :arrow => [Handler.new([:scalar, :repeater, :pointer], :handle_s_r_p),
0
                   Handler.new([:pointer, :scalar, :repeater], :handle_p_s_r),
0
                   Handler.new([:scalar, :repeater, :pointer, 'anchor'], :handle_s_r_p_a)],
0
                   
0
+ # 3rd week in march
0
        :narrow => [Handler.new([:ordinal, :repeater, :separator_in, :repeater], :handle_o_r_s_r),
0
                    Handler.new([:ordinal, :repeater, :grabber, :repeater], :handle_o_r_g_r)]
0
       }
0
@@ -34,6 +39,7 @@ module Chronic
0
       
0
       self.definitions[:date].each do |handler|
0
         if handler.match(tokens, self.definitions)
0
+ puts "-date" if Chronic.debug
0
           good_tokens = tokens.select { |o| !o.get_tag Separator }
0
           return self.send(handler.handler_method, good_tokens, options)
0
         end
0
@@ -43,6 +49,7 @@ module Chronic
0
             
0
       self.definitions[:anchor].each do |handler|
0
         if handler.match(tokens, self.definitions)
0
+ puts "-anchor" if Chronic.debug
0
           good_tokens = tokens.select { |o| !o.get_tag Separator }
0
           return self.send(handler.handler_method, good_tokens, options)
0
         end
0
@@ -52,21 +59,24 @@ module Chronic
0
       
0
       self.definitions[:arrow].each do |handler|
0
         if handler.match(tokens, self.definitions)
0
+ puts "-arrow" if Chronic.debug
0
           good_tokens = tokens.reject { |o| o.get_tag(SeparatorAt) || o.get_tag(SeparatorSlashOrDash) || o.get_tag(SeparatorComma) }
0
           return self.send(handler.handler_method, good_tokens, options)
0
         end
0
       end
0
       
0
- # not an arrow, let's hope it's an narrow
0
+ # not an arrow, let's hope it's a narrow
0
       
0
       self.definitions[:narrow].each do |handler|
0
         if handler.match(tokens, self.definitions)
0
+ puts "-narrow" if Chronic.debug
0
           #good_tokens = tokens.select { |o| !o.get_tag Separator }
0
           return self.send(handler.handler_method, tokens, options)
0
         end
0
       end
0
       
0
       # I guess you're out of luck!
0
+ puts "-none" if Chronic.debug
0
       return nil
0
     end
0
     
0
@@ -122,6 +132,19 @@ module Chronic
0
       end
0
     end
0
     
0
+ def handle_rdn_rmn_sd_t_tz_sy(tokens, options) #:nodoc:
0
+ month = tokens[1].get_tag(RepeaterMonthName).index
0
+ day = tokens[2].get_tag(ScalarDay).type
0
+ year = tokens[5].get_tag(ScalarYear).type
0
+
0
+ begin
0
+ day_start = Time.local(year, month, day)
0
+ day_or_time(day_start, [tokens[3]], options)
0
+ rescue ArgumentError
0
+ nil
0
+ end
0
+ end
0
+
0
     def handle_rmn_sd_sy(tokens, options) #:nodoc:
0
       month = tokens[0].get_tag(RepeaterMonthName).index
0
       day = tokens[1].get_tag(ScalarDay).type
0
@@ -333,22 +356,54 @@ module Chronic
0
     
0
     def dealias_and_disambiguate_times(tokens, options) #:nodoc:
0
       # handle aliases of am/pm
0
- # 5:00 in the morning => 5:00 am
0
- # 7:00 in the evening => 7:00 pm
0
- #ttokens = []
0
- tokens.each_with_index do |t0, i|
0
- t1 = tokens[i + 1]
0
- if t1 && (t1tag = t1.get_tag(RepeaterDayPortion)) && t0.get_tag(RepeaterTime)
0
- if [:morning].include?(t1tag.type)
0
- t1.untag(RepeaterDayPortion)
0
- t1.tag(RepeaterDayPortion.new(:am))
0
- elsif [:afternoon, :evening, :night].include?(t1tag.type)
0
- t1.untag(RepeaterDayPortion)
0
- t1.tag(RepeaterDayPortion.new(:pm))
0
- end
0
+ # 5:00 in the morning -> 5:00 am
0
+ # 7:00 in the evening -> 7:00 pm
0
+
0
+ day_portion_index = nil
0
+ tokens.each_with_index do |t, i|
0
+ if t.get_tag(RepeaterDayPortion)
0
+ day_portion_index = i
0
+ break
0
+ end
0
+ end
0
+
0
+ time_index = nil
0
+ tokens.each_with_index do |t, i|
0
+ if t.get_tag(RepeaterTime)
0
+ time_index = i
0
+ break
0
+ end
0
+ end
0
+
0
+ if (day_portion_index && time_index)
0
+ t1 = tokens[day_portion_index]
0
+ t1tag = t1.get_tag(RepeaterDayPortion)
0
+
0
+ if [:morning].include?(t1tag.type)
0
+ puts '--morning->am' if Chronic.debug
0
+ t1.untag(RepeaterDayPortion)
0
+ t1.tag(RepeaterDayPortion.new(:am))
0
+ elsif [:afternoon, :evening, :night].include?(t1tag.type)
0
+ puts "--#{t1tag.type}->pm" if Chronic.debug
0
+ t1.untag(RepeaterDayPortion)
0
+ t1.tag(RepeaterDayPortion.new(:pm))
0
         end
0
       end
0
- #tokens = ttokens
0
+
0
+ # tokens.each_with_index do |t0, i|
0
+ # t1 = tokens[i + 1]
0
+ # if t1 && (t1tag = t1.get_tag(RepeaterDayPortion)) && t0.get_tag(RepeaterTime)
0
+ # if [:morning].include?(t1tag.type)
0
+ # puts '--morning->am' if Chronic.debug
0
+ # t1.untag(RepeaterDayPortion)
0
+ # t1.tag(RepeaterDayPortion.new(:am))
0
+ # elsif [:afternoon, :evening, :night].include?(t1tag.type)
0
+ # puts "--#{t1tag.type}->pm" if Chronic.debug
0
+ # t1.untag(RepeaterDayPortion)
0
+ # t1.tag(RepeaterDayPortion.new(:pm))
0
+ # end
0
+ # end
0
+ # end
0
             
0
       # handle ambiguous times if :ambiguous_time_range is specified
0
       if options[:ambiguous_time_range] != :none
...
33
34
35
 
36
37
38
...
33
34
35
36
37
38
39
0
@@ -33,6 +33,7 @@ class Chronic::Repeater < Chronic::Tag #:nodoc:
0
   def self.scan_for_day_names(token)
0
     scanner = {/^m[ou]n(day)?$/ => :monday,
0
                /^t(ue|eu|oo|u|)s(day)?$/ => :tuesday,
0
+ /^tue$/ => :tuesday,
0
                /^we(dnes|nds|nns)day$/ => :wednesday,
0
                /^wed$/ => :wednesday,
0
                /^th(urs|ers)day$/ => :thursday,
...
34
35
36
37
 
38
39
40
...
34
35
36
 
37
38
39
40
0
@@ -34,7 +34,7 @@ class Chronic::RepeaterDayName < Chronic::Repeater #:nodoc:
0
   end
0
   
0
   def to_s
0
- super << '-dayofweek-' << @type.to_s
0
+ super << '-dayname-' << @type.to_s
0
   end
0
   
0
   private
...
4
5
6
7
 
 
 
 
 
 
 
 
 
 
 
8
9
 
10
11
12
...
4
5
6
 
7
8
9
10
11
12
13
14
15
16
17
18
 
19
20
21
22
0
@@ -4,9 +4,19 @@ class Chronic::RepeaterMinute < Chronic::Repeater #:nodoc:
0
   def next(pointer = :future)
0
     super
0
     
0
- direction = pointer == :future ? 1 : -1
0
+ if !@current_minute_start
0
+ case pointer
0
+ when :future
0
+ @current_minute_start = Time.construct(@now.year, @now.month, @now.day, @now.hour, @now.min + 1)
0
+ when :past
0
+ @current_minute_start = Time.construct(@now.year, @now.month, @now.day, @now.hour, @now.min - 1)
0
+ end
0
+ else
0
+ direction = pointer == :future ? 1 : -1
0
+ @current_minute_start += direction * MINUTE_SECONDS
0
+ end
0
     
0
- raise 'not implemented'
0
+ Chronic::Span.new(@current_minute_start, @current_minute_start + MINUTE_SECONDS)
0
   end
0
   
0
   def this(pointer = :future)
...
54
55
56
 
 
 
 
57
58
...
54
55
56
57
58
59
60
61
62
0
@@ -54,4 +54,8 @@ class Chronic::RepeaterMonth < Chronic::Repeater #:nodoc:
0
   def width
0
     MONTH_SECONDS
0
   end
0
+
0
+ def to_s
0
+ super << '-month'
0
+ end
0
 end
0
\ No newline at end of file
...
70
71
72
73
 
74
75
76
...
70
71
72
 
73
74
75
76
0
@@ -70,7 +70,7 @@ class Chronic::RepeaterMonthName < Chronic::Repeater #:nodoc:
0
   end
0
   
0
   def to_s
0
- super << '-month-' << @type.to_s
0
+ super << '-monthname-' << @type.to_s
0
   end
0
   
0
   private
...
136
137
138
 
 
 
 
 
 
 
 
 
139
140
141
...
152
153
154
 
 
 
 
155
156
157
158
159
 
 
 
160
161
162
...
214
215
216
 
 
 
217
218
219
...
313
314
315
 
 
 
 
 
316
317
318
...
358
359
360
 
 
 
 
 
 
 
 
 
361
362
 
363
364
365
...
372
373
374
 
 
 
 
 
 
375
376
377
...
492
493
494
 
 
 
495
496
497
...
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
...
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
...
230
231
232
233
234
235
236
237
238
...
332
333
334
335
336
337
338
339
340
341
342
...
382
383
384
385
386
387
388
389
390
391
392
393
394
 
395
396
397
398
...
405
406
407
408
409
410
411
412
413
414
415
416
...
531
532
533
534
535
536
537
538
539
0
@@ -136,6 +136,15 @@ class TestParsing < Test::Unit::TestCase
0
     time = parse_now("2006-08-20 15:30.30")
0
     assert_equal Time.local(2006, 8, 20, 15, 30, 30), time
0
     
0
+ # rdn_rm_rd_rt_rtz_ry
0
+
0
+ time = parse_now("Mon Apr 02 17:00:00 PDT 2007")
0
+ assert_equal Time.local(2007, 4, 2, 17), time
0
+
0
+ now = Time.now
0
+ time = parse_now(now.to_s)
0
+ assert_equal now.to_s, time.to_s
0
+
0
     # rm_sd_rt
0
     
0
     #time = parse_now("jan 5 13:00")
0
@@ -152,11 +161,18 @@ class TestParsing < Test::Unit::TestCase
0
     time = parse_now("1800-08-20")
0
     assert_equal nil, time
0
   end
0
+
0
+ def test_foo
0
+ Chronic.parse('two months ago this friday')
0
+ end
0
 
0
   def test_parse_guess_r
0
     time = parse_now("friday")
0
     assert_equal Time.local(2006, 8, 18, 12), time
0
     
0
+ time = parse_now("tue")
0
+ assert_equal Time.local(2006, 8, 22, 12), time
0
+
0
     time = parse_now("5")
0
     assert_equal Time.local(2006, 8, 16, 17), time
0
     
0
@@ -214,6 +230,9 @@ class TestParsing < Test::Unit::TestCase
0
     
0
     time = parse_now("sunday 6am")
0
     assert_equal Time.local(2006, 8, 20, 6), time
0
+
0
+ time = parse_now("friday evening at 7")
0
+ assert_equal Time.local(2006, 8, 18, 19), time
0
   end
0
   
0
   def test_parse_guess_gr
0
@@ -313,6 +332,11 @@ class TestParsing < Test::Unit::TestCase
0
     time = parse_now("tonight")
0
     assert_equal Time.local(2006, 8, 16, 22), time
0
     
0
+ # minute
0
+
0
+ time = parse_now("next minute")
0
+ assert_equal Time.local(2006, 8, 16, 14, 1, 30), time
0
+
0
     # second
0
     
0
     time = parse_now("this second")
0
@@ -358,8 +382,17 @@ class TestParsing < Test::Unit::TestCase
0
     
0
     time = parse_now("last week tuesday")
0
     assert_equal Time.local(2006, 8, 8, 12), time
0
+
0
+ time = parse_now("tonight at 7")
0
+ assert_equal Time.local(2006, 8, 16, 19), time
0
+
0
+ time = parse_now("tonight 7")
0
+ assert_equal Time.local(2006, 8, 16, 19), time
0
+
0
+ time = parse_now("7 tonight")
0
+ assert_equal Time.local(2006, 8, 16, 19), time
0
   end
0
-
0
+
0
   def test_parse_guess_grrr
0
     time = parse_now("today at 6:00pm")
0
     assert_equal Time.local(2006, 8, 16, 18), time
0
@@ -372,6 +405,12 @@ class TestParsing < Test::Unit::TestCase
0
     
0
     time = parse_now("yesterday at 4:00pm")
0
     assert_equal Time.local(2006, 8, 15, 16), time
0
+
0
+ time = parse_now("tomorrow evening at 7")
0
+ assert_equal Time.local(2006, 8, 17, 19), time
0
+
0
+ time = parse_now("tomorrow morning at 5:30")
0
+ assert_equal Time.local(2006, 8, 17, 5, 30), time
0
   end
0
   
0
   def test_parse_guess_rgr
0
@@ -492,6 +531,9 @@ class TestParsing < Test::Unit::TestCase
0
     
0
     time = parse_now("10th wednesday in november")
0
     assert_equal nil, time
0
+
0
+ # time = parse_now("3rd wednesday in 2007")
0
+ # assert_equal Time.local(2007, 1, 20, 12), time
0
   end
0
   
0
   def test_parse_guess_o_r_g_r

Comments

    No one has commented yet.