Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

svrc updates

  • Loading branch information...
commit f4fef51a3a315e08132366506cd76a9a893689e5 1 parent 044fbd7
Tom Preston-Werner mojombo authored
19 History.txt
... ... @@ -1,11 +1,22 @@
1   -= 0.2.0 2007-03-20
  1 += 0.2.2
  2 +
  3 +* added missing files (damn you manifest
  4 +
  5 += 0.2.1
2 6
3 7 * fixed time overflow issue
4   -* implemented numerizer, allowing the use of number words (e.g. five weeks ago) (thanks shalev!)
  8 +* implemented "next" for minute repeater
  9 +* generalized time dealiasing to dealias regardless of day portion and time position
  10 +* added additional token match for cases like "friday evening at 7" and "tomorrow evening at 7"
  11 +* added support for Time#to_s output format: "Mon Apr 02 17:00:00 PDT 2007"
  12 +
  13 += 0.2.0 2007-03-20
  14 +
  15 +* implemented numerizer, allowing the use of number words (e.g. five weeks ago) (by shalev)
5 16
6 17 = 0.1.6 2006-01-15
7 18
8   -* added 'weekend' support (eventualbuddha)
  19 +* added 'weekend' support (by eventualbuddha)
9 20
10 21 = 0.1.5 2006-12-20
11 22
@@ -20,7 +31,7 @@
20 31
21 32 = 0.1.3
22 33
23   -* improved regexes for word variations (Josh Goebel)
  34 +* improved regexes for word variations (by Josh Goebel)
24 35 * fixed a bug that caused "today at 3am" to return nil if current time is after 3am
25 36
26 37 = 0.1.2
2  Manifest.txt
@@ -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
30 lib/chronic.rb
@@ -34,11 +34,12 @@
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.0"
  42 + VERSION = "0.2.2"
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
2  lib/chronic/chronic.rb
@@ -66,7 +66,7 @@ def parse(text, specified_options = {})
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
87 lib/chronic/handlers.rb
@@ -6,7 +6,8 @@ def definitions #:nodoc:
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], :handle_rmn_sd_sy),
  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 @@ def definitions #:nodoc:
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 @@ def tokens_to_span(tokens, options) #:nodoc:
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 @@ def tokens_to_span(tokens, options) #:nodoc:
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 @@ def tokens_to_span(tokens, options) #:nodoc:
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 an narrow
  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 @@ def handle_rmn_sy(tokens, options) #:nodoc:
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 @@ def find_within(tags, span, pointer) #:nodoc:
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 => 5:00 am
337   - # 7:00 in the evening => 7:00 pm
338   - #ttokens = []
339   - tokens.each_with_index do |t0, i|
340   - t1 = tokens[i + 1]
341   - if t1 && (t1tag = t1.get_tag(RepeaterDayPortion)) && t0.get_tag(RepeaterTime)
342   - if [:morning].include?(t1tag.type)
343   - t1.untag(RepeaterDayPortion)
344   - t1.tag(RepeaterDayPortion.new(:am))
345   - elsif [:afternoon, :evening, :night].include?(t1tag.type)
346   - t1.untag(RepeaterDayPortion)
347   - t1.tag(RepeaterDayPortion.new(:pm))
348   - end
  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   - #tokens = ttokens
  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
1  lib/chronic/repeater.rb
@@ -33,6 +33,7 @@ def self.scan_for_month_names(token)
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,
2  lib/chronic/repeaters/repeater_day_name.rb
@@ -34,7 +34,7 @@ def width
34 34 end
35 35
36 36 def to_s
37   - super << '-dayofweek-' << @type.to_s
  37 + super << '-dayname-' << @type.to_s
38 38 end
39 39
40 40 private
14 lib/chronic/repeaters/repeater_minute.rb
@@ -4,9 +4,19 @@ class Chronic::RepeaterMinute < Chronic::Repeater #:nodoc:
4 4 def next(pointer = :future)
5 5 super
6 6
7   - direction = pointer == :future ? 1 : -1
  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   - raise 'not implemented'
  19 + Chronic::Span.new(@current_minute_start, @current_minute_start + MINUTE_SECONDS)
10 20 end
11 21
12 22 def this(pointer = :future)
4 lib/chronic/repeaters/repeater_month.rb
@@ -54,4 +54,8 @@ def offset_by(time, amount, pointer)
54 54 def width
55 55 MONTH_SECONDS
56 56 end
  57 +
  58 + def to_s
  59 + super << '-month'
  60 + end
57 61 end
2  lib/chronic/repeaters/repeater_month_name.rb
@@ -70,7 +70,7 @@ def index
70 70 end
71 71
72 72 def to_s
73   - super << '-month-' << @type.to_s
  73 + super << '-monthname-' << @type.to_s
74 74 end
75 75
76 76 private
22 lib/chronic/time_zone.rb
... ... @@ -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
44 test/test_parsing.rb
@@ -136,6 +136,15 @@ def test_parse_guess_dates
136 136 time = parse_now("2006-08-20 15:30.30")
137 137 assert_equal Time.local(2006, 8, 20, 15, 30, 30), time
138 138
  139 + # rdn_rm_rd_rt_rtz_ry
  140 +
  141 + time = parse_now("Mon Apr 02 17:00:00 PDT 2007")
  142 + assert_equal Time.local(2007, 4, 2, 17), time
  143 +
  144 + now = Time.now
  145 + time = parse_now(now.to_s)
  146 + assert_equal now.to_s, time.to_s
  147 +
139 148 # rm_sd_rt
140 149
141 150 #time = parse_now("jan 5 13:00")
@@ -152,11 +161,18 @@ def test_parse_guess_dates
152 161 time = parse_now("1800-08-20")
153 162 assert_equal nil, time
154 163 end
  164 +
  165 + def test_foo
  166 + Chronic.parse('two months ago this friday')
  167 + end
155 168
156 169 def test_parse_guess_r
157 170 time = parse_now("friday")
158 171 assert_equal Time.local(2006, 8, 18, 12), time
159 172
  173 + time = parse_now("tue")
  174 + assert_equal Time.local(2006, 8, 22, 12), time
  175 +
160 176 time = parse_now("5")
161 177 assert_equal Time.local(2006, 8, 16, 17), time
162 178
@@ -214,6 +230,9 @@ def test_parse_guess_rrr
214 230
215 231 time = parse_now("sunday 6am")
216 232 assert_equal Time.local(2006, 8, 20, 6), time
  233 +
  234 + time = parse_now("friday evening at 7")
  235 + assert_equal Time.local(2006, 8, 18, 19), time
217 236 end
218 237
219 238 def test_parse_guess_gr
@@ -313,6 +332,11 @@ def test_parse_guess_gr
313 332 time = parse_now("tonight")
314 333 assert_equal Time.local(2006, 8, 16, 22), time
315 334
  335 + # minute
  336 +
  337 + time = parse_now("next minute")
  338 + assert_equal Time.local(2006, 8, 16, 14, 1, 30), time
  339 +
316 340 # second
317 341
318 342 time = parse_now("this second")
@@ -358,8 +382,17 @@ def test_parse_guess_grr
358 382
359 383 time = parse_now("last week tuesday")
360 384 assert_equal Time.local(2006, 8, 8, 12), time
  385 +
  386 + time = parse_now("tonight at 7")
  387 + assert_equal Time.local(2006, 8, 16, 19), time
  388 +
  389 + time = parse_now("tonight 7")
  390 + assert_equal Time.local(2006, 8, 16, 19), time
  391 +
  392 + time = parse_now("7 tonight")
  393 + assert_equal Time.local(2006, 8, 16, 19), time
361 394 end
362   -
  395 +
363 396 def test_parse_guess_grrr
364 397 time = parse_now("today at 6:00pm")
365 398 assert_equal Time.local(2006, 8, 16, 18), time
@@ -372,6 +405,12 @@ def test_parse_guess_grrr
372 405
373 406 time = parse_now("yesterday at 4:00pm")
374 407 assert_equal Time.local(2006, 8, 15, 16), time
  408 +
  409 + time = parse_now("tomorrow evening at 7")
  410 + assert_equal Time.local(2006, 8, 17, 19), time
  411 +
  412 + time = parse_now("tomorrow morning at 5:30")
  413 + assert_equal Time.local(2006, 8, 17, 5, 30), time
375 414 end
376 415
377 416 def test_parse_guess_rgr
@@ -492,6 +531,9 @@ def test_parse_guess_o_r_s_r
492 531
493 532 time = parse_now("10th wednesday in november")
494 533 assert_equal nil, time
  534 +
  535 + # time = parse_now("3rd wednesday in 2007")
  536 + # assert_equal Time.local(2007, 1, 20, 12), time
495 537 end
496 538
497 539 def test_parse_guess_o_r_g_r

0 comments on commit f4fef51

Please sign in to comment.
Something went wrong with that request. Please try again.