minute 0.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +86 -1
- data/lib/minute.rb +74 -70
- metadata +15 -4
data/README.md
CHANGED
@@ -1,4 +1,89 @@
|
|
1
1
|
Minute
|
2
2
|
======
|
3
3
|
|
4
|
-
Natural Language Date/Time parsing library for Ruby
|
4
|
+
Flexible Natural Language Date/Time parsing library for Ruby (1.9.x)
|
5
|
+
|
6
|
+
## Feature
|
7
|
+
|
8
|
+
* Flexible Natural Language parsing
|
9
|
+
* Source code is small, readable and maintainable
|
10
|
+
* Can parse short or long sentence when date/time is included
|
11
|
+
|
12
|
+
## Approach
|
13
|
+
|
14
|
+
[Chronic](http://) has been an amazing gem, but I learned its limitation. It cannot parse long sentence or it will give you `nil` as a resultant value. Chronic's code design is difficult to maintain.
|
15
|
+
|
16
|
+
I was thinking what if I can create a simple Ruby library with the same approach, but more flexible and more features. Thus, Minute was created to address this problem.
|
17
|
+
|
18
|
+
Examples:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
|
22
|
+
Chronic.parse("I have to meet my friend on December 13 in Dallas, TX")
|
23
|
+
#=> nil
|
24
|
+
|
25
|
+
Minute.parse("I have to meet my friend on December 13 in Dallas, TX")
|
26
|
+
#=> 2012-12-13 00:00:00 -0600
|
27
|
+
|
28
|
+
```
|
29
|
+
|
30
|
+
|
31
|
+
## Installation
|
32
|
+
|
33
|
+
**Gemfile**
|
34
|
+
|
35
|
+
gem 'minute'
|
36
|
+
|
37
|
+
**RubyGem**
|
38
|
+
|
39
|
+
gem install minute
|
40
|
+
|
41
|
+
## Usage
|
42
|
+
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
|
46
|
+
require 'minute'
|
47
|
+
|
48
|
+
Minute.parse("Hi! I am Ruby. I was born on Feb 24 1993. My father taught me to make programmers happy.")
|
49
|
+
#=> 1993-02-24 00:00:00 -0600
|
50
|
+
|
51
|
+
Minute.parse("Birthday party is tomorrow night!", now: Time.now)
|
52
|
+
#=> => 2012-05-01 11:20:48 -0500
|
53
|
+
|
54
|
+
```
|
55
|
+
|
56
|
+
|
57
|
+
## Speed
|
58
|
+
|
59
|
+
Minute is using `String#index?` or Regular Expression to recognize the date or time. `String#index?` is using [Rabin-Karp String Search algorithm](http://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_string_search_algorithm), which it is making the search faster.
|
60
|
+
|
61
|
+
user system total real
|
62
|
+
minute 12.750000 0.080000 12.830000 ( 12.833267)
|
63
|
+
chronic 97.860000 0.130000 97.990000 ( 97.983466)
|
64
|
+
|
65
|
+
|
66
|
+
## TODO
|
67
|
+
|
68
|
+
Minute is still young. Some features are being added soon.
|
69
|
+
|
70
|
+
Planned to add the following:
|
71
|
+
|
72
|
+
* This december
|
73
|
+
* Last Janurary
|
74
|
+
* between 8am and 5pm
|
75
|
+
* between Jan and July
|
76
|
+
* every weekend
|
77
|
+
* this morning
|
78
|
+
* tonight
|
79
|
+
* and more..
|
80
|
+
|
81
|
+
## Contribution
|
82
|
+
|
83
|
+
If you discover bugs or want to help or improve it, Pull Requests are welcome.
|
84
|
+
|
85
|
+
Ensure that your Pull request is using a topic branch for patching, improving, refactoring, etc.
|
86
|
+
|
87
|
+
## License
|
88
|
+
|
89
|
+
Minute is licensed under the MIT License.
|
data/lib/minute.rb
CHANGED
@@ -1,101 +1,105 @@
|
|
1
|
+
require 'small'
|
2
|
+
# Public: Minute is a natural language Date/Time parsing for Ruby Library
|
3
|
+
#
|
4
|
+
# Examples:
|
5
|
+
#
|
6
|
+
# require 'minute'
|
7
|
+
#
|
8
|
+
# Minute.parse("Hi I am Ruby I was born on Feb 24, 1993.")
|
9
|
+
#
|
10
|
+
# Minute.parse("I came back from May 13, 2025", now: (Time.now + 23.years))
|
11
|
+
#
|
1
12
|
class Minute
|
2
13
|
|
3
14
|
VERSION = "0.1"
|
4
15
|
|
5
|
-
|
6
|
-
class_eval("def self.#{name}; #{value} end", __FILE__, __LINE__ + 1)
|
7
|
-
end
|
16
|
+
MONTHS = Date::MONTHNAMES.zip(Date::ABBR_MONTHNAMES).flatten.compact.map(&:downcase).freeze
|
8
17
|
|
9
|
-
|
10
|
-
|
11
|
-
|
18
|
+
DATE_WITH_ORDINAL = /(\d{1,2})(st|nd|rd|th)?\s+(#{MONTHS.join("|")})(\s+\d{4})?/
|
19
|
+
ENGLISH_DATE = /(#{MONTHS.join("|")}),?\s(\d{1,2})(\s+\d{4})?/
|
20
|
+
DATE = /(\d{1,4})-?(\d{1,4})-?(\d{1,4})?/
|
12
21
|
|
13
|
-
|
14
|
-
|
22
|
+
# Public: Create a match pattern to capture date/time strings.
|
23
|
+
#
|
24
|
+
# Examples:
|
25
|
+
#
|
26
|
+
# match "tomorrow", lambda {|time| time + 1.day}
|
27
|
+
#
|
28
|
+
# match "yesterday" do |time|
|
29
|
+
# time - 1.day
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# Returns Hash of match patterns.
|
33
|
+
def self.match(pattern, block = nil, &blk)
|
34
|
+
patterns.merge!({pattern => (block ? block : blk)})
|
15
35
|
end
|
16
36
|
|
37
|
+
# Public: parse the String to capture Date/Time
|
38
|
+
#
|
39
|
+
# Examples:
|
40
|
+
#
|
41
|
+
# Minute.parse("tomorrow")
|
42
|
+
#
|
43
|
+
# Returns an instance of Time
|
17
44
|
def self.parse(text, options = {})
|
18
45
|
obj = new(options)
|
19
|
-
obj.parse_string(text)
|
46
|
+
obj.parse_string(text.to_s.downcase.strip)
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.patterns
|
50
|
+
@patterns ||= {}
|
20
51
|
end
|
21
52
|
|
53
|
+
attr_reader :now, :current_time
|
54
|
+
|
22
55
|
def initialize(options = {})
|
23
|
-
@
|
24
|
-
@current_time = @
|
56
|
+
@now = options[:now] || Time.now
|
57
|
+
@current_time = @now
|
25
58
|
end
|
26
59
|
|
27
60
|
def parse_string(text)
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
61
|
+
patterns.keys.each do |pattern|
|
62
|
+
begin
|
63
|
+
if pattern.is_a?(Regexp) && text =~ pattern
|
64
|
+
@now = patterns[pattern].call(current_time, Regexp.last_match)
|
65
|
+
elsif pattern.is_a?(String) && text.include?(pattern)
|
66
|
+
@now = patterns[pattern].call(current_time)
|
67
|
+
end
|
68
|
+
rescue
|
69
|
+
@now
|
33
70
|
end
|
34
71
|
end
|
35
|
-
|
72
|
+
@now
|
36
73
|
end
|
37
74
|
|
38
|
-
def
|
39
|
-
@
|
75
|
+
def patterns
|
76
|
+
@patterns ||= self.class.patterns
|
40
77
|
end
|
41
78
|
|
42
|
-
|
43
|
-
time
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
79
|
+
match 'tomorrow', lambda {|time| time + 1.day }
|
80
|
+
match 'yesterday', lambda {|time| time - 1.day }
|
81
|
+
match 'today', lambda {|time| time }
|
82
|
+
|
83
|
+
match /(last|next|\d+)\s+(year|month|week|day|hour|minute|second)s?(\s+ago)?/ do |time, m|
|
84
|
+
case m[1]
|
85
|
+
when "next" then time + 1.send(m[2].to_sym)
|
86
|
+
when "last" then time - 1.send(m[2].to_sym)
|
87
|
+
when /\d+/
|
88
|
+
if !m[3].nil?
|
89
|
+
time - m[1].to_i.send(m[2].to_sym)
|
90
|
+
else
|
91
|
+
time + m[1].to_i.send(m[2].to_sym)
|
92
|
+
end
|
93
|
+
end
|
49
94
|
end
|
50
95
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
add_unit 'day', 3600 * 24
|
55
|
-
add_unit 'hour', 3600
|
56
|
-
add_unit 'minute', 60
|
57
|
-
add_unit 'second', 1
|
58
|
-
|
59
|
-
rule 'tomorrow' => lambda {|time| time + day }
|
60
|
-
rule 'yesterday' => lambda {|time| time - day }
|
61
|
-
rule 'today' => lambda {|time| time }
|
62
|
-
|
63
|
-
rule 'next year' => lambda {|time| time + year }
|
64
|
-
rule 'next month' => lambda {|time| time + month }
|
65
|
-
rule 'next week' => lambda {|time| time + week }
|
66
|
-
rule 'next day' => lambda {|time| time + day }
|
67
|
-
rule 'next hour' => lambda {|time| time + hour }
|
68
|
-
rule 'next minute' => lambda {|time| time + minute }
|
69
|
-
rule 'next second' => lambda {|time| time + second }
|
70
|
-
|
71
|
-
rule 'last year' => lambda {|time| time - year }
|
72
|
-
rule 'last month' => lambda {|time| time - month }
|
73
|
-
rule 'last week' => lambda {|time| time - week }
|
74
|
-
rule 'last day' => lambda {|time| time - day }
|
75
|
-
rule 'last hour' => lambda {|time| time - hour }
|
76
|
-
rule 'last minute' => lambda {|time| time - minute }
|
77
|
-
rule 'last second' => lambda {|time| time - second }
|
78
|
-
|
79
|
-
rule /(\d+) years?/ => lambda {|time, m| time + (m[1].to_i * year) }
|
80
|
-
rule /(\d+) years? ago/ => lambda {|time, m| time - (m[1].to_i * year) }
|
81
|
-
rule /(\d+) months?/ => lambda {|time, m| time + (m[1].to_i * month) }
|
82
|
-
rule /(\d+) months? ago/ => lambda {|time, m| time - (m[1].to_i * month) }
|
83
|
-
rule /(\d+) weeks?/ => lambda {|time, m| time + (m[1].to_i * week) }
|
84
|
-
rule /(\d+) weeks? ago/ => lambda {|time, m| time - (m[1].to_i * week) }
|
85
|
-
rule /(\d+) days?/ => lambda {|time, m| time + (m[1].to_i * day) }
|
86
|
-
rule /(\d+) days? ago/ => lambda {|time, m| time - (m[1].to_i * day) }
|
87
|
-
rule /(\d+) hours?/ => lambda {|time, m| time + (m[1].to_i * hour) }
|
88
|
-
rule /(\d+) hours? ago/ => lambda {|time, m| time - (m[1].to_i * hour) }
|
89
|
-
rule /(\d+) minutes?/ => lambda {|time, m| time + (m[1].to_i * minute) }
|
90
|
-
rule /(\d+) minutes? ago/ => lambda {|time, m| time - (m[1].to_i * minute) }
|
91
|
-
rule /(\d+) seconds?/ => lambda {|time, m| time + (m[1].to_i * second) }
|
92
|
-
rule /(\d+) seconds? ago/ => lambda {|time, m| time - (m[1].to_i * second) }
|
96
|
+
match DATE, lambda {|time, m| Time.parse(m[1..-1].compact.join("-")) }
|
97
|
+
match ENGLISH_DATE, lambda {|time, m| Time.parse(m[1..-1].compact.join(" ")) }
|
98
|
+
match DATE_WITH_ORDINAL, lambda {|time, m| Time.parse(m[1..-1].compact.join(" ")) }
|
93
99
|
|
94
100
|
# TODO
|
95
101
|
# this december
|
96
102
|
# last december
|
97
|
-
# 13 dec 2010
|
98
|
-
# dec 13 2010
|
99
103
|
# next jan 13
|
100
104
|
# every wed
|
101
105
|
# this morning
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: minute
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.2'
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,8 +9,19 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-04-
|
13
|
-
dependencies:
|
12
|
+
date: 2012-04-30 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: small
|
16
|
+
requirement: &70134369380160 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - =
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0.3'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70134369380160
|
14
25
|
description:
|
15
26
|
email: bryann83@gmail.com
|
16
27
|
executables: []
|
@@ -39,7 +50,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
39
50
|
version: '0'
|
40
51
|
requirements: []
|
41
52
|
rubyforge_project:
|
42
|
-
rubygems_version: 1.8.
|
53
|
+
rubygems_version: 1.8.11
|
43
54
|
signing_key:
|
44
55
|
specification_version: 3
|
45
56
|
summary: Natural Language Date/Time parsing library for Ruby
|