chronic 0.6.3 → 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 +7 -0
- data/.gitignore +4 -3
- data/.travis.yml +8 -0
- data/HISTORY.md +76 -0
- data/README.md +47 -42
- data/Rakefile +30 -12
- data/chronic.gemspec +10 -4
- data/lib/chronic/date.rb +82 -0
- data/lib/chronic/grabber.rb +9 -7
- data/lib/chronic/handler.rb +47 -40
- data/lib/chronic/handlers.rb +240 -28
- data/lib/chronic/numerizer.rb +11 -2
- data/lib/chronic/ordinal.rb +28 -23
- data/lib/chronic/parser.rb +268 -0
- data/lib/chronic/pointer.rb +9 -7
- data/lib/chronic/repeater.rb +58 -48
- 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 +33 -18
- 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 +6 -5
- data/lib/chronic/scalar.rb +40 -68
- data/lib/chronic/season.rb +1 -12
- data/lib/chronic/separator.rb +142 -23
- data/lib/chronic/sign.rb +49 -0
- data/lib/chronic/span.rb +2 -2
- data/lib/chronic/tag.rb +10 -15
- data/lib/chronic/time.rb +40 -0
- data/lib/chronic/time_zone.rb +9 -7
- data/lib/chronic/token.rb +16 -10
- data/lib/chronic.rb +109 -70
- data/test/helper.rb +8 -2
- data/test/{test_Chronic.rb → test_chronic.rb} +69 -34
- data/test/{test_DaylightSavings.rb → test_daylight_savings.rb} +1 -1
- data/test/{test_Handler.rb → test_handler.rb} +38 -14
- data/test/{test_MiniDate.rb → test_mini_date.rb} +9 -9
- data/test/{test_Numerizer.rb → test_numerizer.rb} +16 -2
- data/test/test_parsing.rb +392 -22
- data/test/{test_RepeaterDayName.rb → test_repeater_day_name.rb} +1 -1
- data/test/test_repeater_day_portion.rb +254 -0
- data/test/{test_RepeaterFortnight.rb → test_repeater_fortnight.rb} +1 -1
- data/test/{test_RepeaterHour.rb → test_repeater_hour.rb} +1 -1
- data/test/{test_RepeaterMinute.rb → test_repeater_minute.rb} +1 -1
- data/test/{test_RepeaterMonth.rb → test_repeater_month.rb} +1 -1
- data/test/{test_RepeaterMonthName.rb → test_repeater_month_name.rb} +1 -1
- data/test/{test_RepeaterSeason.rb → test_repeater_season.rb} +1 -1
- data/test/{test_RepeaterTime.rb → test_repeater_time.rb} +19 -1
- data/test/{test_RepeaterWeek.rb → test_repeater_week.rb} +1 -1
- data/test/{test_RepeaterWeekday.rb → test_repeater_weekday.rb} +1 -1
- data/test/{test_RepeaterWeekend.rb → test_repeater_weekend.rb} +1 -1
- data/test/{test_RepeaterYear.rb → test_repeater_year.rb} +1 -1
- data/test/{test_Span.rb → test_span.rb} +2 -2
- data/test/{test_Token.rb → test_token.rb} +1 -1
- metadata +129 -87
- data/.gemtest +0 -0
- data/.yardopts +0 -3
- data/lib/chronic/chronic.rb +0 -323
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: d0bcdd2460eb86f8898212a9eca5e325c9aa5e61
|
|
4
|
+
data.tar.gz: d8abd19da58e28473139b8f137991cb0ee150c49
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 04bfd4f32ce8f8ef6585042425ea64cc95d07998287fb16fa45b8d0a06d5dd48f8b85e6fecbf00e69101e560f21527ef6539a8994a9457357d25c2c832c7b87f
|
|
7
|
+
data.tar.gz: 87db3c22292ed9d5a22ecc84e6b14dafb2ed1c7a4c2f829873593b86b37707999adfaf43a5e0d801daffaaec9c7b599beade2d366d108c60bf2e8989998ed4b4
|
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/HISTORY.md
CHANGED
|
@@ -1,3 +1,79 @@
|
|
|
1
|
+
# 0.10.2 / 2013-09-09
|
|
2
|
+
|
|
3
|
+
* Fix 1.8.7 support (due to be dropped in 0.11.0)
|
|
4
|
+
* Bugfix for times with negative zones
|
|
5
|
+
|
|
6
|
+
# 0.10.1 / 2013-08-27
|
|
7
|
+
|
|
8
|
+
* Support `ActiveSupport::TimeZone` (#209, #208)
|
|
9
|
+
|
|
10
|
+
# 0.10.0 / 2013-08-25
|
|
11
|
+
|
|
12
|
+
* Chronic will parse subseconds correctly
|
|
13
|
+
for all supported date/time formats (#195, #198 and #200)
|
|
14
|
+
* Support for date format: dd.mm.yyyy (#197)
|
|
15
|
+
* Option `:hours24` to parse as 24 hour clock (#201 and #202)
|
|
16
|
+
* `:guess` option allows to specify which part of Span to return.
|
|
17
|
+
(accepted values `false`,`true`,`:begin`, `:middle`, `:end`)
|
|
18
|
+
* Replace `rcov` with `SimpleCov` for coverage generation
|
|
19
|
+
* Add more tests
|
|
20
|
+
* Various changes in codebase (#202 and #206)
|
|
21
|
+
|
|
22
|
+
# 0.9.1 / 2013-02-25
|
|
23
|
+
|
|
24
|
+
* Ensure Chronic strips periods from day portions (#173)
|
|
25
|
+
* Properly numerize "twelfth", "twentieth" etc. (#172, James McKinney)
|
|
26
|
+
* Ensure Chronic is compatible with Ruby 2.0.0 (#165, Ravil Bayramgalin)
|
|
27
|
+
|
|
28
|
+
# 0.9.0 / 2012-12-21
|
|
29
|
+
|
|
30
|
+
* Implement Chronic::Parser class and create an instance of this class
|
|
31
|
+
instead of leaving all data in the class level of Chronic
|
|
32
|
+
* Various bug fixes
|
|
33
|
+
* Add support for excel date formats (#149, @jmondo)
|
|
34
|
+
* Added support for time expressions such as '10 till' or 'half
|
|
35
|
+
past two' (#146, @chicagogrooves)
|
|
36
|
+
* Add support for RepeaterDayName, RepeaterMonthName,
|
|
37
|
+
Ordinal/ScalarDay and Time (#153, @kareemk)
|
|
38
|
+
|
|
39
|
+
# 0.8.0 / 2012-09-16
|
|
40
|
+
|
|
41
|
+
* Support parsing "<ordinal> of this month" (#109)
|
|
42
|
+
* Support parsing ISO 8601 format (#115)
|
|
43
|
+
* Support parsing "on <day>" without a timestamp (#117)
|
|
44
|
+
* Fix time parsing regexp (#125)
|
|
45
|
+
* Support time when parsing dd-mm-yyy <time> (#126)
|
|
46
|
+
* Allow anchor handler to accept any separators (at, on) (#128)
|
|
47
|
+
* Support parsing EXIF date format (#112)
|
|
48
|
+
* Start using minitest for testing
|
|
49
|
+
* Ensure periods are interpreted as colons (#81).
|
|
50
|
+
* Support month/day and day/month parsing (#59).
|
|
51
|
+
* Support day(scalar)-month(name)-year(scalar) (#99).
|
|
52
|
+
* Handle text starting with 'a' or 'an' (#101, @steveburkett).
|
|
53
|
+
* Ensure post medium timestamps are correctly formatted (#89)
|
|
54
|
+
|
|
55
|
+
# 0.6.7 / 2012-01-31
|
|
56
|
+
|
|
57
|
+
* Handle day, month names with scalar day and year (Joe Fiorini)
|
|
58
|
+
* Ensure 31st parses correctly with day names (Joe Fiorini)
|
|
59
|
+
|
|
60
|
+
# 0.6.6 / 2011-11-23
|
|
61
|
+
|
|
62
|
+
* `Chronic.parse('thur')` no longer returns `nil` (@harold)
|
|
63
|
+
|
|
64
|
+
# 0.6.5 / 2011-11-04
|
|
65
|
+
|
|
66
|
+
* Fix bug when parsing ordinal repeaters (#73)
|
|
67
|
+
* Added handler support for day_name month_name (@imme5150)
|
|
68
|
+
* Fix bug when parsing strings prefixed with PM
|
|
69
|
+
|
|
70
|
+
# 0.6.4 / 2011-08-08
|
|
71
|
+
|
|
72
|
+
* Fixed bug where 'noon' was parsed as 00:00 rather than 12:00
|
|
73
|
+
with :ambiguous_time_range => :none (Vladimir Chernis)
|
|
74
|
+
* Add support for handling '2009 May 22nd'
|
|
75
|
+
* Add the ability to handle scalar-day/repeater-month-name as well as ordinals
|
|
76
|
+
|
|
1
77
|
# 0.6.3 / 2011-08-01
|
|
2
78
|
|
|
3
79
|
* Ensure 'thu' is parsed as Thursday for 1.8.7 generic timestamp
|
data/README.md
CHANGED
|
@@ -1,56 +1,54 @@
|
|
|
1
1
|
Chronic
|
|
2
2
|
=======
|
|
3
3
|
|
|
4
|
-
## DESCRIPTION
|
|
5
|
-
|
|
6
4
|
Chronic is a natural language date/time parser written in pure Ruby. See below
|
|
7
5
|
for the wide variety of formats Chronic will parse.
|
|
8
6
|
|
|
7
|
+
## Installation
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
$ [sudo] gem install chronic
|
|
9
|
+
```
|
|
10
|
+
$ gem install chronic
|
|
11
|
+
```
|
|
15
12
|
|
|
16
|
-
|
|
13
|
+
## Usage
|
|
17
14
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
$ gem install chronic-<version>.gem
|
|
15
|
+
```ruby
|
|
16
|
+
require 'chronic'
|
|
21
17
|
|
|
18
|
+
Time.now #=> Sun Aug 27 23:18:25 PDT 2006
|
|
22
19
|
|
|
23
|
-
|
|
20
|
+
Chronic.parse('tomorrow')
|
|
21
|
+
#=> Mon Aug 28 12:00:00 PDT 2006
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
Chronic.parse('monday', :context => :past)
|
|
24
|
+
#=> Mon Aug 21 12:00:00 PDT 2006
|
|
27
25
|
|
|
28
|
-
|
|
26
|
+
Chronic.parse('this tuesday 5:00')
|
|
27
|
+
#=> Tue Aug 29 17:00:00 PDT 2006
|
|
29
28
|
|
|
30
|
-
|
|
29
|
+
Chronic.parse('this tuesday 5:00', :ambiguous_time_range => :none)
|
|
30
|
+
#=> Tue Aug 29 05:00:00 PDT 2006
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
Chronic.parse('may 27th', :now => Time.local(2000, 1, 1))
|
|
33
|
+
#=> Sat May 27 12:00:00 PDT 2000
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
Chronic.parse('may 27th', :guess => false)
|
|
36
|
+
#=> Sun May 27 00:00:00 PDT 2007..Mon May 28 00:00:00 PDT 2007
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
Chronic.parse('6/4/2012', :endian_precedence => :little)
|
|
39
|
+
#=> Fri Apr 06 00:00:00 PDT 2012
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
Chronic.parse('INVALID DATE')
|
|
42
|
+
#=> nil
|
|
43
|
+
```
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
Chronic.parse('may 27th', :guess => false)
|
|
48
|
-
#=> Sun May 27 00:00:00 PDT 2007..Mon May 28 00:00:00 PDT 2007
|
|
45
|
+
If the parser can find a date or time, either a Time or Chronic::Span
|
|
46
|
+
will be returned (depending on the value of `:guess`). If no
|
|
47
|
+
date or time can be found, `nil` will be returned.
|
|
49
48
|
|
|
50
49
|
See `Chronic.parse` for detailed usage instructions.
|
|
51
50
|
|
|
52
|
-
|
|
53
|
-
## EXAMPLES
|
|
51
|
+
## Examples
|
|
54
52
|
|
|
55
53
|
Chronic can parse a huge variety of date and time formats. Following is a
|
|
56
54
|
small sample of strings that will be properly parsed. Parsing is case
|
|
@@ -64,12 +62,17 @@ Simple
|
|
|
64
62
|
* friday 13:00
|
|
65
63
|
* mon 2:35
|
|
66
64
|
* 4pm
|
|
65
|
+
* 10 to 8
|
|
66
|
+
* 10 past 2
|
|
67
|
+
* half past 2
|
|
67
68
|
* 6 in the morning
|
|
68
69
|
* friday 1pm
|
|
69
70
|
* sat 7 in the evening
|
|
70
71
|
* yesterday
|
|
71
72
|
* today
|
|
72
73
|
* tomorrow
|
|
74
|
+
* last week
|
|
75
|
+
* next week
|
|
73
76
|
* this tuesday
|
|
74
77
|
* next month
|
|
75
78
|
* last winter
|
|
@@ -86,6 +89,7 @@ Simple
|
|
|
86
89
|
Complex
|
|
87
90
|
|
|
88
91
|
* 3 years ago
|
|
92
|
+
* a year ago
|
|
89
93
|
* 5 months before now
|
|
90
94
|
* 7 hours ago
|
|
91
95
|
* 7 days from now
|
|
@@ -131,10 +135,11 @@ Specific Times (many of the above with an added time)
|
|
|
131
135
|
* January 5 at 7pm
|
|
132
136
|
* 22nd of june at 8am
|
|
133
137
|
* 1979-05-27 05:00:00
|
|
138
|
+
* 03/01/2012 07:25:09.234567
|
|
134
139
|
* etc
|
|
135
140
|
|
|
136
141
|
|
|
137
|
-
##
|
|
142
|
+
## Time Zones
|
|
138
143
|
|
|
139
144
|
Chronic allows you to set which Time class to use when constructing times. By
|
|
140
145
|
default, the built in Ruby time class creates times in your system's local
|
|
@@ -142,13 +147,14 @@ time zone. You can set this to something like ActiveSupport's
|
|
|
142
147
|
[TimeZone](http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html)
|
|
143
148
|
class to get full time zone support.
|
|
144
149
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
+
```
|
|
151
|
+
>> Time.zone = "UTC"
|
|
152
|
+
>> Chronic.time_class = Time.zone
|
|
153
|
+
>> Chronic.parse("June 15 2006 at 5:45 AM")
|
|
154
|
+
=> Thu, 15 Jun 2006 05:45:00 UTC +00:00
|
|
155
|
+
```
|
|
150
156
|
|
|
151
|
-
##
|
|
157
|
+
## Limitations
|
|
152
158
|
|
|
153
159
|
Chronic uses Ruby's built in Time class for all time storage and computation.
|
|
154
160
|
Because of this, only times that the Time class can handle will be properly
|
|
@@ -156,20 +162,19 @@ parsed. Parsing for times outside of this range will simply return nil.
|
|
|
156
162
|
Support for a wider range of times is planned for a future release.
|
|
157
163
|
|
|
158
164
|
|
|
159
|
-
##
|
|
165
|
+
## Contribute
|
|
160
166
|
|
|
161
167
|
If you'd like to hack on Chronic, start by forking the repo on GitHub:
|
|
162
168
|
|
|
163
169
|
https://github.com/mojombo/chronic
|
|
164
170
|
|
|
165
|
-
|
|
166
|
-
your changes merged back into core is as follows:
|
|
171
|
+
The best way to get your changes merged back into core is as follows:
|
|
167
172
|
|
|
168
173
|
1. Clone down your fork
|
|
169
174
|
1. Create a thoughtfully named topic branch to contain your change
|
|
170
175
|
1. Hack away
|
|
171
176
|
1. Add tests and make sure everything still passes by running `rake`
|
|
172
|
-
1. Ensure your tests pass in multiple timezones
|
|
177
|
+
1. Ensure your tests pass in multiple timezones. ie `TZ=utc rake` `TZ=BST rake`
|
|
173
178
|
1. If you are adding new functionality, document it in the README
|
|
174
179
|
1. Do not change the version number, we will do that on our end
|
|
175
180
|
1. If necessary, rebase your commits into logical chunks, without errors
|
data/Rakefile
CHANGED
|
@@ -5,19 +5,37 @@ def version
|
|
|
5
5
|
contents[/VERSION = "([^"]+)"/, 1]
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
test.
|
|
11
|
-
test.pattern = 'test/**/test_*.rb'
|
|
12
|
-
test.verbose = true
|
|
8
|
+
def do_test
|
|
9
|
+
$:.unshift './test'
|
|
10
|
+
Dir.glob('test/test_*.rb').each { |t| require File.basename(t) }
|
|
13
11
|
end
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
def open_command
|
|
14
|
+
case RUBY_PLATFORM
|
|
15
|
+
when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
|
|
16
|
+
'start'
|
|
17
|
+
when /darwin|mac os/
|
|
18
|
+
'open'
|
|
19
|
+
else
|
|
20
|
+
'xdg-open'
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
task :test do
|
|
25
|
+
do_test
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
desc "Generate SimpleCov test coverage and open in your browser"
|
|
16
29
|
task :coverage do
|
|
17
|
-
require '
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
30
|
+
require 'simplecov'
|
|
31
|
+
FileUtils.rm_rf("./coverage")
|
|
32
|
+
SimpleCov.command_name 'Unit Tests'
|
|
33
|
+
SimpleCov.at_exit do
|
|
34
|
+
SimpleCov.result.format!
|
|
35
|
+
sh "#{open_command} #{SimpleCov.coverage_path}/index.html"
|
|
36
|
+
end
|
|
37
|
+
SimpleCov.start
|
|
38
|
+
do_test
|
|
21
39
|
end
|
|
22
40
|
|
|
23
41
|
desc "Open an irb session preloaded with this library"
|
|
@@ -40,9 +58,9 @@ end
|
|
|
40
58
|
|
|
41
59
|
desc "Build a gem from the gemspec"
|
|
42
60
|
task :build do
|
|
43
|
-
|
|
61
|
+
FileUtils.mkdir_p "pkg"
|
|
44
62
|
sh "gem build chronic.gemspec"
|
|
45
|
-
|
|
63
|
+
FileUtils.mv("./chronic-#{version}.gem", "pkg")
|
|
46
64
|
end
|
|
47
65
|
|
|
48
66
|
task :default => :test
|
data/chronic.gemspec
CHANGED
|
@@ -8,10 +8,16 @@ Gem::Specification.new do |s|
|
|
|
8
8
|
s.summary = 'Natural language date/time parsing.'
|
|
9
9
|
s.description = 'Chronic is a natural language date/time parser written in pure Ruby.'
|
|
10
10
|
s.authors = ['Tom Preston-Werner', 'Lee Jarvis']
|
|
11
|
-
s.email = ['tom@mojombo.com', '
|
|
11
|
+
s.email = ['tom@mojombo.com', 'ljjarvis@gmail.com']
|
|
12
12
|
s.homepage = 'http://github.com/mojombo/chronic'
|
|
13
|
+
s.license = 'MIT'
|
|
13
14
|
s.rdoc_options = ['--charset=UTF-8']
|
|
14
15
|
s.extra_rdoc_files = %w[README.md HISTORY.md LICENSE]
|
|
15
|
-
s.files = `git ls-files`.split(
|
|
16
|
-
s.test_files = `git ls-files -- test`.split(
|
|
17
|
-
|
|
16
|
+
s.files = `git ls-files`.split($/)
|
|
17
|
+
s.test_files = `git ls-files -- test`.split($/)
|
|
18
|
+
|
|
19
|
+
s.add_development_dependency 'rake'
|
|
20
|
+
s.add_development_dependency 'simplecov'
|
|
21
|
+
s.add_development_dependency 'minitest', '~> 5.0'
|
|
22
|
+
s.add_development_dependency 'activesupport'
|
|
23
|
+
end
|
data/lib/chronic/date.rb
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
module Chronic
|
|
2
|
+
class Date
|
|
3
|
+
YEAR_MONTHS = 12
|
|
4
|
+
MONTH_DAYS = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
|
5
|
+
MONTH_DAYS_LEAP = [nil, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
|
6
|
+
YEAR_SECONDS = 31_536_000 # 365 * 24 * 60 * 60
|
|
7
|
+
SEASON_SECONDS = 7_862_400 # 91 * 24 * 60 * 60
|
|
8
|
+
MONTH_SECONDS = 2_592_000 # 30 * 24 * 60 * 60
|
|
9
|
+
FORTNIGHT_SECONDS = 1_209_600 # 14 * 24 * 60 * 60
|
|
10
|
+
WEEK_SECONDS = 604_800 # 7 * 24 * 60 * 60
|
|
11
|
+
WEEKEND_SECONDS = 172_800 # 2 * 24 * 60 * 60
|
|
12
|
+
DAY_SECONDS = 86_400 # 24 * 60 * 60
|
|
13
|
+
MONTHS = {
|
|
14
|
+
:january => 1,
|
|
15
|
+
:february => 2,
|
|
16
|
+
:march => 3,
|
|
17
|
+
:april => 4,
|
|
18
|
+
:may => 5,
|
|
19
|
+
:june => 6,
|
|
20
|
+
:july => 7,
|
|
21
|
+
:august => 8,
|
|
22
|
+
:september => 9,
|
|
23
|
+
:october => 10,
|
|
24
|
+
:november => 11,
|
|
25
|
+
:december => 12
|
|
26
|
+
}
|
|
27
|
+
DAYS = {
|
|
28
|
+
:sunday => 0,
|
|
29
|
+
:monday => 1,
|
|
30
|
+
:tuesday => 2,
|
|
31
|
+
:wednesday => 3,
|
|
32
|
+
:thursday => 4,
|
|
33
|
+
:friday => 5,
|
|
34
|
+
:saturday => 6
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# Checks if given number could be day
|
|
38
|
+
def self.could_be_day?(day)
|
|
39
|
+
day >= 1 && day <= 31
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Checks if given number could be month
|
|
43
|
+
def self.could_be_month?(month)
|
|
44
|
+
month >= 1 && month <= 12
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Checks if given number could be year
|
|
48
|
+
def self.could_be_year?(year)
|
|
49
|
+
year >= 1 && year <= 9999
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Build a year from a 2 digit suffix.
|
|
53
|
+
#
|
|
54
|
+
# year - The two digit Integer year to build from.
|
|
55
|
+
# bias - The Integer amount of future years to bias.
|
|
56
|
+
#
|
|
57
|
+
# Examples:
|
|
58
|
+
#
|
|
59
|
+
# make_year(96, 50) #=> 1996
|
|
60
|
+
# make_year(79, 20) #=> 2079
|
|
61
|
+
# make_year(00, 50) #=> 2000
|
|
62
|
+
#
|
|
63
|
+
# Returns The Integer 4 digit year.
|
|
64
|
+
def self.make_year(year, bias)
|
|
65
|
+
return year if year.to_s.size > 2
|
|
66
|
+
start_year = Chronic.time_class.now.year - bias
|
|
67
|
+
century = (start_year / 100) * 100
|
|
68
|
+
full_year = century + year
|
|
69
|
+
full_year += 100 if full_year < start_year
|
|
70
|
+
full_year
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def self.month_overflow?(year, month, day)
|
|
74
|
+
if ::Date.leap?(year)
|
|
75
|
+
day > Date::MONTH_DAYS_LEAP[month]
|
|
76
|
+
else
|
|
77
|
+
day > Date::MONTH_DAYS[month]
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
end
|
|
82
|
+
end
|
data/lib/chronic/grabber.rb
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
module Chronic
|
|
2
2
|
class Grabber < Tag
|
|
3
3
|
|
|
4
|
-
# Scan an Array of
|
|
5
|
-
# each token
|
|
4
|
+
# Scan an Array of Tokens and apply any necessary Grabber tags to
|
|
5
|
+
# each token.
|
|
6
6
|
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
7
|
+
# tokens - An Array of Token objects to scan.
|
|
8
|
+
# options - The Hash of options specified in Chronic::parse.
|
|
9
|
+
#
|
|
10
|
+
# Returns an Array of Token objects.
|
|
10
11
|
def self.scan(tokens, options)
|
|
11
12
|
tokens.each do |token|
|
|
12
13
|
if t = scan_for_all(token) then token.tag(t); next end
|
|
13
14
|
end
|
|
14
15
|
end
|
|
15
16
|
|
|
16
|
-
#
|
|
17
|
-
#
|
|
17
|
+
# token - The Token object to scan.
|
|
18
|
+
#
|
|
19
|
+
# Returns a new Grabber object.
|
|
18
20
|
def self.scan_for_all(token)
|
|
19
21
|
scan_for token, self,
|
|
20
22
|
{
|
data/lib/chronic/handler.rb
CHANGED
|
@@ -1,77 +1,84 @@
|
|
|
1
1
|
module Chronic
|
|
2
2
|
class Handler
|
|
3
3
|
|
|
4
|
-
# @return [Array] A list of patterns
|
|
5
4
|
attr_reader :pattern
|
|
6
5
|
|
|
7
|
-
# @return [Symbol] The method which handles this list of patterns.
|
|
8
|
-
# This method should exist inside the {Handlers} module
|
|
9
6
|
attr_reader :handler_method
|
|
10
7
|
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
8
|
+
# pattern - An Array of patterns to match tokens against.
|
|
9
|
+
# handler_method - A Symbol representing the method to be invoked
|
|
10
|
+
# when a pattern matches.
|
|
14
11
|
def initialize(pattern, handler_method)
|
|
15
12
|
@pattern = pattern
|
|
16
13
|
@handler_method = handler_method
|
|
17
14
|
end
|
|
18
15
|
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
16
|
+
# tokens - An Array of tokens to process.
|
|
17
|
+
# definitions - A Hash of definitions to check against.
|
|
18
|
+
#
|
|
19
|
+
# Returns true if a match is found.
|
|
23
20
|
def match(tokens, definitions)
|
|
24
21
|
token_index = 0
|
|
22
|
+
@pattern.each do |elements|
|
|
23
|
+
was_optional = false
|
|
24
|
+
elements = [elements] unless elements.is_a?(Array)
|
|
25
|
+
|
|
26
|
+
elements.each_index do |i|
|
|
27
|
+
name = elements[i].to_s
|
|
28
|
+
optional = name[-1, 1] == '?'
|
|
29
|
+
name = name.chop if optional
|
|
30
|
+
|
|
31
|
+
case elements[i]
|
|
32
|
+
when Symbol
|
|
33
|
+
if tags_match?(name, tokens, token_index)
|
|
34
|
+
token_index += 1
|
|
35
|
+
break
|
|
36
|
+
else
|
|
37
|
+
if optional
|
|
38
|
+
was_optional = true
|
|
39
|
+
next
|
|
40
|
+
elsif i + 1 < elements.count
|
|
41
|
+
next
|
|
42
|
+
else
|
|
43
|
+
return false unless was_optional
|
|
44
|
+
end
|
|
45
|
+
end
|
|
25
46
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
optional = name[-1, 1] == '?'
|
|
29
|
-
name = name.chop if optional
|
|
47
|
+
when String
|
|
48
|
+
return true if optional && token_index == tokens.size
|
|
30
49
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if tags_match?(name, tokens, token_index)
|
|
34
|
-
token_index += 1
|
|
35
|
-
next
|
|
36
|
-
else
|
|
37
|
-
if optional
|
|
38
|
-
next
|
|
50
|
+
if definitions.key?(name.to_sym)
|
|
51
|
+
sub_handlers = definitions[name.to_sym]
|
|
39
52
|
else
|
|
40
|
-
|
|
53
|
+
raise "Invalid subset #{name} specified"
|
|
41
54
|
end
|
|
42
|
-
end
|
|
43
|
-
when String
|
|
44
|
-
return true if optional && token_index == tokens.size
|
|
45
55
|
|
|
46
|
-
|
|
47
|
-
|
|
56
|
+
sub_handlers.each do |sub_handler|
|
|
57
|
+
return true if sub_handler.match(tokens[token_index..tokens.size], definitions)
|
|
58
|
+
end
|
|
48
59
|
else
|
|
49
|
-
raise
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
sub_handlers.each do |sub_handler|
|
|
53
|
-
return true if sub_handler.match(tokens[token_index..tokens.size], definitions)
|
|
60
|
+
raise "Invalid match type: #{elements[i].class}"
|
|
54
61
|
end
|
|
55
|
-
else
|
|
56
|
-
raise ChronicPain, "Invalid match type: #{element.class}"
|
|
57
62
|
end
|
|
63
|
+
|
|
58
64
|
end
|
|
59
65
|
|
|
60
66
|
return false if token_index != tokens.size
|
|
61
67
|
return true
|
|
62
68
|
end
|
|
63
69
|
|
|
64
|
-
def invoke(type, tokens, options)
|
|
70
|
+
def invoke(type, tokens, parser, options)
|
|
65
71
|
if Chronic.debug
|
|
66
72
|
puts "-#{type}"
|
|
67
73
|
puts "Handler: #{@handler_method}"
|
|
68
74
|
end
|
|
69
75
|
|
|
70
|
-
|
|
76
|
+
parser.send(@handler_method, tokens, options)
|
|
71
77
|
end
|
|
72
78
|
|
|
73
|
-
#
|
|
74
|
-
#
|
|
79
|
+
# other - The other Handler object to compare.
|
|
80
|
+
#
|
|
81
|
+
# Returns true if these Handlers match.
|
|
75
82
|
def ==(other)
|
|
76
83
|
@pattern == other.pattern
|
|
77
84
|
end
|
|
@@ -87,4 +94,4 @@ module Chronic
|
|
|
87
94
|
end
|
|
88
95
|
|
|
89
96
|
end
|
|
90
|
-
end
|
|
97
|
+
end
|