chronic 0.9.1 → 0.10.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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -4
  3. data/HISTORY.md +21 -0
  4. data/README.md +8 -0
  5. data/Rakefile +28 -8
  6. data/chronic.gemspec +8 -5
  7. data/lib/chronic/date.rb +82 -0
  8. data/lib/chronic/handler.rb +34 -25
  9. data/lib/chronic/handlers.rb +22 -3
  10. data/lib/chronic/ordinal.rb +22 -20
  11. data/lib/chronic/parser.rb +31 -26
  12. data/lib/chronic/repeater.rb +18 -18
  13. data/lib/chronic/repeaters/repeater_day.rb +4 -3
  14. data/lib/chronic/repeaters/repeater_day_name.rb +5 -4
  15. data/lib/chronic/repeaters/repeater_day_portion.rb +4 -3
  16. data/lib/chronic/repeaters/repeater_fortnight.rb +4 -3
  17. data/lib/chronic/repeaters/repeater_hour.rb +4 -3
  18. data/lib/chronic/repeaters/repeater_minute.rb +4 -3
  19. data/lib/chronic/repeaters/repeater_month.rb +5 -4
  20. data/lib/chronic/repeaters/repeater_month_name.rb +4 -3
  21. data/lib/chronic/repeaters/repeater_season.rb +5 -3
  22. data/lib/chronic/repeaters/repeater_second.rb +4 -3
  23. data/lib/chronic/repeaters/repeater_time.rb +35 -25
  24. data/lib/chronic/repeaters/repeater_week.rb +4 -3
  25. data/lib/chronic/repeaters/repeater_weekday.rb +4 -3
  26. data/lib/chronic/repeaters/repeater_weekend.rb +4 -3
  27. data/lib/chronic/repeaters/repeater_year.rb +5 -4
  28. data/lib/chronic/scalar.rb +34 -69
  29. data/lib/chronic/separator.rb +107 -8
  30. data/lib/chronic/sign.rb +49 -0
  31. data/lib/chronic/tag.rb +5 -4
  32. data/lib/chronic/time.rb +40 -0
  33. data/lib/chronic.rb +16 -10
  34. data/test/helper.rb +2 -2
  35. data/test/test_chronic.rb +55 -4
  36. data/test/test_handler.rb +20 -0
  37. data/test/test_parsing.rb +75 -3
  38. data/test/test_repeater_time.rb +18 -0
  39. metadata +37 -5
@@ -11,11 +11,18 @@ module Chronic
11
11
  def self.scan(tokens, options)
12
12
  tokens.each do |token|
13
13
  if t = scan_for_commas(token) then token.tag(t); next end
14
- if t = scan_for_slash_or_dash(token) then token.tag(t); next end
14
+ if t = scan_for_dots(token) then token.tag(t); next end
15
+ if t = scan_for_colon(token) then token.tag(t); next end
16
+ if t = scan_for_space(token) then token.tag(t); next end
17
+ if t = scan_for_slash(token) then token.tag(t); next end
18
+ if t = scan_for_dash(token) then token.tag(t); next end
19
+ if t = scan_for_quote(token) then token.tag(t); next end
15
20
  if t = scan_for_at(token) then token.tag(t); next end
16
21
  if t = scan_for_in(token) then token.tag(t); next end
17
22
  if t = scan_for_on(token) then token.tag(t); next end
18
23
  if t = scan_for_and(token) then token.tag(t); next end
24
+ if t = scan_for_t(token) then token.tag(t); next end
25
+ if t = scan_for_w(token) then token.tag(t); next end
19
26
  end
20
27
  end
21
28
 
@@ -28,12 +35,47 @@ module Chronic
28
35
 
29
36
  # token - The Token object we want to scan.
30
37
  #
31
- # Returns a new SeparatorSlashOrDash object.
32
- def self.scan_for_slash_or_dash(token)
33
- scan_for token, SeparatorSlashOrDash,
38
+ # Returns a new SeparatorDot object.
39
+ def self.scan_for_dots(token)
40
+ scan_for token, SeparatorDot, { /^\.$/ => :dot }
41
+ end
42
+
43
+ # token - The Token object we want to scan.
44
+ #
45
+ # Returns a new SeparatorColon object.
46
+ def self.scan_for_colon(token)
47
+ scan_for token, SeparatorColon, { /^:$/ => :colon }
48
+ end
49
+
50
+ # token - The Token object we want to scan.
51
+ #
52
+ # Returns a new SeparatorSpace object.
53
+ def self.scan_for_space(token)
54
+ scan_for token, SeparatorSpace, { /^ $/ => :space }
55
+ end
56
+
57
+ # token - The Token object we want to scan.
58
+ #
59
+ # Returns a new SeparatorSlash object.
60
+ def self.scan_for_slash(token)
61
+ scan_for token, SeparatorSlash, { /^\/$/ => :slash }
62
+ end
63
+
64
+ # token - The Token object we want to scan.
65
+ #
66
+ # Returns a new SeparatorDash object.
67
+ def self.scan_for_dash(token)
68
+ scan_for token, SeparatorDash, { /^-$/ => :dash }
69
+ end
70
+
71
+ # token - The Token object we want to scan.
72
+ #
73
+ # Returns a new SeparatorQuote object.
74
+ def self.scan_for_quote(token)
75
+ scan_for token, SeparatorQuote,
34
76
  {
35
- /^-$/ => :dash,
36
- /^\/$/ => :slash
77
+ /^'$/ => :single_quote,
78
+ /^"$/ => :double_quote
37
79
  }
38
80
  end
39
81
 
@@ -65,6 +107,20 @@ module Chronic
65
107
  scan_for token, SeparatorAnd, { /^and$/ => :and }
66
108
  end
67
109
 
110
+ # token - The Token object we want to scan.
111
+ #
112
+ # Returns a new SeperatorT Object object.
113
+ def self.scan_for_t(token)
114
+ scan_for token, SeparatorT, { /^t$/ => :T }
115
+ end
116
+
117
+ # token - The Token object we want to scan.
118
+ #
119
+ # Returns a new SeperatorW Object object.
120
+ def self.scan_for_w(token)
121
+ scan_for token, SeparatorW, { /^w$/ => :W }
122
+ end
123
+
68
124
  def to_s
69
125
  'separator'
70
126
  end
@@ -76,9 +132,39 @@ module Chronic
76
132
  end
77
133
  end
78
134
 
79
- class SeparatorSlashOrDash < Separator #:nodoc:
135
+ class SeparatorDot < Separator #:nodoc:
80
136
  def to_s
81
- super << '-slashordash-' << @type.to_s
137
+ super << '-dot'
138
+ end
139
+ end
140
+
141
+ class SeparatorColon < Separator #:nodoc:
142
+ def to_s
143
+ super << '-colon'
144
+ end
145
+ end
146
+
147
+ class SeparatorSpace < Separator #:nodoc:
148
+ def to_s
149
+ super << '-space'
150
+ end
151
+ end
152
+
153
+ class SeparatorSlash < Separator #:nodoc:
154
+ def to_s
155
+ super << '-slash'
156
+ end
157
+ end
158
+
159
+ class SeparatorDash < Separator #:nodoc:
160
+ def to_s
161
+ super << '-dash'
162
+ end
163
+ end
164
+
165
+ class SeparatorQuote < Separator #:nodoc:
166
+ def to_s
167
+ super << '-quote-' << @type.to_s
82
168
  end
83
169
  end
84
170
 
@@ -105,4 +191,17 @@ module Chronic
105
191
  super << '-and'
106
192
  end
107
193
  end
194
+
195
+ class SeparatorT < Separator #:nodoc:
196
+ def to_s
197
+ super << '-T'
198
+ end
199
+ end
200
+
201
+ class SeparatorW < Separator #:nodoc:
202
+ def to_s
203
+ super << '-W'
204
+ end
205
+ end
206
+
108
207
  end
@@ -0,0 +1,49 @@
1
+ module Chronic
2
+ class Sign < Tag
3
+
4
+ # Scan an Array of Token objects and apply any necessary Sign
5
+ # tags to each token.
6
+ #
7
+ # tokens - An Array of tokens to scan.
8
+ # options - The Hash of options specified in Chronic::parse.
9
+ #
10
+ # Returns an Array of tokens.
11
+ def self.scan(tokens, options)
12
+ tokens.each do |token|
13
+ if t = scan_for_plus(token) then token.tag(t); next end
14
+ if t = scan_for_minus(token) then token.tag(t); next end
15
+ end
16
+ end
17
+
18
+ # token - The Token object we want to scan.
19
+ #
20
+ # Returns a new SignPlus object.
21
+ def self.scan_for_plus(token)
22
+ scan_for token, SignPlus, { /^\+$/ => :plus }
23
+ end
24
+
25
+ # token - The Token object we want to scan.
26
+ #
27
+ # Returns a new SignMinus object.
28
+ def self.scan_for_minus(token)
29
+ scan_for token, SignMinus, { /^-$/ => :minus }
30
+ end
31
+
32
+ def to_s
33
+ 'sign'
34
+ end
35
+ end
36
+
37
+ class SignPlus < Sign #:nodoc:
38
+ def to_s
39
+ super << '-plus'
40
+ end
41
+ end
42
+
43
+ class SignMinus < Sign #:nodoc:
44
+ def to_s
45
+ super << '-minus'
46
+ end
47
+ end
48
+
49
+ end
data/lib/chronic/tag.rb CHANGED
@@ -6,8 +6,9 @@ module Chronic
6
6
  attr_accessor :type
7
7
 
8
8
  # type - The Symbol type of this tag.
9
- def initialize(type)
9
+ def initialize(type, options = {})
10
10
  @type = type
11
+ @options = options
11
12
  end
12
13
 
13
14
  # time - Set the start Time for this Tag.
@@ -18,13 +19,13 @@ module Chronic
18
19
  class << self
19
20
  private
20
21
 
21
- def scan_for(token, klass, items={})
22
+ def scan_for(token, klass, items={}, options = {})
22
23
  case items
23
24
  when Regexp
24
- return klass.new(token.word) if items =~ token.word
25
+ return klass.new(token.word, options) if items =~ token.word
25
26
  when Hash
26
27
  items.each do |item, symbol|
27
- return klass.new(symbol) if item =~ token.word
28
+ return klass.new(symbol, options) if item =~ token.word
28
29
  end
29
30
  end
30
31
  nil
@@ -0,0 +1,40 @@
1
+ module Chronic
2
+ class Time
3
+ HOUR_SECONDS = 3600 # 60 * 60
4
+ MINUTE_SECONDS = 60
5
+ SECOND_SECONDS = 1 # haha, awesome
6
+ SUBSECOND_SECONDS = 0.001
7
+
8
+ # Checks if given number could be hour
9
+ def self.could_be_hour?(hour)
10
+ hour >= 0 && hour <= 24
11
+ end
12
+
13
+ # Checks if given number could be minute
14
+ def self.could_be_minute?(minute)
15
+ minute >= 0 && minute <= 60
16
+ end
17
+
18
+ # Checks if given number could be second
19
+ def self.could_be_second?(second)
20
+ second >= 0 && second <= 60
21
+ end
22
+
23
+ # Checks if given number could be subsecond
24
+ def self.could_be_subsecond?(subsecond)
25
+ subsecond >= 0 && subsecond <= 999999
26
+ end
27
+
28
+ # normalize offset in seconds to offset as string +mm:ss or -mm:ss
29
+ def self.normalize_offset(offset)
30
+ return offset if offset.is_a?(String)
31
+ offset = Chronic.time_class.now.to_time.utc_offset unless offset # get current system's UTC offset if offset is nil
32
+ sign = '+'
33
+ sign = '-' if offset < 0
34
+ hours = (offset.abs / 3600).to_i.to_s.rjust(2,'0')
35
+ minutes = (offset.abs % 3600).to_s.rjust(2,'0')
36
+ sign + hours + minutes
37
+ end
38
+
39
+ end
40
+ end
data/lib/chronic.rb CHANGED
@@ -2,6 +2,8 @@ require 'time'
2
2
  require 'date'
3
3
 
4
4
  require 'chronic/parser'
5
+ require 'chronic/date'
6
+ require 'chronic/time'
5
7
 
6
8
  require 'chronic/handler'
7
9
  require 'chronic/handlers'
@@ -14,6 +16,7 @@ require 'chronic/pointer'
14
16
  require 'chronic/scalar'
15
17
  require 'chronic/ordinal'
16
18
  require 'chronic/separator'
19
+ require 'chronic/sign'
17
20
  require 'chronic/time_zone'
18
21
  require 'chronic/numerizer'
19
22
  require 'chronic/season'
@@ -50,7 +53,7 @@ require 'chronic/repeaters/repeater_time'
50
53
  # Chronic.parse('monday', :context => :past)
51
54
  # #=> Mon Aug 21 12:00:00 PDT 2006
52
55
  module Chronic
53
- VERSION = "0.9.1"
56
+ VERSION = "0.10.2"
54
57
 
55
58
  class << self
56
59
 
@@ -72,7 +75,7 @@ module Chronic
72
75
  end
73
76
 
74
77
  self.debug = false
75
- self.time_class = Time
78
+ self.time_class = ::Time
76
79
 
77
80
 
78
81
  # Parses a string containing a natural language date or time.
@@ -98,7 +101,7 @@ module Chronic
98
101
  # second - Integer second.
99
102
  #
100
103
  # Returns a new Time object constructed from these params.
101
- def self.construct(year, month = 1, day = 1, hour = 0, minute = 0, second = 0)
104
+ def self.construct(year, month = 1, day = 1, hour = 0, minute = 0, second = 0, offset = nil)
102
105
  if second >= 60
103
106
  minute += second / 60
104
107
  second = second % 60
@@ -117,11 +120,8 @@ module Chronic
117
120
  # determine if there is a day overflow. this is complicated by our crappy calendar
118
121
  # system (non-constant number of days per month)
119
122
  day <= 56 || raise("day must be no more than 56 (makes month resolution easier)")
120
- if day > 28
121
- # no month ever has fewer than 28 days, so only do this if necessary
122
- leap_year_month_days = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
123
- common_year_month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
124
- days_this_month = Date.leap?(year) ? leap_year_month_days[month - 1] : common_year_month_days[month - 1]
123
+ if day > 28 # no month ever has fewer than 28 days, so only do this if necessary
124
+ days_this_month = ::Date.leap?(year) ? Date::MONTH_DAYS_LEAP[month] : Date::MONTH_DAYS[month]
125
125
  if day > days_this_month
126
126
  month += day / days_this_month
127
127
  day = day % days_this_month
@@ -137,8 +137,14 @@ module Chronic
137
137
  month = month % 12
138
138
  end
139
139
  end
140
-
141
- Chronic.time_class.local(year, month, day, hour, minute, second)
140
+ if Chronic.time_class.name == "Date"
141
+ Chronic.time_class.new(year, month, day)
142
+ elsif not Chronic.time_class.respond_to?(:new) or (RUBY_VERSION.to_f < 1.9 and Chronic.time_class.name == "Time")
143
+ Chronic.time_class.local(year, month, day, hour, minute, second)
144
+ else
145
+ offset = Time::normalize_offset(offset) if Chronic.time_class.name == "DateTime"
146
+ Chronic.time_class.new(year, month, day, hour, minute, second, offset)
147
+ end
142
148
  end
143
149
 
144
150
  end
data/test/helper.rb CHANGED
@@ -5,8 +5,8 @@ end
5
5
 
6
6
  require 'minitest/autorun'
7
7
 
8
- class TestCase < MiniTest::Unit::TestCase
8
+ class TestCase < MiniTest::Test
9
9
  def self.test(name, &block)
10
10
  define_method("test_#{name.gsub(/\W/, '_')}", &block) if block
11
11
  end
12
- end
12
+ end
data/test/test_chronic.rb CHANGED
@@ -58,10 +58,10 @@ class TestChronic < TestCase
58
58
  def test_endian_definitions
59
59
  # middle, little
60
60
  endians = [
61
- Chronic::Handler.new([:scalar_month, :separator_slash_or_dash, :scalar_day, :separator_slash_or_dash, :scalar_year, :separator_at?, 'time?'], :handle_sm_sd_sy),
62
- Chronic::Handler.new([:scalar_month, :separator_slash_or_dash, :scalar_day, :separator_at?, 'time?'], :handle_sm_sd),
63
- Chronic::Handler.new([:scalar_day, :separator_slash_or_dash, :scalar_month, :separator_at?, 'time?'], :handle_sd_sm),
64
- Chronic::Handler.new([:scalar_day, :separator_slash_or_dash, :scalar_month, :separator_slash_or_dash, :scalar_year, :separator_at?, 'time?'], :handle_sd_sm_sy)
61
+ Chronic::Handler.new([:scalar_month, [:separator_slash, :separator_dash], :scalar_day, [:separator_slash, :separator_dash], :scalar_year, :separator_at?, 'time?'], :handle_sm_sd_sy),
62
+ Chronic::Handler.new([:scalar_month, [:separator_slash, :separator_dash], :scalar_day, :separator_at?, 'time?'], :handle_sm_sd),
63
+ Chronic::Handler.new([:scalar_day, [:separator_slash, :separator_dash], :scalar_month, :separator_at?, 'time?'], :handle_sd_sm),
64
+ Chronic::Handler.new([:scalar_day, [:separator_slash, :separator_dash], :scalar_month, [:separator_slash, :separator_dash], :scalar_year, :separator_at?, 'time?'], :handle_sd_sm_sy)
65
65
  ]
66
66
 
67
67
  assert_equal endians, Chronic::Parser.new.definitions[:endian]
@@ -129,4 +129,55 @@ class TestChronic < TestCase
129
129
  assert_equal Time.local(2005, 12), Chronic.construct(2000, 72)
130
130
  end
131
131
 
132
+ def test_time
133
+ org = Chronic.time_class
134
+ begin
135
+ Chronic.time_class = ::Time
136
+ assert_equal ::Time.new(2013, 8, 27, 20, 30, 40, '+05:30'), Chronic.construct(2013, 8, 27, 20, 30, 40, '+05:30')
137
+ assert_equal ::Time.new(2013, 8, 27, 20, 30, 40, '-08:00'), Chronic.construct(2013, 8, 27, 20, 30, 40, -28800)
138
+ ensure
139
+ Chronic.time_class = org
140
+ end
141
+ end
142
+
143
+ def test_date
144
+ org = Chronic.time_class
145
+ begin
146
+ Chronic.time_class = ::Date
147
+ assert_equal Date.new(2013, 8, 27), Chronic.construct(2013, 8, 27)
148
+ ensure
149
+ Chronic.time_class = org
150
+ end
151
+ end
152
+
153
+ def test_datetime
154
+ org = Chronic.time_class
155
+ begin
156
+ Chronic.time_class = ::DateTime
157
+ assert_equal DateTime.new(2013, 8, 27, 20, 30, 40, '+05:30'), Chronic.construct(2013, 8, 27, 20, 30, 40, '+05:30')
158
+ assert_equal DateTime.new(2013, 8, 27, 20, 30, 40, '-08:00'), Chronic.construct(2013, 8, 27, 20, 30, 40, -28800)
159
+ ensure
160
+ Chronic.time_class = org
161
+ end
162
+ end
163
+
164
+ def test_activesupport
165
+ =begin
166
+ # ActiveSupport needs MiniTest '~> 4.2' which conflicts with '~> 5.0'
167
+ require 'active_support/time'
168
+ org = Chronic.time_class
169
+ org_zone = ::Time.zone
170
+ begin
171
+ ::Time.zone = "Tokyo"
172
+ Chronic.time_class = ::Time.zone
173
+ assert_equal Time.new(2013, 8, 27, 20, 30, 40, '+09:00'), Chronic.construct(2013, 8, 27, 20, 30, 40)
174
+ ::Time.zone = "Indiana (East)"
175
+ Chronic.time_class = ::Time.zone
176
+ assert_equal Time.new(2013, 8, 27, 20, 30, 40, -14400), Chronic.construct(2013, 8, 27, 20, 30, 40)
177
+ ensure
178
+ Chronic.time_class = org
179
+ ::Time.zone = org_zone
180
+ end
181
+ =end
182
+ end
132
183
  end
data/test/test_handler.rb CHANGED
@@ -105,4 +105,24 @@ class TestHandler < TestCase
105
105
  assert handler.match(tokens, definitions)
106
106
  end
107
107
 
108
+ def test_handler_class_7
109
+ handler = Chronic::Handler.new([[:separator_on, :separator_at], :scalar], :handler)
110
+
111
+ tokens = [Chronic::Token.new('at'),
112
+ Chronic::Token.new('14')]
113
+
114
+ tokens[0].tag(Chronic::SeparatorAt.new('at'))
115
+ tokens[1].tag(Chronic::Scalar.new(14))
116
+
117
+ assert handler.match(tokens, definitions)
118
+
119
+ tokens = [Chronic::Token.new('on'),
120
+ Chronic::Token.new('15')]
121
+
122
+ tokens[0].tag(Chronic::SeparatorOn.new('on'))
123
+ tokens[1].tag(Chronic::Scalar.new(15))
124
+
125
+ assert handler.match(tokens, definitions)
126
+ end
127
+
108
128
  end
data/test/test_parsing.rb CHANGED
@@ -18,12 +18,25 @@ class TestParsing < TestCase
18
18
  time = Chronic.parse("2012-08-02T08:00:00-04:00")
19
19
  assert_equal Time.utc(2012, 8, 2, 12), time
20
20
 
21
+ time = Chronic.parse("2013-08-01T19:30:00.345-07:00")
22
+ time2 = Time.parse("2013-08-01 019:30:00.345-07:00")
23
+ assert_in_delta time, time2, 0.001
24
+
21
25
  time = Chronic.parse("2012-08-02T12:00:00Z")
22
26
  assert_equal Time.utc(2012, 8, 2, 12), time
23
27
 
24
28
  time = Chronic.parse("2012-01-03 01:00:00.100")
25
29
  time2 = Time.parse("2012-01-03 01:00:00.100")
26
- assert_equal time, time2
30
+ assert_in_delta time, time2, 0.001
31
+
32
+ time = Chronic.parse("2012-01-03 01:00:00.234567")
33
+ time2 = Time.parse("2012-01-03 01:00:00.234567")
34
+ assert_in_delta time, time2, 0.000001
35
+
36
+ assert_nil Chronic.parse("1/1/32.1")
37
+
38
+ time = Chronic.parse("28th", {:guess => :begin})
39
+ assert_equal Time.new(Time.now.year, Time.now.month, 28), time
27
40
  end
28
41
 
29
42
  def test_handle_rmn_sd
@@ -59,6 +72,9 @@ class TestParsing < TestCase
59
72
 
60
73
  time = parse_now("may 28 at 5:32.19pm", :context => :past)
61
74
  assert_equal Time.local(2006, 5, 28, 17, 32, 19), time
75
+
76
+ time = parse_now("may 28 at 5:32:19.764")
77
+ assert_in_delta Time.local(2007, 5, 28, 17, 32, 19, 764000), time, 0.001
62
78
  end
63
79
 
64
80
  def test_handle_rmn_sd_on
@@ -164,8 +180,14 @@ class TestParsing < TestCase
164
180
  time = parse_now("2011-07-03 22:11:35 +01:00")
165
181
  assert_equal 1309727495, time.to_i
166
182
 
183
+ time = parse_now("2011-07-03 16:11:35 -05:00")
184
+ assert_equal 1309727495, time.to_i
185
+
167
186
  time = parse_now("2011-07-03 21:11:35 UTC")
168
187
  assert_equal 1309727495, time.to_i
188
+
189
+ time = parse_now("2011-07-03 21:11:35.362 UTC")
190
+ assert_in_delta 1309727495.362, time.to_f, 0.001
169
191
  end
170
192
 
171
193
  def test_handle_rmn_sd_sy
@@ -282,6 +304,9 @@ class TestParsing < TestCase
282
304
  # month day overflows
283
305
  time = parse_now("30/2/2000")
284
306
  assert_nil time
307
+
308
+ time = parse_now("2013-03-12 17:00", :context => :past)
309
+ assert_equal Time.local(2013, 3, 12, 17, 0, 0), time
285
310
  end
286
311
 
287
312
  def test_handle_sd_sm_sy
@@ -293,6 +318,15 @@ class TestParsing < TestCase
293
318
 
294
319
  time = parse_now("03/18/2012 09:26 pm")
295
320
  assert_equal Time.local(2012, 3, 18, 21, 26), time
321
+
322
+ time = parse_now("30.07.2013 16:34:22")
323
+ assert_equal Time.local(2013, 7, 30, 16, 34, 22), time
324
+
325
+ time = parse_now("09.08.2013")
326
+ assert_equal Time.local(2013, 8, 9, 12), time
327
+
328
+ time = parse_now("30-07-2013 21:53:49")
329
+ assert_equal Time.local(2013, 7, 30, 21, 53, 49), time
296
330
  end
297
331
 
298
332
  def test_handle_sy_sm_sd
@@ -317,9 +351,18 @@ class TestParsing < TestCase
317
351
  time = parse_now("2006-08-20 15:30.30")
318
352
  assert_equal Time.local(2006, 8, 20, 15, 30, 30), time
319
353
 
354
+ time = parse_now("2006-08-20 15:30:30:000536")
355
+ assert_in_delta Time.local(2006, 8, 20, 15, 30, 30, 536), time, 0.000001
356
+
320
357
  time = parse_now("1902-08-20")
321
358
  assert_equal Time.local(1902, 8, 20, 12, 0, 0), time
322
359
 
360
+ time = parse_now("2013.07.30 11:45:23")
361
+ assert_equal Time.local(2013, 7, 30, 11, 45, 23), time
362
+
363
+ time = parse_now("2013.08.09")
364
+ assert_equal Time.local(2013, 8, 9, 12, 0, 0), time
365
+
323
366
  # exif date time original
324
367
  time = parse_now("2012:05:25 22:06:50")
325
368
  assert_equal Time.local(2012, 5, 25, 22, 6, 50), time
@@ -367,8 +410,8 @@ class TestParsing < TestCase
367
410
  time = parse_now("2012-06")
368
411
  assert_equal Time.local(2012, 06, 16), time
369
412
 
370
- time = parse_now("2013/11")
371
- assert_equal Time.local(2013, 11, 16), time
413
+ time = parse_now("2013/12")
414
+ assert_equal Time.local(2013, 12, 16, 12, 0), time
372
415
  end
373
416
 
374
417
  def test_handle_r
@@ -383,6 +426,21 @@ class TestParsing < TestCase
383
426
 
384
427
  time = parse_now("01:00:00 PM")
385
428
  assert_equal Time.local(2006, 8, 16, 13), time
429
+
430
+ time = parse_now("today at 02:00:00", :hours24 => false)
431
+ assert_equal Time.local(2006, 8, 16, 14), time
432
+
433
+ time = parse_now("today at 02:00:00 AM", :hours24 => false)
434
+ assert_equal Time.local(2006, 8, 16, 2), time
435
+
436
+ time = parse_now("today at 3:00:00", :hours24 => true)
437
+ assert_equal Time.local(2006, 8, 16, 3), time
438
+
439
+ time = parse_now("today at 03:00:00", :hours24 => true)
440
+ assert_equal Time.local(2006, 8, 16, 3), time
441
+
442
+ time = parse_now("tomorrow at 4a.m.")
443
+ assert_equal Time.local(2006, 8, 17, 4), time
386
444
  end
387
445
 
388
446
  def test_handle_r_g_r
@@ -1149,6 +1207,20 @@ class TestParsing < TestCase
1149
1207
  assert_equal Time.local(2006, 12, 31, 12), time
1150
1208
  end
1151
1209
 
1210
+ def test_handle_rdn_rmn_od_sy
1211
+ time = parse_now("Thu Aug 10th 2005")
1212
+ assert_equal Time.local(2005, 8, 10, 12), time
1213
+
1214
+ time = parse_now("Thursday July 31st 2005")
1215
+ assert_equal Time.local(2005, 7, 31, 12), time
1216
+
1217
+ time = parse_now("Thursday December 31st 2005")
1218
+ assert_equal Time.local(2005, 12, 31, 12), time
1219
+
1220
+ time = parse_now("Thursday December 30th 2005")
1221
+ assert_equal Time.local(2005, 12, 30, 12), time
1222
+ end
1223
+
1152
1224
  def test_normalizing_day_portions
1153
1225
  assert_equal pre_normalize("8:00 pm February 11"), pre_normalize("8:00 p.m. February 11")
1154
1226
  end
@@ -7,6 +7,12 @@ class TestRepeaterTime < TestCase
7
7
  @now = Time.local(2006, 8, 16, 14, 0, 0, 0)
8
8
  end
9
9
 
10
+ def test_generic
11
+ assert_raises(ArgumentError) do
12
+ Chronic::RepeaterTime.new('00:01:02:03:004')
13
+ end
14
+ end
15
+
10
16
  def test_next_future
11
17
  t = Chronic::RepeaterTime.new('4:00')
12
18
  t.start = @now
@@ -25,6 +31,12 @@ class TestRepeaterTime < TestCase
25
31
 
26
32
  assert_equal Time.local(2006, 8, 17, 4), t.next(:future).begin
27
33
  assert_equal Time.local(2006, 8, 18, 4), t.next(:future).begin
34
+
35
+ t = Chronic::RepeaterTime.new('0000')
36
+ t.start = @now
37
+
38
+ assert_equal Time.local(2006, 8, 17, 0), t.next(:future).begin
39
+ assert_equal Time.local(2006, 8, 18, 0), t.next(:future).begin
28
40
  end
29
41
 
30
42
  def test_next_past
@@ -39,6 +51,12 @@ class TestRepeaterTime < TestCase
39
51
 
40
52
  assert_equal Time.local(2006, 8, 16, 13), t.next(:past).begin
41
53
  assert_equal Time.local(2006, 8, 15, 13), t.next(:past).begin
54
+
55
+ t = Chronic::RepeaterTime.new('0:00.000')
56
+ t.start = @now
57
+
58
+ assert_equal Time.local(2006, 8, 16, 0), t.next(:past).begin
59
+ assert_equal Time.local(2006, 8, 15, 0), t.next(:past).begin
42
60
  end
43
61
 
44
62
  def test_type