pangel-chronic 0.3.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/README.rdoc +182 -0
  2. data/lib/chronic/chronic.rb +303 -0
  3. data/lib/chronic/grabber.rb +26 -0
  4. data/lib/chronic/handlers.rb +560 -0
  5. data/lib/chronic/ordinal.rb +39 -0
  6. data/lib/chronic/pointer.rb +29 -0
  7. data/lib/chronic/repeater.rb +139 -0
  8. data/lib/chronic/repeaters/repeater_day.rb +52 -0
  9. data/lib/chronic/repeaters/repeater_day_name.rb +53 -0
  10. data/lib/chronic/repeaters/repeater_day_portion.rb +94 -0
  11. data/lib/chronic/repeaters/repeater_fortnight.rb +70 -0
  12. data/lib/chronic/repeaters/repeater_hour.rb +58 -0
  13. data/lib/chronic/repeaters/repeater_minute.rb +57 -0
  14. data/lib/chronic/repeaters/repeater_month.rb +66 -0
  15. data/lib/chronic/repeaters/repeater_month_name.rb +98 -0
  16. data/lib/chronic/repeaters/repeater_season.rb +150 -0
  17. data/lib/chronic/repeaters/repeater_season_name.rb +45 -0
  18. data/lib/chronic/repeaters/repeater_second.rb +41 -0
  19. data/lib/chronic/repeaters/repeater_time.rb +124 -0
  20. data/lib/chronic/repeaters/repeater_week.rb +73 -0
  21. data/lib/chronic/repeaters/repeater_weekday.rb +77 -0
  22. data/lib/chronic/repeaters/repeater_weekend.rb +65 -0
  23. data/lib/chronic/repeaters/repeater_year.rb +64 -0
  24. data/lib/chronic/scalar.rb +76 -0
  25. data/lib/chronic/separator.rb +91 -0
  26. data/lib/chronic/time_zone.rb +23 -0
  27. data/lib/chronic.rb +57 -0
  28. data/lib/numerizer/numerizer.rb +98 -0
  29. data/test/suite.rb +9 -0
  30. data/test/test_Chronic.rb +50 -0
  31. data/test/test_Handler.rb +110 -0
  32. data/test/test_Numerizer.rb +54 -0
  33. data/test/test_RepeaterDayName.rb +52 -0
  34. data/test/test_RepeaterFortnight.rb +63 -0
  35. data/test/test_RepeaterHour.rb +68 -0
  36. data/test/test_RepeaterMonth.rb +47 -0
  37. data/test/test_RepeaterMonthName.rb +57 -0
  38. data/test/test_RepeaterTime.rb +72 -0
  39. data/test/test_RepeaterWeek.rb +63 -0
  40. data/test/test_RepeaterWeekday.rb +56 -0
  41. data/test/test_RepeaterWeekend.rb +75 -0
  42. data/test/test_RepeaterYear.rb +63 -0
  43. data/test/test_Span.rb +33 -0
  44. data/test/test_Time.rb +50 -0
  45. data/test/test_Token.rb +26 -0
  46. data/test/test_parsing.rb +797 -0
  47. metadata +111 -0
@@ -0,0 +1,76 @@
1
+ module Chronic
2
+
3
+ class Scalar < Tag #:nodoc:
4
+ def self.scan(tokens)
5
+ # for each token
6
+ tokens.each_index do |i|
7
+ if t = self.scan_for_scalars(tokens[i], tokens[i + 1]) then tokens[i].tag(t) end
8
+ if t = self.scan_for_days(tokens[i], tokens[i + 1]) then tokens[i].tag(t) end
9
+ if t = self.scan_for_months(tokens[i], tokens[i + 1]) then tokens[i].tag(t) end
10
+ if t = self.scan_for_years(tokens[i], tokens[i + 1]) then tokens[i].tag(t) end
11
+ end
12
+ tokens
13
+ end
14
+
15
+ def self.scan_for_scalars(token, post_token)
16
+ if token.word =~ /^\d*$/ || token.word =~ /^\d\.\d*$/
17
+ unless post_token && %w{am pm morning afternoon evening night}.include?(post_token)
18
+ return Scalar.new(token.word.to_f)
19
+ end
20
+ end
21
+ return nil
22
+ end
23
+
24
+ def self.scan_for_days(token, post_token)
25
+ if token.word =~ /^\d\d?$/
26
+ toi = token.word.to_i
27
+ unless toi > 31 || toi < 1 || (post_token && %w{am pm morning afternoon evening night}.include?(post_token.word))
28
+ return ScalarDay.new(toi)
29
+ end
30
+ end
31
+ return nil
32
+ end
33
+
34
+ def self.scan_for_months(token, post_token)
35
+ if token.word =~ /^\d\d?$/
36
+ toi = token.word.to_i
37
+ unless toi > 12 || toi < 1 || (post_token && %w{am pm morning afternoon evening night}.include?(post_token.word))
38
+ return ScalarMonth.new(toi)
39
+ end
40
+ end
41
+ return nil
42
+ end
43
+
44
+ def self.scan_for_years(token, post_token)
45
+ if token.word =~ /^([1-9]\d)?\d\d?$/
46
+ unless post_token && %w{am pm morning afternoon evening night}.include?(post_token.word)
47
+ return ScalarYear.new(token.word.to_i)
48
+ end
49
+ end
50
+ return nil
51
+ end
52
+
53
+ def to_s
54
+ 'scalar'
55
+ end
56
+ end
57
+
58
+ class ScalarDay < Scalar #:nodoc:
59
+ def to_s
60
+ super << '-day-' << @type.to_s
61
+ end
62
+ end
63
+
64
+ class ScalarMonth < Scalar #:nodoc:
65
+ def to_s
66
+ super << '-month-' << @type.to_s
67
+ end
68
+ end
69
+
70
+ class ScalarYear < Scalar #:nodoc:
71
+ def to_s
72
+ super << '-year-' << @type.to_s
73
+ end
74
+ end
75
+
76
+ end
@@ -0,0 +1,91 @@
1
+ module Chronic
2
+
3
+ class Separator < Tag #:nodoc:
4
+ def self.scan(tokens)
5
+ tokens.each_index do |i|
6
+ if t = self.scan_for_commas(tokens[i]) then tokens[i].tag(t); next end
7
+ if t = self.scan_for_slash_or_dash(tokens[i]) then tokens[i].tag(t); next end
8
+ if t = self.scan_for_at(tokens[i]) then tokens[i].tag(t); next end
9
+ if t = self.scan_for_in(tokens[i]) then tokens[i].tag(t); next end
10
+ if t = self.scan_for_on(tokens[i]) then tokens[i].tag(t); next end
11
+ end
12
+ tokens
13
+ end
14
+
15
+ def self.scan_for_commas(token)
16
+ scanner = {/^,$/ => :comma}
17
+ scanner.keys.each do |scanner_item|
18
+ return SeparatorComma.new(scanner[scanner_item]) if scanner_item =~ token.word
19
+ end
20
+ return nil
21
+ end
22
+
23
+ def self.scan_for_slash_or_dash(token)
24
+ scanner = {/^-$/ => :dash,
25
+ /^\/$/ => :slash}
26
+ scanner.keys.each do |scanner_item|
27
+ return SeparatorSlashOrDash.new(scanner[scanner_item]) if scanner_item =~ token.word
28
+ end
29
+ return nil
30
+ end
31
+
32
+ def self.scan_for_at(token)
33
+ scanner = {/^(at|@)$/i => :at}
34
+ scanner.keys.each do |scanner_item|
35
+ return SeparatorAt.new(scanner[scanner_item]) if scanner_item =~ token.word
36
+ end
37
+ return nil
38
+ end
39
+
40
+ def self.scan_for_in(token)
41
+ scanner = {/^in$/i => :in}
42
+ scanner.keys.each do |scanner_item|
43
+ return SeparatorIn.new(scanner[scanner_item]) if scanner_item =~ token.word
44
+ end
45
+ return nil
46
+ end
47
+
48
+ def self.scan_for_on(token)
49
+ scanner = {/^on$/i => :on}
50
+ scanner.keys.each do |scanner_item|
51
+ return SeparatorOn.new(scanner[scanner_item]) if scanner_item =~ token.word
52
+ end
53
+ return nil
54
+ end
55
+
56
+ def to_s
57
+ 'separator'
58
+ end
59
+ end
60
+
61
+ class SeparatorComma < Separator #:nodoc:
62
+ def to_s
63
+ super << '-comma'
64
+ end
65
+ end
66
+
67
+ class SeparatorSlashOrDash < Separator #:nodoc:
68
+ def to_s
69
+ super << '-slashordash-' << @type.to_s
70
+ end
71
+ end
72
+
73
+ class SeparatorAt < Separator #:nodoc:
74
+ def to_s
75
+ super << '-at'
76
+ end
77
+ end
78
+
79
+ class SeparatorIn < Separator #:nodoc:
80
+ def to_s
81
+ super << '-in'
82
+ end
83
+ end
84
+
85
+ class SeparatorOn < Separator #:nodoc:
86
+ def to_s
87
+ super << '-on'
88
+ end
89
+ end
90
+
91
+ end
@@ -0,0 +1,23 @@
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
+ /(tzminus)?\d{4}/i => :tz}
13
+ scanner.keys.each do |scanner_item|
14
+ return self.new(scanner[scanner_item]) if scanner_item =~ token.word
15
+ end
16
+ return nil
17
+ end
18
+
19
+ def to_s
20
+ 'timezone'
21
+ end
22
+ end
23
+ end
data/lib/chronic.rb ADDED
@@ -0,0 +1,57 @@
1
+ #=============================================================================
2
+ #
3
+ # Name: Chronic
4
+ # Author: Tom Preston-Werner
5
+ # Purpose: Parse natural language dates and times into Time or
6
+ # Chronic::Span objects
7
+ #
8
+ #=============================================================================
9
+
10
+ $:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
11
+
12
+ require 'core_ext/object'
13
+ require 'core_ext/time'
14
+
15
+ require 'chronic/chronic'
16
+ require 'chronic/handlers'
17
+
18
+ require 'chronic/repeater'
19
+ require 'chronic/repeaters/repeater_year'
20
+ require 'chronic/repeaters/repeater_season'
21
+ require 'chronic/repeaters/repeater_season_name'
22
+ require 'chronic/repeaters/repeater_month'
23
+ require 'chronic/repeaters/repeater_month_name'
24
+ require 'chronic/repeaters/repeater_fortnight'
25
+ require 'chronic/repeaters/repeater_week'
26
+ require 'chronic/repeaters/repeater_weekend'
27
+ require 'chronic/repeaters/repeater_weekday'
28
+ require 'chronic/repeaters/repeater_day'
29
+ require 'chronic/repeaters/repeater_day_name'
30
+ require 'chronic/repeaters/repeater_day_portion'
31
+ require 'chronic/repeaters/repeater_decade'
32
+ require 'chronic/repeaters/repeater_hour'
33
+ require 'chronic/repeaters/repeater_minute'
34
+ require 'chronic/repeaters/repeater_second'
35
+ require 'chronic/repeaters/repeater_time'
36
+
37
+ require 'chronic/grabber'
38
+ require 'chronic/pointer'
39
+ require 'chronic/scalar'
40
+ require 'chronic/ordinal'
41
+ require 'chronic/separator'
42
+ require 'chronic/time_zone'
43
+ require 'chronic/blunt.rb'
44
+
45
+ require 'numerizer/numerizer'
46
+
47
+ module Chronic
48
+ VERSION = "0.3.0.2"
49
+
50
+ class << self
51
+ attr_accessor :debug
52
+ attr_accessor :time_class
53
+ end
54
+
55
+ self.debug = false
56
+ self.time_class = Time
57
+ end
@@ -0,0 +1,98 @@
1
+ require 'strscan'
2
+
3
+ class Numerizer
4
+
5
+ DIRECT_NUMS = [
6
+ ['eleven', '11'],
7
+ ['twelve', '12'],
8
+ ['thirteen', '13'],
9
+ ['fourteen', '14'],
10
+ ['fifteen', '15'],
11
+ ['sixteen', '16'],
12
+ ['seventeen', '17'],
13
+ ['eighteen', '18'],
14
+ ['nineteen', '19'],
15
+ ['ninteen', '19'], # Common mis-spelling
16
+ ['zero', '0'],
17
+ ['one', '1'],
18
+ ['two', '2'],
19
+ ['three', '3'],
20
+ ['four(\W|$)', '4\1'], # The weird regex is so that it matches four but not fourty
21
+ ['five', '5'],
22
+ ['six(\W|$)', '6\1'],
23
+ ['seven(\W|$)', '7\1'],
24
+ ['eight(\W|$)', '8\1'],
25
+ ['nine(\W|$)', '9\1'],
26
+ ['ten', '10'],
27
+ ['\ba[\b^$]', '1'] # doesn't make sense for an 'a' at the end to be a 1
28
+ ]
29
+
30
+ TEN_PREFIXES = [ ['twenty', 20],
31
+ ['thirty', 30],
32
+ ['forty', 40],
33
+ ['fourty', 40], # Common misspelling
34
+ ['fifty', 50],
35
+ ['sixty', 60],
36
+ ['seventy', 70],
37
+ ['eighty', 80],
38
+ ['ninety', 90]
39
+ ]
40
+
41
+ BIG_PREFIXES = [ ['hundred', 100],
42
+ ['thousand', 1000],
43
+ ['million', 1_000_000],
44
+ ['billion', 1_000_000_000],
45
+ ['trillion', 1_000_000_000_000],
46
+ ]
47
+
48
+ def self.numerize(string)
49
+ string = string.dup
50
+
51
+ # preprocess
52
+ string.gsub!(/ +|([^\d])-([^\d])/, '\1 \2') # will mutilate hyphenated-words but shouldn't matter for date extraction
53
+ string.gsub!(/a half/, 'haAlf') # take the 'a' out so it doesn't turn into a 1, save the half for the end
54
+
55
+ # easy/direct replacements
56
+
57
+ DIRECT_NUMS.each do |dn|
58
+ string.gsub!(/#{dn[0]}/i, '<num>' + dn[1])
59
+ end
60
+
61
+ # ten, twenty, etc.
62
+
63
+ TEN_PREFIXES.each do |tp|
64
+ string.gsub!(/(?:#{tp[0]}) *<num>(\d(?=[^\d]|$))*/i) { '<num>' + (tp[1] + $1.to_i).to_s }
65
+ end
66
+
67
+ TEN_PREFIXES.each do |tp|
68
+ string.gsub!(/#{tp[0]}/i) { '<num>' + tp[1].to_s }
69
+ end
70
+
71
+ # hundreds, thousands, millions, etc.
72
+
73
+ BIG_PREFIXES.each do |bp|
74
+ string.gsub!(/(?:<num>)?(\d*) *#{bp[0]}/i) { '<num>' + (bp[1] * $1.to_i).to_s}
75
+ andition(string)
76
+ end
77
+
78
+ # fractional addition
79
+ # I'm not combining this with the previous block as using float addition complicates the strings
80
+ # (with extraneous .0's and such )
81
+ string.gsub!(/(\d+)(?: | and |-)*haAlf/i) { ($1.to_f + 0.5).to_s }
82
+
83
+ string.gsub(/<num>/, '')
84
+ end
85
+
86
+ private
87
+
88
+ def self.andition(string)
89
+ sc = StringScanner.new(string)
90
+ while(sc.scan_until(/<num>(\d+)( | and )<num>(\d+)(?=[^\w]|$)/i))
91
+ if sc[2] =~ /and/ || sc[1].size > sc[3].size
92
+ string[(sc.pos - sc.matched_size)..(sc.pos-1)] = '<num>' + (sc[1].to_i + sc[3].to_i).to_s
93
+ sc.reset
94
+ end
95
+ end
96
+ end
97
+
98
+ end
data/test/suite.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'test/unit'
2
+
3
+ tests = Dir["#{File.dirname(__FILE__)}/test_*.rb"]
4
+ tests.delete_if { |o| o =~ /test_parsing/ }
5
+ tests.each do |file|
6
+ require file
7
+ end
8
+
9
+ require File.dirname(__FILE__) + '/test_parsing.rb'
@@ -0,0 +1,50 @@
1
+ require File.dirname(__FILE__) + '/../lib/chronic'
2
+ require 'test/unit'
3
+
4
+ class TestChronic < Test::Unit::TestCase
5
+
6
+ def setup
7
+ # Wed Aug 16 14:00:00 UTC 2006
8
+ @now = Time.local(2006, 8, 16, 14, 0, 0, 0)
9
+ end
10
+
11
+ def test_post_normalize_am_pm_aliases
12
+ # affect wanted patterns
13
+
14
+ tokens = [Chronic::Token.new("5:00"), Chronic::Token.new("morning")]
15
+ tokens[0].tag(Chronic::RepeaterTime.new("5:00"))
16
+ tokens[1].tag(Chronic::RepeaterDayPortion.new(:morning))
17
+
18
+ assert_equal :morning, tokens[1].tags[0].type
19
+
20
+ tokens = Chronic.dealias_and_disambiguate_times(tokens, {})
21
+
22
+ assert_equal :am, tokens[1].tags[0].type
23
+ assert_equal 2, tokens.size
24
+
25
+ # don't affect unwanted patterns
26
+
27
+ tokens = [Chronic::Token.new("friday"), Chronic::Token.new("morning")]
28
+ tokens[0].tag(Chronic::RepeaterDayName.new(:friday))
29
+ tokens[1].tag(Chronic::RepeaterDayPortion.new(:morning))
30
+
31
+ assert_equal :morning, tokens[1].tags[0].type
32
+
33
+ tokens = Chronic.dealias_and_disambiguate_times(tokens, {})
34
+
35
+ assert_equal :morning, tokens[1].tags[0].type
36
+ assert_equal 2, tokens.size
37
+ end
38
+
39
+ def test_guess
40
+ span = Chronic::Span.new(Time.local(2006, 8, 16, 0), Time.local(2006, 8, 17, 0))
41
+ assert_equal Time.local(2006, 8, 16, 12), Chronic.guess(span)
42
+
43
+ span = Chronic::Span.new(Time.local(2006, 8, 16, 0), Time.local(2006, 8, 17, 0, 0, 1))
44
+ assert_equal Time.local(2006, 8, 16, 12), Chronic.guess(span)
45
+
46
+ span = Chronic::Span.new(Time.local(2006, 11), Time.local(2006, 12))
47
+ assert_equal Time.local(2006, 11, 16), Chronic.guess(span)
48
+ end
49
+
50
+ end
@@ -0,0 +1,110 @@
1
+ require 'chronic'
2
+ require 'test/unit'
3
+
4
+ class TestHandler < Test::Unit::TestCase
5
+
6
+ def setup
7
+ # Wed Aug 16 14:00:00 UTC 2006
8
+ @now = Time.local(2006, 8, 16, 14, 0, 0, 0)
9
+ end
10
+
11
+ def test_handler_class_1
12
+ handler = Chronic::Handler.new([:repeater], :handler)
13
+
14
+ tokens = [Chronic::Token.new('friday')]
15
+ tokens[0].tag(Chronic::RepeaterDayName.new(:friday))
16
+
17
+ assert handler.match(tokens, Chronic.definitions)
18
+
19
+ tokens << Chronic::Token.new('afternoon')
20
+ tokens[1].tag(Chronic::RepeaterDayPortion.new(:afternoon))
21
+
22
+ assert !handler.match(tokens, Chronic.definitions)
23
+ end
24
+
25
+ def test_handler_class_2
26
+ handler = Chronic::Handler.new([:repeater, :repeater?], :handler)
27
+
28
+ tokens = [Chronic::Token.new('friday')]
29
+ tokens[0].tag(Chronic::RepeaterDayName.new(:friday))
30
+
31
+ assert handler.match(tokens, Chronic.definitions)
32
+
33
+ tokens << Chronic::Token.new('afternoon')
34
+ tokens[1].tag(Chronic::RepeaterDayPortion.new(:afternoon))
35
+
36
+ assert handler.match(tokens, Chronic.definitions)
37
+
38
+ tokens << Chronic::Token.new('afternoon')
39
+ tokens[2].tag(Chronic::RepeaterDayPortion.new(:afternoon))
40
+
41
+ assert !handler.match(tokens, Chronic.definitions)
42
+ end
43
+
44
+ def test_handler_class_3
45
+ handler = Chronic::Handler.new([:repeater, 'time?'], :handler)
46
+
47
+ tokens = [Chronic::Token.new('friday')]
48
+ tokens[0].tag(Chronic::RepeaterDayName.new(:friday))
49
+
50
+ assert handler.match(tokens, Chronic.definitions)
51
+
52
+ tokens << Chronic::Token.new('afternoon')
53
+ tokens[1].tag(Chronic::RepeaterDayPortion.new(:afternoon))
54
+
55
+ assert !handler.match(tokens, Chronic.definitions)
56
+ end
57
+
58
+ def test_handler_class_4
59
+ handler = Chronic::Handler.new([:repeater_month_name, :scalar_day, 'time?'], :handler)
60
+
61
+ tokens = [Chronic::Token.new('may')]
62
+ tokens[0].tag(Chronic::RepeaterMonthName.new(:may))
63
+
64
+ assert !handler.match(tokens, Chronic.definitions)
65
+
66
+ tokens << Chronic::Token.new('27')
67
+ tokens[1].tag(Chronic::ScalarDay.new(27))
68
+
69
+ assert handler.match(tokens, Chronic.definitions)
70
+ end
71
+
72
+ def test_handler_class_5
73
+ handler = Chronic::Handler.new([:repeater, 'time?'], :handler)
74
+
75
+ tokens = [Chronic::Token.new('friday')]
76
+ tokens[0].tag(Chronic::RepeaterDayName.new(:friday))
77
+
78
+ assert handler.match(tokens, Chronic.definitions)
79
+
80
+ tokens << Chronic::Token.new('5:00')
81
+ tokens[1].tag(Chronic::RepeaterTime.new('5:00'))
82
+
83
+ assert handler.match(tokens, Chronic.definitions)
84
+
85
+ tokens << Chronic::Token.new('pm')
86
+ tokens[2].tag(Chronic::RepeaterDayPortion.new(:pm))
87
+
88
+ assert handler.match(tokens, Chronic.definitions)
89
+ end
90
+
91
+ def test_handler_class_6
92
+ handler = Chronic::Handler.new([:scalar, :repeater, :pointer], :handler)
93
+
94
+ tokens = [Chronic::Token.new('3'),
95
+ Chronic::Token.new('years'),
96
+ Chronic::Token.new('past')]
97
+
98
+ tokens[0].tag(Chronic::Scalar.new(3))
99
+ tokens[1].tag(Chronic::RepeaterYear.new(:year))
100
+ tokens[2].tag(Chronic::Pointer.new(:past))
101
+
102
+ assert handler.match(tokens, Chronic.definitions)
103
+ end
104
+
105
+ def test_constantize
106
+ handler = Chronic::Handler.new([], :handler)
107
+ assert_equal Chronic::RepeaterTime, handler.constantize(:repeater_time)
108
+ end
109
+
110
+ end
@@ -0,0 +1,54 @@
1
+ require 'test/unit'
2
+ require 'chronic'
3
+
4
+ class ParseNumbersTest < Test::Unit::TestCase
5
+
6
+ def test_straight_parsing
7
+ strings = { 1 => 'one',
8
+ 5 => 'five',
9
+ 10 => 'ten',
10
+ 11 => 'eleven',
11
+ 12 => 'twelve',
12
+ 13 => 'thirteen',
13
+ 14 => 'fourteen',
14
+ 15 => 'fifteen',
15
+ 16 => 'sixteen',
16
+ 17 => 'seventeen',
17
+ 18 => 'eighteen',
18
+ 19 => 'nineteen',
19
+ 20 => 'twenty',
20
+ 27 => 'twenty seven',
21
+ 31 => 'thirty-one',
22
+ 41 => 'forty one',
23
+ 42 => 'fourty two',
24
+ 59 => 'fifty nine',
25
+ 100 => 'a hundred',
26
+ 100 => 'one hundred',
27
+ 150 => 'one hundred and fifty',
28
+ # 150 => 'one fifty',
29
+ 200 => 'two-hundred',
30
+ 500 => '5 hundred',
31
+ 999 => 'nine hundred and ninety nine',
32
+ 1_000 => 'one thousand',
33
+ 1_200 => 'twelve hundred',
34
+ 1_200 => 'one thousand two hundred',
35
+ 17_000 => 'seventeen thousand',
36
+ 21_473 => 'twentyone-thousand-four-hundred-and-seventy-three',
37
+ 74_002 => 'seventy four thousand and two',
38
+ 99_999 => 'ninety nine thousand nine hundred ninety nine',
39
+ 100_000 => '100 thousand',
40
+ 250_000 => 'two hundred fifty thousand',
41
+ 1_000_000 => 'one million',
42
+ 1_250_007 => 'one million two hundred fifty thousand and seven',
43
+ 1_000_000_000 => 'one billion',
44
+ 1_000_000_001 => 'one billion and one' }
45
+
46
+ strings.keys.sort.each do |key|
47
+ assert_equal key, Numerizer.numerize(strings[key]).to_i
48
+ end
49
+ end
50
+
51
+ def test_edges
52
+ assert_equal "27 Oct 2006 7:30am", Numerizer.numerize("27 Oct 2006 7:30am")
53
+ end
54
+ end
@@ -0,0 +1,52 @@
1
+ require 'chronic'
2
+ require 'test/unit'
3
+
4
+ class TestRepeaterDayName < Test::Unit::TestCase
5
+
6
+ def setup
7
+ @now = Time.local(2006, 8, 16, 14, 0, 0, 0)
8
+ end
9
+
10
+ def test_match
11
+ token = Chronic::Token.new('saturday')
12
+ repeater = Chronic::Repeater.scan_for_day_names(token)
13
+ assert_equal Chronic::RepeaterDayName, repeater.class
14
+ assert_equal :saturday, repeater.type
15
+
16
+ token = Chronic::Token.new('sunday')
17
+ repeater = Chronic::Repeater.scan_for_day_names(token)
18
+ assert_equal Chronic::RepeaterDayName, repeater.class
19
+ assert_equal :sunday, repeater.type
20
+ end
21
+
22
+ def test_next_future
23
+ mondays = Chronic::RepeaterDayName.new(:monday)
24
+ mondays.start = @now
25
+
26
+ span = mondays.next(:future)
27
+
28
+ assert_equal Time.local(2006, 8, 21), span.begin
29
+ assert_equal Time.local(2006, 8, 22), span.end
30
+
31
+ span = mondays.next(:future)
32
+
33
+ assert_equal Time.local(2006, 8, 28), span.begin
34
+ assert_equal Time.local(2006, 8, 29), span.end
35
+ end
36
+
37
+ def test_next_past
38
+ mondays = Chronic::RepeaterDayName.new(:monday)
39
+ mondays.start = @now
40
+
41
+ span = mondays.next(:past)
42
+
43
+ assert_equal Time.local(2006, 8, 14), span.begin
44
+ assert_equal Time.local(2006, 8, 15), span.end
45
+
46
+ span = mondays.next(:past)
47
+
48
+ assert_equal Time.local(2006, 8, 7), span.begin
49
+ assert_equal Time.local(2006, 8, 8), span.end
50
+ end
51
+
52
+ end
@@ -0,0 +1,63 @@
1
+ require 'chronic'
2
+ require 'test/unit'
3
+
4
+ class TestRepeaterFortnight < Test::Unit::TestCase
5
+
6
+ def setup
7
+ @now = Time.local(2006, 8, 16, 14, 0, 0, 0)
8
+ end
9
+
10
+ def test_next_future
11
+ fortnights = Chronic::RepeaterFortnight.new(:fortnight)
12
+ fortnights.start = @now
13
+
14
+ next_fortnight = fortnights.next(:future)
15
+ assert_equal Time.local(2006, 8, 20), next_fortnight.begin
16
+ assert_equal Time.local(2006, 9, 3), next_fortnight.end
17
+
18
+ next_next_fortnight = fortnights.next(:future)
19
+ assert_equal Time.local(2006, 9, 3), next_next_fortnight.begin
20
+ assert_equal Time.local(2006, 9, 17), next_next_fortnight.end
21
+ end
22
+
23
+ def test_next_past
24
+ fortnights = Chronic::RepeaterFortnight.new(:fortnight)
25
+ fortnights.start = @now
26
+
27
+ last_fortnight = fortnights.next(:past)
28
+ assert_equal Time.local(2006, 7, 30), last_fortnight.begin
29
+ assert_equal Time.local(2006, 8, 13), last_fortnight.end
30
+
31
+ last_last_fortnight = fortnights.next(:past)
32
+ assert_equal Time.local(2006, 7, 16), last_last_fortnight.begin
33
+ assert_equal Time.local(2006, 7, 30), last_last_fortnight.end
34
+ end
35
+
36
+ def test_this_future
37
+ fortnights = Chronic::RepeaterFortnight.new(:fortnight)
38
+ fortnights.start = @now
39
+
40
+ this_fortnight = fortnights.this(:future)
41
+ assert_equal Time.local(2006, 8, 16, 15), this_fortnight.begin
42
+ assert_equal Time.local(2006, 8, 27), this_fortnight.end
43
+ end
44
+
45
+ def test_this_past
46
+ fortnights = Chronic::RepeaterFortnight.new(:fortnight)
47
+ fortnights.start = @now
48
+
49
+ this_fortnight = fortnights.this(:past)
50
+ assert_equal Time.local(2006, 8, 13, 0), this_fortnight.begin
51
+ assert_equal Time.local(2006, 8, 16, 14), this_fortnight.end
52
+ end
53
+
54
+ def test_offset
55
+ span = Chronic::Span.new(@now, @now + 1)
56
+
57
+ offset_span = Chronic::RepeaterFortnight.new(:week).offset(span, 3, :future)
58
+
59
+ assert_equal Time.local(2006, 9, 27, 14), offset_span.begin
60
+ assert_equal Time.local(2006, 9, 27, 14, 0, 1), offset_span.end
61
+ end
62
+
63
+ end