chronic 0.4.1 → 0.4.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.
@@ -1,11 +1,20 @@
1
1
  module Chronic
2
- class Pointer < Tag #:nodoc:
2
+ class Pointer < Tag
3
+
4
+ # Scan an Array of {Token}s and apply any necessary Pointer tags to
5
+ # each token
6
+ #
7
+ # @param [Array<Token>] tokens Array of tokens to scan
8
+ # @param [Hash] options Options specified in {Chronic.parse}
9
+ # @return [Array] list of tokens
3
10
  def self.scan(tokens, options)
4
11
  tokens.each_index do |i|
5
12
  if t = scan_for_all(tokens[i]) then tokens[i].tag(t) end
6
13
  end
7
14
  end
8
15
 
16
+ # @param [Token] token
17
+ # @return [Pointer, nil]
9
18
  def self.scan_for_all(token)
10
19
  scan_for token, self,
11
20
  {
@@ -1,5 +1,12 @@
1
1
  module Chronic
2
- class Repeater < Tag #:nodoc:
2
+ class Repeater < Tag
3
+
4
+ # Scan an Array of {Token}s and apply any necessary Repeater tags to
5
+ # each token
6
+ #
7
+ # @param [Array<Token>] tokens Array of tokens to scan
8
+ # @param [Hash] options Options specified in {Chronic.parse}
9
+ # @return [Array] list of tokens
3
10
  def self.scan(tokens, options)
4
11
  tokens.each_index do |i|
5
12
  if t = scan_for_season_names(tokens[i]) then tokens[i].tag(t); next end
@@ -11,6 +18,8 @@ module Chronic
11
18
  end
12
19
  end
13
20
 
21
+ # @param [Token] token
22
+ # @return [RepeaterSeasonName, nil]
14
23
  def self.scan_for_season_names(token)
15
24
  scan_for token, RepeaterSeasonName,
16
25
  {
@@ -21,6 +30,8 @@ module Chronic
21
30
  }
22
31
  end
23
32
 
33
+ # @param [Token] token
34
+ # @return [RepeaterMonthName, nil]
24
35
  def self.scan_for_month_names(token)
25
36
  scan_for token, RepeaterMonthName,
26
37
  {
@@ -39,6 +50,8 @@ module Chronic
39
50
  }
40
51
  end
41
52
 
53
+ # @param [Token] token
54
+ # @return [RepeaterDayName, nil]
42
55
  def self.scan_for_day_names(token)
43
56
  scan_for token, RepeaterDayName,
44
57
  {
@@ -55,6 +68,8 @@ module Chronic
55
68
  }
56
69
  end
57
70
 
71
+ # @param [Token] token
72
+ # @return [RepeaterDayPortion, nil]
58
73
  def self.scan_for_day_portions(token)
59
74
  scan_for token, RepeaterDayPortion,
60
75
  {
@@ -67,10 +82,14 @@ module Chronic
67
82
  }
68
83
  end
69
84
 
85
+ # @param [Token] token
86
+ # @return [RepeaterTime, nil]
70
87
  def self.scan_for_times(token)
71
88
  scan_for token, RepeaterTime, /^\d{1,2}(:?\d{2})?([\.:]?\d{2})?$/
72
89
  end
73
90
 
91
+ # @param [Token] token
92
+ # @return [Repeater] A new instance of a subclass of Repeater
74
93
  def self.scan_for_units(token)
75
94
  {
76
95
  /^years?$/ => :year,
@@ -2,6 +2,8 @@ module Chronic
2
2
  class RepeaterMonth < Repeater #:nodoc:
3
3
  MONTH_SECONDS = 2_592_000 # 30 * 24 * 60 * 60
4
4
  YEAR_MONTHS = 12
5
+ MONTH_DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
6
+ MONTH_DAYS_LEAP = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
5
7
 
6
8
  def initialize(type)
7
9
  super
@@ -54,7 +56,11 @@ module Chronic
54
56
  new_year += 1
55
57
  new_month -= YEAR_MONTHS
56
58
  end
57
- Time.construct(new_year, new_month, time.day, time.hour, time.min, time.sec)
59
+
60
+ days = month_days(new_year, new_month)
61
+ new_day = time.day > days ? days : time.day
62
+
63
+ Time.construct(new_year, new_month, new_day, time.hour, time.min, time.sec)
58
64
  end
59
65
 
60
66
  def width
@@ -64,5 +70,11 @@ module Chronic
64
70
  def to_s
65
71
  super << '-month'
66
72
  end
73
+
74
+ private
75
+
76
+ def month_days(year, month)
77
+ Date.leap?(year) ? MONTH_DAYS_LEAP[month - 1] : MONTH_DAYS[month - 1]
78
+ end
67
79
  end
68
80
  end
@@ -1,7 +1,13 @@
1
1
  module Chronic
2
- class Scalar < Tag #:nodoc:
2
+ class Scalar < Tag
3
3
  DAY_PORTIONS = %w( am pm morning afternoon evening night )
4
4
 
5
+ # Scan an Array of {Token}s and apply any necessary Scalar tags to
6
+ # each token
7
+ #
8
+ # @param [Array<Token>] tokens Array of tokens to scan
9
+ # @param [Hash] options Options specified in {Chronic.parse}
10
+ # @return [Array] list of tokens
5
11
  def self.scan(tokens, options)
6
12
  tokens.each_index do |i|
7
13
  if t = scan_for_scalars(tokens[i], tokens[i + 1]) then tokens[i].tag(t) end
@@ -11,6 +17,9 @@ module Chronic
11
17
  end
12
18
  end
13
19
 
20
+ # @param [Token] token
21
+ # @param [Token] post_token
22
+ # @return [Scalar, nil]
14
23
  def self.scan_for_scalars(token, post_token)
15
24
  if token.word =~ /^\d*$/
16
25
  unless post_token && DAY_PORTIONS.include?(post_token.word)
@@ -19,6 +28,9 @@ module Chronic
19
28
  end
20
29
  end
21
30
 
31
+ # @param [Token] token
32
+ # @param [Token] post_token
33
+ # @return [ScalarDay, nil]
22
34
  def self.scan_for_days(token, post_token)
23
35
  if token.word =~ /^\d\d?$/
24
36
  toi = token.word.to_i
@@ -28,6 +40,9 @@ module Chronic
28
40
  end
29
41
  end
30
42
 
43
+ # @param [Token] token
44
+ # @param [Token] post_token
45
+ # @return [ScalarMonth, nil]
31
46
  def self.scan_for_months(token, post_token)
32
47
  if token.word =~ /^\d\d?$/
33
48
  toi = token.word.to_i
@@ -37,6 +52,10 @@ module Chronic
37
52
  end
38
53
  end
39
54
 
55
+ # @param [Token] token
56
+ # @param [Token] post_token
57
+ # @param [Hash] options Options specified in {Chronic.parse}
58
+ # @return [ScalarYear, nil]
40
59
  def self.scan_for_years(token, post_token, options)
41
60
  if token.word =~ /^([1-9]\d)?\d\d?$/
42
61
  unless post_token && DAY_PORTIONS.include?(post_token.word)
@@ -47,6 +66,15 @@ module Chronic
47
66
  end
48
67
 
49
68
  # Build a year from a 2 digit suffix
69
+ #
70
+ # @example
71
+ # make_year(96, 50) #=> 1996
72
+ # make_year(79, 20) #=> 2079
73
+ # make_year(00, 50) #=> 2000
74
+ #
75
+ # @param [Integer] year The two digit year to build from
76
+ # @param [Integer] bias The amount of future years to bias
77
+ # @return [Integer] The 4 digit year
50
78
  def self.make_year(year, bias)
51
79
  return year if year.to_s.size > 2
52
80
  start_year = Chronic.time_class.now.year - bias
@@ -1,5 +1,12 @@
1
1
  module Chronic
2
- class Separator < Tag #:nodoc:
2
+ class Separator < Tag
3
+
4
+ # Scan an Array of {Token}s and apply any necessary Separator tags to
5
+ # each token
6
+ #
7
+ # @param [Array<Token>] tokens Array of tokens to scan
8
+ # @param [Hash] options Options specified in {Chronic.parse}
9
+ # @return [Array] list of tokens
3
10
  def self.scan(tokens, options)
4
11
  tokens.each_index do |i|
5
12
  if t = scan_for_commas(tokens[i]) then tokens[i].tag(t); next end
@@ -10,10 +17,14 @@ module Chronic
10
17
  end
11
18
  end
12
19
 
20
+ # @param [Token] token
21
+ # @return [SeparatorComma, nil]
13
22
  def self.scan_for_commas(token)
14
23
  scan_for token, SeparatorComma, { /^,$/ => :comma }
15
24
  end
16
25
 
26
+ # @param [Token] token
27
+ # @return [SeparatorSlashOrDash, nil]
17
28
  def self.scan_for_slash_or_dash(token)
18
29
  scan_for token, SeparatorSlashOrDash,
19
30
  {
@@ -22,14 +33,20 @@ module Chronic
22
33
  }
23
34
  end
24
35
 
36
+ # @param [Token] token
37
+ # @return [SeparatorAt, nil]
25
38
  def self.scan_for_at(token)
26
39
  scan_for token, SeparatorAt, { /^(at|@)$/ => :at }
27
40
  end
28
41
 
42
+ # @param [Token] token
43
+ # @return [SeparatorIn, nil]
29
44
  def self.scan_for_in(token)
30
45
  scan_for token, SeparatorIn, { /^in$/ => :in }
31
46
  end
32
47
 
48
+ # @param [Token] token
49
+ # @return [SeparatorOn, nil]
33
50
  def self.scan_for_on(token)
34
51
  scan_for token, SeparatorOn, { /^on$/ => :on }
35
52
  end
@@ -1,11 +1,20 @@
1
1
  module Chronic
2
- class TimeZone < Tag #:nodoc:
2
+ class TimeZone < Tag
3
+
4
+ # Scan an Array of {Token}s and apply any necessary TimeZone tags to
5
+ # each token
6
+ #
7
+ # @param [Array<Token>] tokens Array of tokens to scan
8
+ # @param [Hash] options Options specified in {Chronic.parse}
9
+ # @return [Array] list of tokens
3
10
  def self.scan(tokens, options)
4
11
  tokens.each_index do |i|
5
12
  if t = scan_for_all(tokens[i]) then tokens[i].tag(t); next end
6
13
  end
7
14
  end
8
15
 
16
+ # @param [Token] token
17
+ # @return [TimeZone, nil]
9
18
  def self.scan_for_all(token)
10
19
  scan_for token, self,
11
20
  {
@@ -1,6 +1,11 @@
1
1
  module Chronic
2
- class Token #:nodoc:
3
- attr_accessor :word, :tags
2
+ class Token
3
+
4
+ # @return [String] The word this Token represents
5
+ attr_accessor :word
6
+
7
+ # @return [Array] A list of tag associated with this Token
8
+ attr_accessor :tags
4
9
 
5
10
  def initialize(word)
6
11
  @word = word
@@ -8,21 +13,26 @@ module Chronic
8
13
  end
9
14
 
10
15
  # Tag this token with the specified tag
16
+ #
17
+ # @param [Tag] new_tag A instance of {Tag} or one of its subclasses
11
18
  def tag(new_tag)
12
19
  @tags << new_tag
13
20
  end
14
21
 
15
22
  # Remove all tags of the given class
23
+ #
24
+ # @param [Class] The tag class to remove
16
25
  def untag(tag_class)
17
26
  @tags.delete_if { |m| m.kind_of? tag_class }
18
27
  end
19
28
 
20
- # Return true if this token has any tags
29
+ # @return [Boolean] true if this token has any tags
21
30
  def tagged?
22
31
  @tags.size > 0
23
32
  end
24
33
 
25
- # Return the Tag that matches the given class
34
+ # @param [Class] tag_class The tag class to search for
35
+ # @return [Tag] The first Tag that matches the given class
26
36
  def get_tag(tag_class)
27
37
  @tags.find { |m| m.kind_of? tag_class }
28
38
  end
@@ -21,7 +21,7 @@ class TestChronic < Test::Unit::TestCase
21
21
 
22
22
  assert_equal :morning, tokens[1].tags[0].type
23
23
 
24
- tokens = Chronic.dealias_and_disambiguate_times(tokens, {})
24
+ tokens = Chronic::Handlers.dealias_and_disambiguate_times(tokens, {})
25
25
 
26
26
  assert_equal :am, tokens[1].tags[0].type
27
27
  assert_equal 2, tokens.size
@@ -34,7 +34,7 @@ class TestChronic < Test::Unit::TestCase
34
34
 
35
35
  assert_equal :morning, tokens[1].tags[0].type
36
36
 
37
- tokens = Chronic.dealias_and_disambiguate_times(tokens, {})
37
+ tokens = Chronic::Handlers.dealias_and_disambiguate_times(tokens, {})
38
38
 
39
39
  assert_equal :morning, tokens[1].tags[0].type
40
40
  assert_equal 2, tokens.size
@@ -51,4 +51,54 @@ class TestChronic < Test::Unit::TestCase
51
51
  assert_equal Time.local(2006, 11, 16), Chronic.guess(span)
52
52
  end
53
53
 
54
+ def test_now
55
+ Chronic.parse('now', :now => Time.local(2006, 01))
56
+ assert_equal Time.local(2006, 01), Chronic.now
57
+
58
+ Chronic.parse('now', :now => Time.local(2007, 01))
59
+ assert_equal Time.local(2007, 01), Chronic.now
60
+ end
61
+
62
+ def test_endian_definitions
63
+ # middle, little
64
+ endians = [
65
+ Chronic::Handler.new([:scalar_month, :separator_slash_or_dash, :scalar_day, :separator_slash_or_dash, :scalar_year, :separator_at?, 'time?'], :handle_sm_sd_sy),
66
+ Chronic::Handler.new([:scalar_day, :separator_slash_or_dash, :scalar_month, :separator_slash_or_dash, :scalar_year, :separator_at?, 'time?'], :handle_sd_sm_sy)
67
+ ]
68
+
69
+ assert_equal endians, Chronic.definitions[:endian]
70
+
71
+ defs = Chronic.definitions(:endian_precedence => :little)
72
+ assert_equal endians.reverse, defs[:endian]
73
+
74
+ defs = Chronic.definitions(:endian_precedence => [:little, :middle])
75
+ assert_equal endians.reverse, defs[:endian]
76
+
77
+ assert_raises(Chronic::InvalidArgumentException) do
78
+ Chronic.definitions(:endian_precedence => :invalid)
79
+ end
80
+ end
81
+
82
+ def test_passing_options
83
+ assert_raises(Chronic::InvalidArgumentException) do
84
+ Chronic.parse('now', :invalid => :option)
85
+ end
86
+
87
+ assert_raises(Chronic::InvalidArgumentException) do
88
+ Chronic.parse('now', :context => :invalid_context)
89
+ end
90
+ end
91
+
92
+ def test_debug
93
+ require 'stringio'
94
+ $stdout = StringIO.new
95
+ Chronic.debug = true
96
+
97
+ Chronic.parse 'now'
98
+ assert $stdout.string.include?('this(grabber-this)')
99
+ ensure
100
+ $stdout = STDOUT
101
+ Chronic.debug = false
102
+ end
103
+
54
104
  end
@@ -23,6 +23,10 @@ class TestRepeaterMonth < Test::Unit::TestCase
23
23
 
24
24
  time = Chronic::RepeaterMonth.new(:month).offset_by(@now, 10, :past)
25
25
  assert_equal Time.local(2005, 10, 16, 14), time
26
+
27
+ time = Chronic::RepeaterMonth.new(:month).offset_by(Time.local(2010, 3, 29), 1, :past)
28
+ assert_equal 2, time.month
29
+ assert_equal 28, time.day
26
30
  end
27
31
 
28
32
  def test_offset
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: chronic
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.4.1
5
+ version: 0.4.2
6
6
  platform: ruby
7
7
  authors:
8
8
  - Tom Preston-Werner
@@ -11,7 +11,7 @@ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
13
 
14
- date: 2011-06-05 00:00:00 Z
14
+ date: 2011-06-07 00:00:00 Z
15
15
  dependencies: []
16
16
 
17
17
  description: Chronic is a natural language date/time parser written in pure Ruby.