chronic 0.9.1 → 0.10.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.
- checksums.yaml +4 -4
- data/.gitignore +4 -4
- data/HISTORY.md +12 -0
- data/README.md +8 -0
- data/Rakefile +28 -8
- data/chronic.gemspec +7 -5
- data/lib/chronic.rb +10 -10
- 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/test/helper.rb +2 -2
- data/test/test_chronic.rb +4 -4
- data/test/test_handler.rb +20 -0
- data/test/test_parsing.rb +72 -3
- data/test/test_repeater_time.rb +18 -0
- metadata +24 -6
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/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]
|
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
|
@@ -166,6 +182,9 @@ class TestParsing < TestCase
|
|
166
182
|
|
167
183
|
time = parse_now("2011-07-03 21:11:35 UTC")
|
168
184
|
assert_equal 1309727495, time.to_i
|
185
|
+
|
186
|
+
time = parse_now("2011-07-03 21:11:35.362 UTC")
|
187
|
+
assert_in_delta 1309727495.362, time.to_f, 0.001
|
169
188
|
end
|
170
189
|
|
171
190
|
def test_handle_rmn_sd_sy
|
@@ -282,6 +301,9 @@ class TestParsing < TestCase
|
|
282
301
|
# month day overflows
|
283
302
|
time = parse_now("30/2/2000")
|
284
303
|
assert_nil time
|
304
|
+
|
305
|
+
time = parse_now("2013-03-12 17:00", :context => :past)
|
306
|
+
assert_equal Time.local(2013, 3, 12, 17, 0, 0), time
|
285
307
|
end
|
286
308
|
|
287
309
|
def test_handle_sd_sm_sy
|
@@ -293,6 +315,15 @@ class TestParsing < TestCase
|
|
293
315
|
|
294
316
|
time = parse_now("03/18/2012 09:26 pm")
|
295
317
|
assert_equal Time.local(2012, 3, 18, 21, 26), time
|
318
|
+
|
319
|
+
time = parse_now("30.07.2013 16:34:22")
|
320
|
+
assert_equal Time.local(2013, 7, 30, 16, 34, 22), time
|
321
|
+
|
322
|
+
time = parse_now("09.08.2013")
|
323
|
+
assert_equal Time.local(2013, 8, 9, 12), time
|
324
|
+
|
325
|
+
time = parse_now("30-07-2013 21:53:49")
|
326
|
+
assert_equal Time.local(2013, 7, 30, 21, 53, 49), time
|
296
327
|
end
|
297
328
|
|
298
329
|
def test_handle_sy_sm_sd
|
@@ -317,9 +348,18 @@ class TestParsing < TestCase
|
|
317
348
|
time = parse_now("2006-08-20 15:30.30")
|
318
349
|
assert_equal Time.local(2006, 8, 20, 15, 30, 30), time
|
319
350
|
|
351
|
+
time = parse_now("2006-08-20 15:30:30:000536")
|
352
|
+
assert_in_delta Time.local(2006, 8, 20, 15, 30, 30, 536), time, 0.000001
|
353
|
+
|
320
354
|
time = parse_now("1902-08-20")
|
321
355
|
assert_equal Time.local(1902, 8, 20, 12, 0, 0), time
|
322
356
|
|
357
|
+
time = parse_now("2013.07.30 11:45:23")
|
358
|
+
assert_equal Time.local(2013, 7, 30, 11, 45, 23), time
|
359
|
+
|
360
|
+
time = parse_now("2013.08.09")
|
361
|
+
assert_equal Time.local(2013, 8, 9, 12, 0, 0), time
|
362
|
+
|
323
363
|
# exif date time original
|
324
364
|
time = parse_now("2012:05:25 22:06:50")
|
325
365
|
assert_equal Time.local(2012, 5, 25, 22, 6, 50), time
|
@@ -367,8 +407,8 @@ class TestParsing < TestCase
|
|
367
407
|
time = parse_now("2012-06")
|
368
408
|
assert_equal Time.local(2012, 06, 16), time
|
369
409
|
|
370
|
-
time = parse_now("2013/
|
371
|
-
assert_equal Time.local(2013,
|
410
|
+
time = parse_now("2013/12")
|
411
|
+
assert_equal Time.local(2013, 12, 16, 12, 0), time
|
372
412
|
end
|
373
413
|
|
374
414
|
def test_handle_r
|
@@ -383,6 +423,21 @@ class TestParsing < TestCase
|
|
383
423
|
|
384
424
|
time = parse_now("01:00:00 PM")
|
385
425
|
assert_equal Time.local(2006, 8, 16, 13), time
|
426
|
+
|
427
|
+
time = parse_now("today at 02:00:00", :hours24 => false)
|
428
|
+
assert_equal Time.local(2006, 8, 16, 14), time
|
429
|
+
|
430
|
+
time = parse_now("today at 02:00:00 AM", :hours24 => false)
|
431
|
+
assert_equal Time.local(2006, 8, 16, 2), time
|
432
|
+
|
433
|
+
time = parse_now("today at 3:00:00", :hours24 => true)
|
434
|
+
assert_equal Time.local(2006, 8, 16, 3), time
|
435
|
+
|
436
|
+
time = parse_now("today at 03:00:00", :hours24 => true)
|
437
|
+
assert_equal Time.local(2006, 8, 16, 3), time
|
438
|
+
|
439
|
+
time = parse_now("tomorrow at 4a.m.")
|
440
|
+
assert_equal Time.local(2006, 8, 17, 4), time
|
386
441
|
end
|
387
442
|
|
388
443
|
def test_handle_r_g_r
|
@@ -1149,6 +1204,20 @@ class TestParsing < TestCase
|
|
1149
1204
|
assert_equal Time.local(2006, 12, 31, 12), time
|
1150
1205
|
end
|
1151
1206
|
|
1207
|
+
def test_handle_rdn_rmn_od_sy
|
1208
|
+
time = parse_now("Thu Aug 10th 2005")
|
1209
|
+
assert_equal Time.local(2005, 8, 10, 12), time
|
1210
|
+
|
1211
|
+
time = parse_now("Thursday July 31st 2005")
|
1212
|
+
assert_equal Time.local(2005, 7, 31, 12), time
|
1213
|
+
|
1214
|
+
time = parse_now("Thursday December 31st 2005")
|
1215
|
+
assert_equal Time.local(2005, 12, 31, 12), time
|
1216
|
+
|
1217
|
+
time = parse_now("Thursday December 30th 2005")
|
1218
|
+
assert_equal Time.local(2005, 12, 30, 12), time
|
1219
|
+
end
|
1220
|
+
|
1152
1221
|
def test_normalizing_day_portions
|
1153
1222
|
assert_equal pre_normalize("8:00 pm February 11"), pre_normalize("8:00 p.m. February 11")
|
1154
1223
|
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
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: chronic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tom Preston-Werner
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-08-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -26,7 +26,7 @@ dependencies:
|
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '0'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
|
-
name:
|
29
|
+
name: simplecov
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
32
|
- - '>='
|
@@ -39,10 +39,24 @@ dependencies:
|
|
39
39
|
- - '>='
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: minitest
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ~>
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '5.0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ~>
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '5.0'
|
42
56
|
description: Chronic is a natural language date/time parser written in pure Ruby.
|
43
57
|
email:
|
44
58
|
- tom@mojombo.com
|
45
|
-
-
|
59
|
+
- ljjarvis@gmail.com
|
46
60
|
executables: []
|
47
61
|
extensions: []
|
48
62
|
extra_rdoc_files:
|
@@ -58,6 +72,7 @@ files:
|
|
58
72
|
- Rakefile
|
59
73
|
- chronic.gemspec
|
60
74
|
- lib/chronic.rb
|
75
|
+
- lib/chronic/date.rb
|
61
76
|
- lib/chronic/grabber.rb
|
62
77
|
- lib/chronic/handler.rb
|
63
78
|
- lib/chronic/handlers.rb
|
@@ -86,8 +101,10 @@ files:
|
|
86
101
|
- lib/chronic/scalar.rb
|
87
102
|
- lib/chronic/season.rb
|
88
103
|
- lib/chronic/separator.rb
|
104
|
+
- lib/chronic/sign.rb
|
89
105
|
- lib/chronic/span.rb
|
90
106
|
- lib/chronic/tag.rb
|
107
|
+
- lib/chronic/time.rb
|
91
108
|
- lib/chronic/time_zone.rb
|
92
109
|
- lib/chronic/token.rb
|
93
110
|
- test/helper.rb
|
@@ -113,7 +130,8 @@ files:
|
|
113
130
|
- test/test_span.rb
|
114
131
|
- test/test_token.rb
|
115
132
|
homepage: http://github.com/mojombo/chronic
|
116
|
-
licenses:
|
133
|
+
licenses:
|
134
|
+
- MIT
|
117
135
|
metadata: {}
|
118
136
|
post_install_message:
|
119
137
|
rdoc_options:
|
@@ -132,7 +150,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
132
150
|
version: '0'
|
133
151
|
requirements: []
|
134
152
|
rubyforge_project: chronic
|
135
|
-
rubygems_version: 2.0.
|
153
|
+
rubygems_version: 2.0.2
|
136
154
|
signing_key:
|
137
155
|
specification_version: 4
|
138
156
|
summary: Natural language date/time parsing.
|