pangel-chronic 0.3.0.2

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.
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