chronic 0.9.0 → 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 (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -4
  3. data/.travis.yml +8 -0
  4. data/HISTORY.md +27 -0
  5. data/README.md +8 -0
  6. data/Rakefile +28 -8
  7. data/chronic.gemspec +8 -5
  8. data/lib/chronic/date.rb +82 -0
  9. data/lib/chronic/handler.rb +34 -25
  10. data/lib/chronic/handlers.rb +37 -9
  11. data/lib/chronic/numerizer.rb +11 -2
  12. data/lib/chronic/ordinal.rb +22 -20
  13. data/lib/chronic/parser.rb +33 -25
  14. data/lib/chronic/repeater.rb +18 -18
  15. data/lib/chronic/repeaters/repeater_day.rb +4 -3
  16. data/lib/chronic/repeaters/repeater_day_name.rb +5 -4
  17. data/lib/chronic/repeaters/repeater_day_portion.rb +4 -3
  18. data/lib/chronic/repeaters/repeater_fortnight.rb +4 -3
  19. data/lib/chronic/repeaters/repeater_hour.rb +4 -3
  20. data/lib/chronic/repeaters/repeater_minute.rb +4 -3
  21. data/lib/chronic/repeaters/repeater_month.rb +5 -4
  22. data/lib/chronic/repeaters/repeater_month_name.rb +4 -3
  23. data/lib/chronic/repeaters/repeater_season.rb +5 -3
  24. data/lib/chronic/repeaters/repeater_second.rb +4 -3
  25. data/lib/chronic/repeaters/repeater_time.rb +35 -25
  26. data/lib/chronic/repeaters/repeater_week.rb +4 -3
  27. data/lib/chronic/repeaters/repeater_weekday.rb +4 -3
  28. data/lib/chronic/repeaters/repeater_weekend.rb +4 -3
  29. data/lib/chronic/repeaters/repeater_year.rb +5 -4
  30. data/lib/chronic/scalar.rb +34 -69
  31. data/lib/chronic/separator.rb +107 -8
  32. data/lib/chronic/sign.rb +49 -0
  33. data/lib/chronic/span.rb +2 -2
  34. data/lib/chronic/tag.rb +5 -4
  35. data/lib/chronic/time.rb +40 -0
  36. data/lib/chronic/token.rb +4 -0
  37. data/lib/chronic.rb +16 -11
  38. data/test/helper.rb +2 -2
  39. data/test/test_chronic.rb +55 -4
  40. data/test/test_handler.rb +20 -0
  41. data/test/test_numerizer.rb +14 -0
  42. data/test/test_parsing.rb +91 -1
  43. data/test/test_repeater_time.rb +18 -0
  44. data/test/test_span.rb +1 -1
  45. metadata +46 -20
@@ -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/span.rb CHANGED
@@ -25,7 +25,7 @@ module Chronic
25
25
  '(' << self.begin.to_s << '..' << self.end.to_s << ')'
26
26
  end
27
27
 
28
- alias :cover? :include? unless RUBY_VERSION =~ /^1.9/
28
+ alias :cover? :include? if RUBY_VERSION =~ /^1.8/
29
29
 
30
30
  end
31
- end
31
+ 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/token.rb CHANGED
@@ -43,5 +43,9 @@ module Chronic
43
43
  def to_s
44
44
  @word << '(' << @tags.join(', ') << ') '
45
45
  end
46
+
47
+ def inspect
48
+ to_s
49
+ end
46
50
  end
47
51
  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'
@@ -13,8 +15,8 @@ require 'chronic/grabber'
13
15
  require 'chronic/pointer'
14
16
  require 'chronic/scalar'
15
17
  require 'chronic/ordinal'
16
- require 'chronic/ordinal'
17
18
  require 'chronic/separator'
19
+ require 'chronic/sign'
18
20
  require 'chronic/time_zone'
19
21
  require 'chronic/numerizer'
20
22
  require 'chronic/season'
@@ -51,7 +53,7 @@ require 'chronic/repeaters/repeater_time'
51
53
  # Chronic.parse('monday', :context => :past)
52
54
  # #=> Mon Aug 21 12:00:00 PDT 2006
53
55
  module Chronic
54
- VERSION = "0.9.0"
56
+ VERSION = "0.10.2"
55
57
 
56
58
  class << self
57
59
 
@@ -73,7 +75,7 @@ module Chronic
73
75
  end
74
76
 
75
77
  self.debug = false
76
- self.time_class = Time
78
+ self.time_class = ::Time
77
79
 
78
80
 
79
81
  # Parses a string containing a natural language date or time.
@@ -99,7 +101,7 @@ module Chronic
99
101
  # second - Integer second.
100
102
  #
101
103
  # Returns a new Time object constructed from these params.
102
- 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)
103
105
  if second >= 60
104
106
  minute += second / 60
105
107
  second = second % 60
@@ -118,11 +120,8 @@ module Chronic
118
120
  # determine if there is a day overflow. this is complicated by our crappy calendar
119
121
  # system (non-constant number of days per month)
120
122
  day <= 56 || raise("day must be no more than 56 (makes month resolution easier)")
121
- if day > 28
122
- # no month ever has fewer than 28 days, so only do this if necessary
123
- leap_year_month_days = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
124
- common_year_month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
125
- 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]
126
125
  if day > days_this_month
127
126
  month += day / days_this_month
128
127
  day = day % days_this_month
@@ -138,8 +137,14 @@ module Chronic
138
137
  month = month % 12
139
138
  end
140
139
  end
141
-
142
- 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
143
148
  end
144
149
 
145
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
@@ -57,6 +57,20 @@ class ParseNumbersTest < TestCase
57
57
  'second day' => '2nd day',
58
58
  'second of may' => '2nd of may',
59
59
  'fifth' => '5th',
60
+ 'twelfth' => '12th',
61
+ 'twentieth' => '20th',
62
+ 'thirtieth' => '30th',
63
+ 'fourtieth' => '40th',
64
+ 'fiftieth' => '50th',
65
+ 'sixtieth' => '60th',
66
+ 'seventieth' => '70th',
67
+ 'eightieth' => '80th',
68
+ 'ninetieth' => '90th',
69
+ 'hundredth' => '100th',
70
+ 'thousandth' => '1000th',
71
+ 'millionth' => '1000000th',
72
+ 'billionth' => '1000000000th',
73
+ 'trillionth' => '1000000000000th',
60
74
  'twenty third' => '23rd',
61
75
  'first day month two' => '1st day month 2'
62
76
  }.each do |key, val|
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
@@ -363,12 +406,41 @@ class TestParsing < TestCase
363
406
  # assert_equal nil, time
364
407
  # end
365
408
 
409
+ def test_handle_sy_sm
410
+ time = parse_now("2012-06")
411
+ assert_equal Time.local(2012, 06, 16), time
412
+
413
+ time = parse_now("2013/12")
414
+ assert_equal Time.local(2013, 12, 16, 12, 0), time
415
+ end
416
+
366
417
  def test_handle_r
367
418
  time = parse_now("9am on Saturday")
368
419
  assert_equal Time.local(2006, 8, 19, 9), time
369
420
 
370
421
  time = parse_now("on Tuesday")
371
422
  assert_equal Time.local(2006, 8, 22, 12), time
423
+
424
+ time = parse_now("1:00:00 PM")
425
+ assert_equal Time.local(2006, 8, 16, 13), time
426
+
427
+ time = parse_now("01:00:00 PM")
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
372
444
  end
373
445
 
374
446
  def test_handle_r_g_r
@@ -1135,6 +1207,24 @@ class TestParsing < TestCase
1135
1207
  assert_equal Time.local(2006, 12, 31, 12), time
1136
1208
  end
1137
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
+
1224
+ def test_normalizing_day_portions
1225
+ assert_equal pre_normalize("8:00 pm February 11"), pre_normalize("8:00 p.m. February 11")
1226
+ end
1227
+
1138
1228
  private
1139
1229
  def parse_now(string, options={})
1140
1230
  Chronic.parse(string, {:now => TIME_2006_08_16_14_00_00 }.merge(options))