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.
- checksums.yaml +4 -4
- data/.gitignore +4 -4
- data/HISTORY.md +21 -0
- data/README.md +8 -0
- data/Rakefile +28 -8
- data/chronic.gemspec +8 -5
- data/lib/chronic/date.rb +82 -0
- data/lib/chronic/handler.rb +34 -25
- data/lib/chronic/handlers.rb +22 -3
- data/lib/chronic/ordinal.rb +22 -20
- data/lib/chronic/parser.rb +31 -26
- data/lib/chronic/repeater.rb +18 -18
- data/lib/chronic/repeaters/repeater_day.rb +4 -3
- data/lib/chronic/repeaters/repeater_day_name.rb +5 -4
- data/lib/chronic/repeaters/repeater_day_portion.rb +4 -3
- data/lib/chronic/repeaters/repeater_fortnight.rb +4 -3
- data/lib/chronic/repeaters/repeater_hour.rb +4 -3
- data/lib/chronic/repeaters/repeater_minute.rb +4 -3
- data/lib/chronic/repeaters/repeater_month.rb +5 -4
- data/lib/chronic/repeaters/repeater_month_name.rb +4 -3
- data/lib/chronic/repeaters/repeater_season.rb +5 -3
- data/lib/chronic/repeaters/repeater_second.rb +4 -3
- data/lib/chronic/repeaters/repeater_time.rb +35 -25
- data/lib/chronic/repeaters/repeater_week.rb +4 -3
- data/lib/chronic/repeaters/repeater_weekday.rb +4 -3
- data/lib/chronic/repeaters/repeater_weekend.rb +4 -3
- data/lib/chronic/repeaters/repeater_year.rb +5 -4
- data/lib/chronic/scalar.rb +34 -69
- data/lib/chronic/separator.rb +107 -8
- data/lib/chronic/sign.rb +49 -0
- data/lib/chronic/tag.rb +5 -4
- data/lib/chronic/time.rb +40 -0
- data/lib/chronic.rb +16 -10
- data/test/helper.rb +2 -2
- data/test/test_chronic.rb +55 -4
- data/test/test_handler.rb +20 -0
- data/test/test_parsing.rb +75 -3
- data/test/test_repeater_time.rb +18 -0
- metadata +37 -5
data/lib/chronic/separator.rb
CHANGED
|
@@ -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 =
|
|
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
|
|
32
|
-
def self.
|
|
33
|
-
scan_for token,
|
|
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
|
-
|
|
36
|
-
|
|
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
|
|
135
|
+
class SeparatorDot < Separator #:nodoc:
|
|
80
136
|
def to_s
|
|
81
|
-
super << '-
|
|
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
|
data/lib/chronic/sign.rb
ADDED
|
@@ -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
|
data/lib/chronic/time.rb
ADDED
|
@@ -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.
|
|
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
|
-
|
|
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
|
-
|
|
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
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, :
|
|
62
|
-
Chronic::Handler.new([:scalar_month, :
|
|
63
|
-
Chronic::Handler.new([:scalar_day, :
|
|
64
|
-
Chronic::Handler.new([:scalar_day, :
|
|
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
|
-
|
|
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/
|
|
371
|
-
assert_equal Time.local(2013,
|
|
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
|
data/test/test_repeater_time.rb
CHANGED
|
@@ -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
|