chronic 0.3.0 → 0.4.0

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 (43) hide show
  1. data/HISTORY.md +27 -0
  2. data/Manifest.txt +16 -5
  3. data/README.md +14 -8
  4. data/Rakefile +2 -8
  5. data/chronic.gemspec +8 -11
  6. data/lib/chronic.rb +21 -14
  7. data/lib/chronic/chronic.rb +38 -130
  8. data/lib/chronic/grabber.rb +11 -15
  9. data/lib/chronic/handlers.rb +63 -40
  10. data/lib/chronic/mini_date.rb +27 -0
  11. data/lib/chronic/numerizer.rb +120 -0
  12. data/lib/chronic/ordinal.rb +5 -10
  13. data/lib/chronic/pointer.rb +8 -10
  14. data/lib/chronic/repeater.rb +106 -109
  15. data/lib/chronic/repeaters/repeater_day.rb +43 -41
  16. data/lib/chronic/repeaters/repeater_day_name.rb +38 -36
  17. data/lib/chronic/repeaters/repeater_day_portion.rb +74 -73
  18. data/lib/chronic/repeaters/repeater_fortnight.rb +57 -55
  19. data/lib/chronic/repeaters/repeater_hour.rb +46 -44
  20. data/lib/chronic/repeaters/repeater_minute.rb +46 -44
  21. data/lib/chronic/repeaters/repeater_month.rb +52 -50
  22. data/lib/chronic/repeaters/repeater_month_name.rb +84 -80
  23. data/lib/chronic/repeaters/repeater_season.rb +97 -119
  24. data/lib/chronic/repeaters/repeater_season_name.rb +39 -39
  25. data/lib/chronic/repeaters/repeater_second.rb +32 -30
  26. data/lib/chronic/repeaters/repeater_time.rb +106 -101
  27. data/lib/chronic/repeaters/repeater_week.rb +60 -58
  28. data/lib/chronic/repeaters/repeater_weekday.rb +67 -58
  29. data/lib/chronic/repeaters/repeater_weekend.rb +54 -52
  30. data/lib/chronic/repeaters/repeater_year.rb +50 -48
  31. data/lib/chronic/scalar.rb +24 -16
  32. data/lib/chronic/separator.rb +15 -33
  33. data/lib/chronic/span.rb +31 -0
  34. data/lib/chronic/tag.rb +26 -0
  35. data/lib/chronic/time_zone.rb +7 -9
  36. data/lib/chronic/token.rb +35 -0
  37. data/test/helper.rb +5 -6
  38. data/test/test_Chronic.rb +5 -0
  39. data/test/test_Numerizer.rb +60 -39
  40. data/test/test_RepeaterHour.rb +4 -0
  41. data/test/test_parsing.rb +104 -13
  42. metadata +14 -20
  43. data/lib/chronic/numerizer/numerizer.rb +0 -97
@@ -1,53 +1,61 @@
1
1
  module Chronic
2
2
 
3
3
  class Scalar < Tag #:nodoc:
4
- def self.scan(tokens)
4
+ DAY_PORTIONS = %w( am pm morning afternoon evening night )
5
+
6
+ def self.scan(tokens, options)
5
7
  # for each token
6
8
  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
9
+ if t = scan_for_scalars(tokens[i], tokens[i + 1]) then tokens[i].tag(t) end
10
+ if t = scan_for_days(tokens[i], tokens[i + 1]) then tokens[i].tag(t) end
11
+ if t = scan_for_months(tokens[i], tokens[i + 1]) then tokens[i].tag(t) end
12
+ if t = scan_for_years(tokens[i], tokens[i + 1], options) then tokens[i].tag(t) end
11
13
  end
12
- tokens
13
14
  end
14
15
 
15
16
  def self.scan_for_scalars(token, post_token)
16
17
  if token.word =~ /^\d*$/
17
- unless post_token && %w{am pm morning afternoon evening night}.include?(post_token)
18
+ unless post_token && DAY_PORTIONS.include?(post_token.word)
18
19
  return Scalar.new(token.word.to_i)
19
20
  end
20
21
  end
21
- return nil
22
22
  end
23
23
 
24
24
  def self.scan_for_days(token, post_token)
25
25
  if token.word =~ /^\d\d?$/
26
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))
27
+ unless toi > 31 || toi < 1 || (post_token && DAY_PORTIONS.include?(post_token.word))
28
28
  return ScalarDay.new(toi)
29
29
  end
30
30
  end
31
- return nil
32
31
  end
33
32
 
34
33
  def self.scan_for_months(token, post_token)
35
34
  if token.word =~ /^\d\d?$/
36
35
  toi = token.word.to_i
37
- unless toi > 12 || toi < 1 || (post_token && %w{am pm morning afternoon evening night}.include?(post_token.word))
36
+ unless toi > 12 || toi < 1 || (post_token && DAY_PORTIONS.include?(post_token.word))
38
37
  return ScalarMonth.new(toi)
39
38
  end
40
39
  end
41
- return nil
42
40
  end
43
41
 
44
- def self.scan_for_years(token, post_token)
42
+ def self.scan_for_years(token, post_token, options)
45
43
  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)
44
+ unless post_token && DAY_PORTIONS.include?(post_token.word)
45
+ year = make_year(token.word.to_i, options[:ambiguous_year_future_bias])
46
+ return ScalarYear.new(year.to_i)
48
47
  end
49
48
  end
50
- return nil
49
+ end
50
+
51
+ # Build a year from a 2 digit suffix
52
+ def self.make_year(year, bias)
53
+ return year if year.to_s.size > 2
54
+ start_year = Chronic.time_class.now.year - bias
55
+ century = (start_year / 100) * 100
56
+ full_year = century + year
57
+ full_year += 100 if full_year < start_year
58
+ full_year
51
59
  end
52
60
 
53
61
  def to_s
@@ -1,56 +1,38 @@
1
1
  module Chronic
2
2
 
3
3
  class Separator < Tag #:nodoc:
4
- def self.scan(tokens)
4
+ def self.scan(tokens, options)
5
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
6
+ if t = scan_for_commas(tokens[i]) then tokens[i].tag(t); next end
7
+ if t = scan_for_slash_or_dash(tokens[i]) then tokens[i].tag(t); next end
8
+ if t = scan_for_at(tokens[i]) then tokens[i].tag(t); next end
9
+ if t = scan_for_in(tokens[i]) then tokens[i].tag(t); next end
10
+ if t = scan_for_on(tokens[i]) then tokens[i].tag(t); next end
11
11
  end
12
- tokens
13
12
  end
14
13
 
15
14
  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
15
+ scan_for token, SeparatorComma, { /^,$/ => :comma }
21
16
  end
22
17
 
23
18
  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
19
+ scan_for token, SeparatorSlashOrDash,
20
+ {
21
+ /^-$/ => :dash,
22
+ /^\/$/ => :slash
23
+ }
30
24
  end
31
25
 
32
26
  def self.scan_for_at(token)
33
- scanner = {/^(at|@)$/ => :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
27
+ scan_for token, SeparatorAt, { /^(at|@)$/ => :at }
38
28
  end
39
29
 
40
30
  def self.scan_for_in(token)
41
- scanner = {/^in$/ => :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
31
+ scan_for token, SeparatorIn, { /^in$/ => :in }
46
32
  end
47
33
 
48
34
  def self.scan_for_on(token)
49
- scanner = {/^on$/ => :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
35
+ scan_for token, SeparatorOn, { /^on$/ => :on }
54
36
  end
55
37
 
56
38
  def to_s
@@ -0,0 +1,31 @@
1
+ module Chronic
2
+ # A Span represents a range of time. Since this class extends
3
+ # Range, you can use #begin and #end to get the beginning and
4
+ # ending times of the span (they will be of class Time)
5
+ class Span < Range
6
+ # Returns the width of this span in seconds
7
+ def width
8
+ (self.end - self.begin).to_i
9
+ end
10
+
11
+ # Add a number of seconds to this span, returning the
12
+ # resulting Span
13
+ def +(seconds)
14
+ Span.new(self.begin + seconds, self.end + seconds)
15
+ end
16
+
17
+ # Subtract a number of seconds to this span, returning the
18
+ # resulting Span
19
+ def -(seconds)
20
+ self + -seconds
21
+ end
22
+
23
+ # Prints this span in a nice fashion
24
+ def to_s
25
+ '(' << self.begin.to_s << '..' << self.end.to_s << ')'
26
+ end
27
+
28
+ alias :cover? :include? unless RUBY_VERSION =~ /^1.9/
29
+
30
+ end
31
+ end
@@ -0,0 +1,26 @@
1
+ module Chronic
2
+ # Tokens are tagged with subclassed instances of this class when
3
+ # they match specific criteria
4
+ class Tag #:nodoc:
5
+ attr_accessor :type
6
+
7
+ def initialize(type)
8
+ @type = type
9
+ end
10
+
11
+ def start=(s)
12
+ @now = s
13
+ end
14
+
15
+ class << self
16
+ private
17
+
18
+ def scan_for(token, klass, items={})
19
+ items.each do |item, symbol|
20
+ return klass.new(symbol) if item =~ token.word
21
+ end
22
+ nil
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,19 +1,17 @@
1
1
  module Chronic
2
2
  class TimeZone < Tag #:nodoc:
3
- def self.scan(tokens)
3
+ def self.scan(tokens, options)
4
4
  tokens.each_index do |i|
5
- if t = self.scan_for_all(tokens[i]) then tokens[i].tag(t); next end
5
+ if t = scan_for_all(tokens[i]) then tokens[i].tag(t); next end
6
6
  end
7
- tokens
8
7
  end
9
8
 
10
9
  def self.scan_for_all(token)
11
- scanner = {/[PMCE][DS]T/i => :tz,
12
- /(tzminus)?\d{4}/ => :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
10
+ scan_for token, self,
11
+ {
12
+ /[PMCE][DS]T/i => :tz,
13
+ /(tzminus)?\d{4}/ => :tz
14
+ }
17
15
  end
18
16
 
19
17
  def to_s
@@ -0,0 +1,35 @@
1
+ module Chronic
2
+ class Token #:nodoc:
3
+ attr_accessor :word, :tags
4
+
5
+ def initialize(word)
6
+ @word = word
7
+ @tags = []
8
+ end
9
+
10
+ # Tag this token with the specified tag
11
+ def tag(new_tag)
12
+ @tags << new_tag
13
+ end
14
+
15
+ # Remove all tags of the given class
16
+ def untag(tag_class)
17
+ @tags.delete_if { |m| m.kind_of? tag_class }
18
+ end
19
+
20
+ # Return true if this token has any tags
21
+ def tagged?
22
+ @tags.size > 0
23
+ end
24
+
25
+ # Return the Tag that matches the given class
26
+ def get_tag(tag_class)
27
+ @tags.find { |m| m.kind_of? tag_class }
28
+ end
29
+
30
+ # Print this Token in a pretty way
31
+ def to_s
32
+ @word << '(' << @tags.join(', ') << ') '
33
+ end
34
+ end
35
+ end
@@ -1,7 +1,6 @@
1
- require 'test/unit'
1
+ unless defined? Chronic
2
+ $:.unshift File.expand_path('../../lib')
3
+ require 'chronic'
4
+ end
2
5
 
3
- dir = File.dirname(File.expand_path(__FILE__))
4
- $LOAD_PATH.unshift(File.join(dir, '..', 'lib'))
5
- $LOAD_PATH.unshift(dir)
6
-
7
- require 'chronic'
6
+ require 'test/unit'
@@ -7,6 +7,11 @@ class TestChronic < Test::Unit::TestCase
7
7
  @now = Time.local(2006, 8, 16, 14, 0, 0, 0)
8
8
  end
9
9
 
10
+ def test_pre_normalize_numerized_string
11
+ string = 'two and a half years'
12
+ assert_equal Chronic::Numerizer.numerize(string), Chronic.pre_normalize(string)
13
+ end
14
+
10
15
  def test_post_normalize_am_pm_aliases
11
16
  # affect wanted patterns
12
17
 
@@ -3,49 +3,70 @@ require File.join(File.dirname(__FILE__), *%w[helper])
3
3
  class ParseNumbersTest < Test::Unit::TestCase
4
4
 
5
5
  def test_straight_parsing
6
- strings = { 1 => 'one',
7
- 5 => 'five',
8
- 10 => 'ten',
9
- 11 => 'eleven',
10
- 12 => 'twelve',
11
- 13 => 'thirteen',
12
- 14 => 'fourteen',
13
- 15 => 'fifteen',
14
- 16 => 'sixteen',
15
- 17 => 'seventeen',
16
- 18 => 'eighteen',
17
- 19 => 'nineteen',
18
- 20 => 'twenty',
19
- 27 => 'twenty seven',
20
- 31 => 'thirty-one',
21
- 59 => 'fifty nine',
22
- 100 => 'a hundred',
23
- 100 => 'one hundred',
24
- 150 => 'one hundred and fifty',
25
- # 150 => 'one fifty',
26
- 200 => 'two-hundred',
27
- 500 => '5 hundred',
28
- 999 => 'nine hundred and ninety nine',
29
- 1_000 => 'one thousand',
30
- 1_200 => 'twelve hundred',
31
- 1_200 => 'one thousand two hundred',
32
- 17_000 => 'seventeen thousand',
33
- 21_473 => 'twentyone-thousand-four-hundred-and-seventy-three',
34
- 74_002 => 'seventy four thousand and two',
35
- 99_999 => 'ninety nine thousand nine hundred ninety nine',
36
- 100_000 => '100 thousand',
37
- 250_000 => 'two hundred fifty thousand',
38
- 1_000_000 => 'one million',
39
- 1_250_007 => 'one million two hundred fifty thousand and seven',
40
- 1_000_000_000 => 'one billion',
41
- 1_000_000_001 => 'one billion and one' }
6
+ strings = {
7
+ 'one' => 1,
8
+ 'five' => 5,
9
+ 'ten' => 10,
10
+ 'eleven' => 11,
11
+ 'twelve' => 12,
12
+ 'thirteen' => 13,
13
+ 'fourteen' => 14,
14
+ 'fifteen' => 15,
15
+ 'sixteen' => 16,
16
+ 'seventeen' => 17,
17
+ 'eighteen' => 18,
18
+ 'nineteen' => 19,
19
+ 'twenty' => 20,
20
+ 'twenty seven' => 27,
21
+ 'thirty-one' => 31,
22
+ 'thirty-seven' => 37,
23
+ 'thirty seven' => 37,
24
+ 'fifty nine' => 59,
25
+ 'forty two' => 42,
26
+ 'fourty two' => 42,
27
+ # 'a hundred' => 100,
28
+ 'one hundred' => 100,
29
+ 'one hundred and fifty' => 150,
30
+ # 'one fifty' => 150,
31
+ 'two-hundred' => 200,
32
+ '5 hundred' => 500,
33
+ 'nine hundred and ninety nine' => 999,
34
+ 'one thousand' => 1000,
35
+ 'twelve hundred' => 1200,
36
+ 'one thousand two hundred' => 1_200,
37
+ 'seventeen thousand' => 17_000,
38
+ 'twentyone-thousand-four-hundred-and-seventy-three' => 21_473,
39
+ 'seventy four thousand and two' => 74_002,
40
+ 'ninety nine thousand nine hundred ninety nine' => 99_999,
41
+ '100 thousand' => 100_000,
42
+ 'two hundred fifty thousand' => 250_000,
43
+ 'one million' => 1_000_000,
44
+ 'one million two hundred fifty thousand and seven' => 1_250_007,
45
+ 'one billion' => 1_000_000_000,
46
+ 'one billion and one' => 1_000_000_001}
42
47
 
43
- strings.keys.sort.each do |key|
44
- assert_equal key, Numerizer.numerize(strings[key]).to_i
48
+ strings.each do |key, val|
49
+ assert_equal val, Chronic::Numerizer.numerize(key).to_i
50
+ end
51
+ end
52
+
53
+ def test_ordinal_strings
54
+ {
55
+ 'first' => '1st',
56
+ 'second' => 'second',
57
+ 'second day' => '2nd day',
58
+ 'second of may' => '2nd of may',
59
+ 'fifth' => '5th',
60
+ 'twenty third' => '23rd',
61
+ 'first day month two' => '1st day month 2'
62
+ }.each do |key, val|
63
+ # Use pre_normalize here instead of Numerizer directly because
64
+ # pre_normalize deals with parsing 'second' appropriately
65
+ assert_equal val, Chronic.pre_normalize(key)
45
66
  end
46
67
  end
47
68
 
48
69
  def test_edges
49
- assert_equal "27 Oct 2006 7:30am", Numerizer.numerize("27 Oct 2006 7:30am")
70
+ assert_equal "27 Oct 2006 7:30am", Chronic::Numerizer.numerize("27 Oct 2006 7:30am")
50
71
  end
51
72
  end
@@ -45,6 +45,10 @@ class TestRepeaterHour < Test::Unit::TestCase
45
45
  this_hour = hours.this(:past)
46
46
  assert_equal Time.local(2006, 8, 16, 14), this_hour.begin
47
47
  assert_equal Time.local(2006, 8, 16, 14, 30), this_hour.end
48
+
49
+ this_hour = hours.this(:none)
50
+ assert_equal Time.local(2006, 8, 16, 14), this_hour.begin
51
+ assert_equal Time.local(2006, 8, 16, 15), this_hour.end
48
52
  end
49
53
 
50
54
  def test_offset
@@ -26,6 +26,20 @@ class TestParsing < Test::Unit::TestCase
26
26
  time = parse_now("may 28 at 5:32.19pm", :context => :past)
27
27
  assert_equal Time.local(2006, 5, 28, 17, 32, 19), time
28
28
 
29
+ # rm_sd for current month
30
+
31
+ time = parse_now("aug 3")
32
+ assert_equal Time.local(2006, 8, 3, 12), time
33
+
34
+ time = parse_now("aug 3", :context => :past)
35
+ assert_equal Time.local(2006, 8, 3, 12), time
36
+
37
+ time = parse_now("aug 20")
38
+ assert_equal Time.local(2006, 8, 20, 12), time
39
+
40
+ time = parse_now("aug 20", :context => :future)
41
+ assert_equal Time.local(2006, 8, 20, 12), time
42
+
29
43
  # rm_sd_on
30
44
 
31
45
  time = parse_now("5pm on may 28")
@@ -59,6 +73,9 @@ class TestParsing < Test::Unit::TestCase
59
73
  time = parse_now("5:00 pm may 27th", :context => :past)
60
74
  assert_equal Time.local(2006, 5, 27, 17), time
61
75
 
76
+ time = parse_now("05:00 pm may 27th", :context => :past)
77
+ assert_equal Time.local(2006, 5, 27, 17), time
78
+
62
79
  time = parse_now("5pm on may 27th", :context => :past)
63
80
  assert_equal Time.local(2006, 5, 27, 17), time
64
81
 
@@ -73,8 +90,46 @@ class TestParsing < Test::Unit::TestCase
73
90
  time = parse_now("dec 79")
74
91
  assert_equal Time.local(1979, 12, 16, 12), time
75
92
 
93
+ # rm_od_sy
94
+
95
+ time = parse_now("November 18th 2010")
96
+ assert_equal Time.local(2010, 11, 18, 12), time
97
+
98
+ time = parse_now("November 18th, 2010")
99
+ assert_equal Time.local(2010, 11, 18, 12), time
100
+
101
+ time = parse_now("November 18th 2010 midnight")
102
+ assert_equal Time.local(2010, 11, 19, 0), time
103
+
104
+ time = parse_now("November 18th 2010 at midnight")
105
+ assert_equal Time.local(2010, 11, 19, 0), time
106
+
107
+ time = parse_now("November 18th 2010 at 4")
108
+ assert_equal Time.local(2010, 11, 18, 16), time
109
+
110
+ time = parse_now("November 18th 2010 at 4", :ambiguous_time_range => :none)
111
+ assert_equal Time.local(2010, 11, 18, 4), time
112
+
113
+ time = parse_now("March 30th, 1979")
114
+ assert_equal Time.local(1979, 3, 30, 12), time
115
+
116
+ time = parse_now("March 30th 79")
117
+ assert_equal Time.local(1979, 3, 30, 12), time
118
+
119
+ time = parse_now("March 30th 79 4:30")
120
+ assert_equal Time.local(1979, 3, 30, 16, 30), time
121
+
122
+ time = parse_now("March 30th 79 at 4:30", :ambiguous_time_range => :none)
123
+ assert_equal Time.local(1979, 3, 30, 4, 30), time
124
+
76
125
  # rm_sd_sy
77
126
 
127
+ time = parse_now("November 18, 2010")
128
+ assert_equal Time.local(2010, 11, 18, 12), time
129
+
130
+ time = parse_now("February 14, 2004")
131
+ assert_equal Time.local(2004, 2, 14, 12), time
132
+
78
133
  time = parse_now("jan 3 2010")
79
134
  assert_equal Time.local(2010, 1, 3, 12), time
80
135
 
@@ -170,7 +225,7 @@ class TestParsing < Test::Unit::TestCase
170
225
 
171
226
  now = Time.now
172
227
  time = parse_now(now.to_s)
173
- assert_equal now.to_s, time.to_s
228
+ # assert_equal now.to_s, time.to_s
174
229
 
175
230
  # rm_sd_rt
176
231
 
@@ -180,15 +235,26 @@ class TestParsing < Test::Unit::TestCase
180
235
  # old dates
181
236
 
182
237
  time = parse_now("may 40")
183
- assert_equal Time.local(40, 5, 16, 12, 0, 0), time
238
+ assert_equal Time.local(2040, 5, 16, 12, 0, 0), time
184
239
 
185
240
  time = parse_now("may 27 40")
186
- assert_equal Time.local(40, 5, 27, 12, 0, 0), time
241
+ assert_equal Time.local(2040, 5, 27, 12, 0, 0), time
187
242
 
188
243
  time = parse_now("1800-08-20")
189
244
  assert_equal Time.local(1800, 8, 20, 12, 0, 0), time
190
245
  end
191
246
 
247
+ def test_parse_two_digit_years
248
+ time = parse_now("may 97")
249
+ assert_equal Time.local(1997, 5, 16, 12), time
250
+
251
+ time = parse_now("may 1st 01")
252
+ assert_equal Time.local(2001, 5, 1, 12), time
253
+
254
+ time = parse_now("may 79", :ambiguous_year_future_bias => 10)
255
+ assert_equal Time.local(2079, 5, 16, 12), time
256
+ end
257
+
192
258
  def test_parse_guess_r
193
259
  time = parse_now("friday")
194
260
  assert_equal Time.local(2006, 8, 18, 12), time
@@ -246,6 +312,9 @@ class TestParsing < Test::Unit::TestCase
246
312
  time = parse_now("4:00 in the morning")
247
313
  assert_equal Time.local(2006, 8, 16, 4), time
248
314
 
315
+ time = parse_now("0:10")
316
+ assert_equal Time.local(2006, 8, 17, 0, 10), time
317
+
249
318
  time = parse_now("november 4")
250
319
  assert_equal Time.local(2006, 11, 4, 12), time
251
320
 
@@ -325,17 +394,21 @@ class TestParsing < Test::Unit::TestCase
325
394
  # day
326
395
 
327
396
  time = parse_now("this day")
328
- assert_equal Time.local(2006, 8, 16, 19, 30), time
397
+ assert_equal Time.local(2006, 8, 16, 19), time
329
398
 
330
399
  time = parse_now("this day", :context => :past)
331
400
  assert_equal Time.local(2006, 8, 16, 7), time
332
401
 
333
402
  time = parse_now("today")
334
- assert_equal Time.local(2006, 8, 16, 19, 30), time
403
+ assert_equal Time.local(2006, 8, 16, 19), time
335
404
 
336
405
  time = parse_now("yesterday")
337
406
  assert_equal Time.local(2006, 8, 15, 12), time
338
407
 
408
+ now = Time.parse("2011-05-27 23:10") # after 11pm
409
+ time = parse_now("yesterday", :now => now)
410
+ assert_equal Time.local(2011, 05, 26, 12), time
411
+
339
412
  time = parse_now("tomorrow")
340
413
  assert_equal Time.local(2006, 8, 17, 12), time
341
414
 
@@ -452,6 +525,10 @@ class TestParsing < Test::Unit::TestCase
452
525
 
453
526
  time = parse_now("next monday at 12:01 pm")
454
527
  assert_equal Time.local(2006, 8, 21, 12, 1), time
528
+
529
+ # with context
530
+ time = parse_now("sunday at 8:15pm", :context => :past)
531
+ assert_equal Time.local(2006, 8, 13, 20, 15), time
455
532
  end
456
533
 
457
534
  def test_parse_guess_rgr
@@ -578,12 +655,19 @@ class TestParsing < Test::Unit::TestCase
578
655
  end
579
656
 
580
657
  def test_parse_guess_o_r_g_r
581
- time = parse_now("3rd month next year")
582
- assert_equal Time.local(2007, 3, 16, 12, 30), time
658
+ time = parse_now("3rd month next year", :guess => false)
659
+ assert_equal Time.local(2007, 3), time.begin
660
+
661
+ time = parse_now("3rd month next year", :guess => false)
662
+ assert_equal Time.local(2007, 3, 1), time.begin
583
663
 
584
664
  time = parse_now("3rd thursday this september")
585
665
  assert_equal Time.local(2006, 9, 21, 12), time
586
666
 
667
+ now = Time.parse("1/10/2010")
668
+ time = parse_now("3rd thursday this november", :now => now)
669
+ assert_equal Time.local(2010, 11, 18, 12), time
670
+
587
671
  time = parse_now("4th day last week")
588
672
  assert_equal Time.local(2006, 8, 9, 12), time
589
673
  end
@@ -662,16 +746,16 @@ class TestParsing < Test::Unit::TestCase
662
746
  assert_equal Time.local(2007, 6, 20), t.end
663
747
 
664
748
  t = parse_now("this winter", :guess => false)
665
- assert_equal Time.local(2006, 12, 22, 23), t.begin
749
+ assert_equal Time.local(2006, 12, 22), t.begin
666
750
  assert_equal Time.local(2007, 3, 19), t.end
667
751
 
668
752
  t = parse_now("last spring", :guess => false)
669
- assert_equal Time.local(2006, 3, 20, 23), t.begin
753
+ assert_equal Time.local(2006, 3, 20), t.begin
670
754
  assert_equal Time.local(2006, 6, 20), t.end
671
755
 
672
756
  t = parse_now("last winter", :guess => false)
673
- assert_equal Time.local(2005, 12, 22, 23), t.begin
674
- assert_equal Time.local(2006, 3, 19, 23), t.end
757
+ assert_equal Time.local(2005, 12, 22), t.begin
758
+ assert_equal Time.local(2006, 3, 19), t.end
675
759
 
676
760
  t = parse_now("next spring", :guess => false)
677
761
  assert_equal Time.local(2007, 3, 20), t.begin
@@ -694,8 +778,8 @@ class TestParsing < Test::Unit::TestCase
694
778
  t1 = Chronic.parse('1st saturday in november', :now => Time.local(2007))
695
779
  assert_equal Time.local(2007, 11, 3, 12), t1
696
780
 
697
- t1 = Chronic.parse('1st sunday in november', :now => Time.local(2007))
698
- assert_equal Time.local(2007, 11, 4, 11), t1
781
+ # t1 = Chronic.parse('1st sunday in november', :now => Time.local(2007))
782
+ # assert_equal Time.local(2007, 11, 4, 12), t1
699
783
 
700
784
  # Chronic.debug = true
701
785
  #
@@ -703,6 +787,13 @@ class TestParsing < Test::Unit::TestCase
703
787
  # assert_equal Time.local(2007, 11, 5, 11), t1
704
788
  end
705
789
 
790
+ def test_now_changes
791
+ t1 = Chronic.parse("now")
792
+ sleep 0.1
793
+ t2 = Chronic.parse("now")
794
+ assert_not_equal t1, t2
795
+ end
796
+
706
797
  private
707
798
  def parse_now(string, options={})
708
799
  Chronic.parse(string, {:now => TIME_2006_08_16_14_00_00 }.merge(options))