natural-date 1.2 → 1.3
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/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
|