natural-date 1.2 → 1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +13 -4
- data/lib/natural-date.rb +5 -1
- data/lib/natural-date/matcher/day_matcher.rb +5 -0
- data/lib/natural-date/matcher/literal_matcher.rb +6 -0
- data/lib/natural-date/matcher/month_matcher.rb +5 -0
- data/lib/natural-date/matcher/week_matcher.rb +35 -0
- data/lib/natural-date/matcher/year_matcher.rb +12 -0
- data/lib/natural-date/natural_date_expression.rb +10 -56
- data/lib/natural-date/natural_date_expression_factory.rb +3 -4
- data/lib/natural-date/translator/en.yml +8 -8
- data/natural-date.gemspec +1 -0
- metadata +21 -4
- data/lib/natural-date/translator.rb +0 -2
- data/lib/natural-date/translator/time_mounter.rb +0 -55
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c67af377850cb1f455ec9f16d20b11d46775ce32
|
4
|
+
data.tar.gz: 8fee5e98a916dee160b302a2e951862b1a9e9097
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 710e4c81e0f646632c199ee1a2203d6a9cefedf2ab12c43799ea9a037c29d8e5eb9c4648e0b193accf74d2a30e701c709073eca689bb3ba304335d86153efd67
|
7
|
+
data.tar.gz: 1996da7c86a768655b85c8b3de403fd29eb52e73841375022cc19f68206114e43b6856a5c7cf89bca9fa1c297ffeda80a693413835b12a0a9a4b404cc579e5ac
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Natural Date - Alpha
|
2
2
|
|
3
|
-
Natural language date
|
4
|
-
symbolize
|
3
|
+
Natural language date parser in Ruby. `natural-date` can convert from plain text to date expressions that can
|
4
|
+
symbolize both single dates and recurrent dates.
|
5
5
|
|
6
6
|
## Installation
|
7
7
|
|
@@ -50,9 +50,10 @@ natural_date.match?(Date.new(2017, 8, 1))
|
|
50
50
|
natural_date.fetch_dates(Date.today..(Date.today + 5))
|
51
51
|
# => array with dates matched inside the interval
|
52
52
|
|
53
|
-
# If you don't pass any interval a default interval of
|
53
|
+
# If you don't pass any interval a default interval of reference_date..reference_date + 365 will be given
|
54
54
|
natural_date.fetch_dates
|
55
55
|
# => array with dates matched inside the interval
|
56
|
+
|
56
57
|
```
|
57
58
|
|
58
59
|
## Types of expressions
|
@@ -63,9 +64,10 @@ refer both single and recurring dates.
|
|
63
64
|
- **Days**: Can be written as numbers `1..31` or ordinals like `['1st'..'31st']`.
|
64
65
|
- **Months**: Can be written using the whole name `['January'..'December']` or using the first 3 letters `['Jan'..'Dec']`, also they are case insensitive `'JAN' == 'Jan' == 'jAn'` also you can use numbers if you use the 'of' world before `'1 of 12' == '1st December'`.
|
65
66
|
- **Years**: You can write the year as usual 4 digits number like `2016` or you can ignore the year, if you ignore it the year is going to be guessed based on the expression and the reference date
|
66
|
-
- **Week Days**: As months you can write both whole name and abbreviation (first 3 letters) 'monday' == 'mon'
|
67
|
+
- **Week Days**: As months you can write both whole name and abbreviation (first 3 letters) `'monday' == 'mon'`.
|
67
68
|
|
68
69
|
```ruby
|
70
|
+
# first you need to create a Factory based on a language, nowadays only english and brazilian portuguese are available.
|
69
71
|
factory = NaturalDateExpressionFactory.new(:en)
|
70
72
|
|
71
73
|
# Day and month
|
@@ -105,6 +107,13 @@ factory.create('every monday of January')
|
|
105
107
|
# => every monday of January any year
|
106
108
|
factory.create('every monday and friday')
|
107
109
|
# => every monday and friday of any month and any year.
|
110
|
+
|
111
|
+
# You can also pass several dates expressions using a ';' or a '\n' to separate them
|
112
|
+
factory.create('1 2 of May 2016 ; 3 4 June 2016')
|
113
|
+
# => ['1st May 2016', '2nd May 2016', '3rd June 2016', '4th June 2016']
|
114
|
+
|
115
|
+
factory.create("1 May 2016\n4 June 2016")
|
116
|
+
# => ['1st May 2016', '4th June 2016']
|
108
117
|
```
|
109
118
|
|
110
119
|
## Contributing
|
data/lib/natural-date.rb
CHANGED
@@ -7,9 +7,13 @@ require 'natural-date/translator/mod_applier'
|
|
7
7
|
require 'natural-date/translator/mounter'
|
8
8
|
require 'natural-date/translator/normalizer'
|
9
9
|
require 'natural-date/translator/splitter_literal_date'
|
10
|
-
require 'natural-date/translator/time_mounter'
|
11
10
|
require 'natural-date/translator/unknown_cleaner'
|
12
11
|
require 'natural-date/translator/year_finder'
|
12
|
+
require 'natural-date/matcher/week_matcher'
|
13
|
+
require 'natural-date/matcher/month_matcher'
|
14
|
+
require 'natural-date/matcher/literal_matcher'
|
15
|
+
require 'natural-date/matcher/day_matcher'
|
16
|
+
require 'natural-date/matcher/year_matcher'
|
13
17
|
require 'natural-date/language_bundle'
|
14
18
|
require 'natural-date/natural_date_expression_factory'
|
15
19
|
require 'natural-date/natural_date_expression'
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module WeekMatcher
|
2
|
+
def self.match? date, reference_date, expression_map
|
3
|
+
!expression_map[:week_day] || expression_map[:week_day].any? { |token| match_week_token?(token, date) }
|
4
|
+
end
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def self.match_month? date, expression_map
|
9
|
+
!expression_map[:month] || expression_map[:month].include?(date.month)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.match_week_token? token, date
|
13
|
+
return match_nth_week_token?(token, date) if token.to_s.include?('.')
|
14
|
+
token.to_i == date.wday
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.match_nth_week_token? token, date
|
18
|
+
nth, token_wday = token.split('.')
|
19
|
+
|
20
|
+
wdays = (date.beginning_of_month..date.end_of_month).to_a.select do |day|
|
21
|
+
day.wday == token_wday.to_i
|
22
|
+
end
|
23
|
+
|
24
|
+
case nth
|
25
|
+
when 'first_1'
|
26
|
+
wdays.first == date
|
27
|
+
when 'last_999'
|
28
|
+
wdays.last == date
|
29
|
+
when 'prior_last_999'
|
30
|
+
wdays[wdays.size - 2] == date
|
31
|
+
else
|
32
|
+
wdays[nth.to_i - 1] == date
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module YearMatcher
|
2
|
+
def self.match? date, reference_date, expression_map
|
3
|
+
return expression_map[:year].include?(date.year) if expression_map[:year]
|
4
|
+
return true if expression_map[:week_day] || !expression_map[:day] || !expression_map[:month]
|
5
|
+
|
6
|
+
require 'pry'
|
7
|
+
|
8
|
+
binding.pry unless date
|
9
|
+
|
10
|
+
date.year == reference_date.year
|
11
|
+
end
|
12
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
class NaturalDateExpression
|
2
|
-
VERSION = "1.
|
2
|
+
VERSION = "1.3"
|
3
3
|
|
4
4
|
class DateMatch
|
5
5
|
attr_reader :first_matched_expression, :tested_date, :reference_date
|
@@ -30,13 +30,16 @@ class NaturalDateExpression
|
|
30
30
|
match(date).matches?
|
31
31
|
end
|
32
32
|
|
33
|
+
MATCHERS = [
|
34
|
+
WeekMatcher,
|
35
|
+
MonthMatcher,
|
36
|
+
DayMatcher,
|
37
|
+
YearMatcher
|
38
|
+
].freeze
|
39
|
+
|
33
40
|
def match date
|
34
41
|
matches = @data.map do |expression_map|
|
35
|
-
|
36
|
-
match_month?(date, expression_map) &&
|
37
|
-
match_literal?(date, expression_map) &&
|
38
|
-
match_day?(date, expression_map) &&
|
39
|
-
match_year?(date, expression_map)
|
42
|
+
MATCHERS.map { |matcher| matcher.match?(date, @reference_date, expression_map) }.all?
|
40
43
|
end
|
41
44
|
|
42
45
|
DateMatch.new(matches.any?,
|
@@ -54,55 +57,6 @@ class NaturalDateExpression
|
|
54
57
|
end
|
55
58
|
|
56
59
|
def fetch_dates dates_range = nil
|
57
|
-
(dates_range || (
|
58
|
-
end
|
59
|
-
|
60
|
-
private
|
61
|
-
|
62
|
-
def match_year? date, expression_map
|
63
|
-
return expression_map[:year].include?(date.year) if expression_map[:year]
|
64
|
-
return true if expression_map[:week_day] || !expression_map[:day] || !expression_map[:month]
|
65
|
-
|
66
|
-
date.year == @reference_date.year
|
67
|
-
end
|
68
|
-
|
69
|
-
def match_day? date, expression_map
|
70
|
-
!expression_map[:day] || expression_map[:day].include?(date.day)
|
71
|
-
end
|
72
|
-
|
73
|
-
def match_literal? date, expression_map
|
74
|
-
!expression_map[:literal] || expression_map[:literal].any? { |token| match_literal_token?(token, args) }
|
75
|
-
end
|
76
|
-
|
77
|
-
def match_week? date, expression_map
|
78
|
-
!expression_map[:week_day] || expression_map[:week_day].any? { |token| match_week_token?(token, date) }
|
79
|
-
end
|
80
|
-
|
81
|
-
def match_month? date, expression_map
|
82
|
-
!expression_map[:month] || expression_map[:month].include?(date.month)
|
83
|
-
end
|
84
|
-
|
85
|
-
def match_week_token? token, date
|
86
|
-
return match_nth_week_token?(token, date) if token.to_s.include?('.')
|
87
|
-
token.to_i == date.wday
|
88
|
-
end
|
89
|
-
|
90
|
-
def match_nth_week_token? token, date
|
91
|
-
nth, token_wday = token.split('.')
|
92
|
-
|
93
|
-
wdays = (date.beginning_of_month..date.end_of_month).to_a.select do |day|
|
94
|
-
day.wday == token_wday.to_i
|
95
|
-
end
|
96
|
-
|
97
|
-
case nth
|
98
|
-
when 'first_1'
|
99
|
-
wdays.first == date
|
100
|
-
when 'last_999'
|
101
|
-
wdays.last == date
|
102
|
-
when 'prior_last_999'
|
103
|
-
wdays[wdays.size - 2] == date
|
104
|
-
else
|
105
|
-
wdays[nth.to_i - 1] == date
|
106
|
-
end
|
60
|
+
(dates_range || (@reference_date..(@reference_date + 365))).to_a.select { |date| self =~ date }
|
107
61
|
end
|
108
62
|
end
|
@@ -3,15 +3,15 @@ class NaturalDateExpressionFactory
|
|
3
3
|
@lang = lang
|
4
4
|
end
|
5
5
|
|
6
|
-
STEPS = {}
|
7
|
-
EXPRESSION_SPLITTER_STEP = {}
|
8
|
-
|
9
6
|
def create expression_string, reference_date = Date.today
|
10
7
|
NaturalDateExpression.new(create_data_expression(expression_string, reference_date), reference_date, expression_string)
|
11
8
|
end
|
12
9
|
|
13
10
|
private
|
14
11
|
|
12
|
+
STEPS = {}
|
13
|
+
EXPRESSION_SPLITTER_STEP = {}
|
14
|
+
|
15
15
|
def create_data_expression expression_string, reference_date
|
16
16
|
expression_splitter.map(expression_string.to_s).map do |expression|
|
17
17
|
steps.reduce(expression) do |tokens, step|
|
@@ -40,7 +40,6 @@ class NaturalDateExpressionFactory
|
|
40
40
|
[ Translator::Cleaner,
|
41
41
|
Translator::SplitterLiteralDate,
|
42
42
|
Translator::Mounter,
|
43
|
-
Translator::TimeMounter,
|
44
43
|
Translator::Normalizer,
|
45
44
|
Translator::Expander,
|
46
45
|
Translator::ModApplier,
|
@@ -9,13 +9,13 @@ keys:
|
|
9
9
|
last_999: 'last'
|
10
10
|
of_month: 'of month'
|
11
11
|
week_day:
|
12
|
-
sun_0: 'sunday sun'
|
13
|
-
mon_1: 'monday mon'
|
14
|
-
tue_2: 'tuesday tue'
|
15
|
-
wed_3: 'wednesday wed'
|
16
|
-
thu_4: 'thursday thu'
|
17
|
-
fri_5: 'friday fri'
|
18
|
-
sat_6: 'saturday sat'
|
12
|
+
sun_0: 'sunday sundays sun'
|
13
|
+
mon_1: 'monday mondays mon'
|
14
|
+
tue_2: 'tuesday tuesdays tue'
|
15
|
+
wed_3: 'wednesday wednesdays wed'
|
16
|
+
thu_4: 'thursday thursdays thu'
|
17
|
+
fri_5: 'friday fridays fri'
|
18
|
+
sat_6: 'saturday saturdays sat'
|
19
19
|
day:
|
20
20
|
day_1: '1 1th 1st'
|
21
21
|
day_2: '2 2th 2nd'
|
@@ -49,7 +49,7 @@ keys:
|
|
49
49
|
day_30: '30 30th'
|
50
50
|
day_31: '31 31th 31st'
|
51
51
|
month:
|
52
|
-
jan_1: 'jan
|
52
|
+
jan_1: 'jan january'
|
53
53
|
feb_2: 'feb february'
|
54
54
|
mar_3: 'mar march'
|
55
55
|
apr_4: 'apr april'
|
data/natural-date.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: natural-date
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '1.
|
4
|
+
version: '1.3'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew S Aguiar
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-12-
|
11
|
+
date: 2016-12-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.10.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.10.0
|
55
69
|
description: Natural language date/time parser in Ruby. natural-date can convert from
|
56
70
|
plain text to date expressions symbolizing since single dates till recurrent dates
|
57
71
|
email:
|
@@ -72,9 +86,13 @@ files:
|
|
72
86
|
- bin/setup
|
73
87
|
- lib/natural-date.rb
|
74
88
|
- lib/natural-date/language_bundle.rb
|
89
|
+
- lib/natural-date/matcher/day_matcher.rb
|
90
|
+
- lib/natural-date/matcher/literal_matcher.rb
|
91
|
+
- lib/natural-date/matcher/month_matcher.rb
|
92
|
+
- lib/natural-date/matcher/week_matcher.rb
|
93
|
+
- lib/natural-date/matcher/year_matcher.rb
|
75
94
|
- lib/natural-date/natural_date_expression.rb
|
76
95
|
- lib/natural-date/natural_date_expression_factory.rb
|
77
|
-
- lib/natural-date/translator.rb
|
78
96
|
- lib/natural-date/translator/cleaner.rb
|
79
97
|
- lib/natural-date/translator/en.yml
|
80
98
|
- lib/natural-date/translator/expander.rb
|
@@ -86,7 +104,6 @@ files:
|
|
86
104
|
- lib/natural-date/translator/pt-BR.yml
|
87
105
|
- lib/natural-date/translator/splitter_literal_date.rb
|
88
106
|
- lib/natural-date/translator/step.rb
|
89
|
-
- lib/natural-date/translator/time_mounter.rb
|
90
107
|
- lib/natural-date/translator/unknown_cleaner.rb
|
91
108
|
- lib/natural-date/translator/year_finder.rb
|
92
109
|
- natural-date.gemspec
|
@@ -1,55 +0,0 @@
|
|
1
|
-
module Translator
|
2
|
-
class TimeMounter < Step
|
3
|
-
def map tokens, reference_date
|
4
|
-
tokens
|
5
|
-
.map { |token| normalize(token) }
|
6
|
-
end
|
7
|
-
|
8
|
-
private
|
9
|
-
|
10
|
-
def normalize token
|
11
|
-
case token.first
|
12
|
-
when :unknown
|
13
|
-
if time? token
|
14
|
-
[:time, create_token_value(token.last.upcase)]
|
15
|
-
else
|
16
|
-
token
|
17
|
-
end
|
18
|
-
else token
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def time? token
|
23
|
-
token_key = token.last.to_s.upcase
|
24
|
-
|
25
|
-
%w(H AM PM :).any? do |s|
|
26
|
-
token.last.to_s.upcase.include? s
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def create_token_value raw_value
|
31
|
-
if raw_value.include? ':'
|
32
|
-
hour, minute = raw_value.split(':')
|
33
|
-
|
34
|
-
"#{adjust_hour(hour, raw_value)}:#{adjust_minute(minute)}"
|
35
|
-
else
|
36
|
-
#TODO
|
37
|
-
raw_value
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def adjust_minute minute
|
42
|
-
minute.to_i
|
43
|
-
end
|
44
|
-
|
45
|
-
def adjust_hour hour, raw_value
|
46
|
-
if raw_value.include? 'PM'
|
47
|
-
Time.parse("#{hour}pm").hour
|
48
|
-
elsif raw_value.include? 'AM'
|
49
|
-
Time.parse("#{hour}am").hour
|
50
|
-
else
|
51
|
-
hour.to_i
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|