minute 0.1 → 0.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.
- 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
|