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.
Files changed (3) hide show
  1. data/README.md +86 -1
  2. data/lib/minute.rb +74 -70
  3. 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.
@@ -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
- def self.add_unit(name, value)
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
- def self.rule(options = {})
10
- rules.merge! options
11
- end
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
- def self.rules
14
- @rules ||= {}
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
- @time = options[:now] || Time.now
24
- @current_time = @time
56
+ @now = options[:now] || Time.now
57
+ @current_time = @now
25
58
  end
26
59
 
27
60
  def parse_string(text)
28
- rules.keys.each do |key|
29
- if key.is_a?(Regexp) && text =~ key
30
- @current_time = rules[key].call(@time, Regexp.last_match)
31
- elsif key.is_a?(String) && text.include?(key)
32
- @current_time = rules[key].call(@time)
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
- current_time
72
+ @now
36
73
  end
37
74
 
38
- def rules
39
- @rules ||= self.class.rules
75
+ def patterns
76
+ @patterns ||= self.class.patterns
40
77
  end
41
78
 
42
- def to_date(time = nil)
43
- time ||= @current_time
44
- Date.new(time.year, time.month, time.day)
45
- end
46
-
47
- def current_time
48
- @current_time
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
- add_unit 'year', 31536000
52
- add_unit 'month', 3600 * 24 * 30
53
- add_unit 'week', 3600 * 24 * 7
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.1'
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-23 00:00:00.000000000 Z
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.15
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