chronic 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/HISTORY.md +76 -0
  2. data/LICENSE +21 -0
  3. data/README.md +165 -0
  4. data/Rakefile +145 -18
  5. data/benchmark/benchmark.rb +13 -0
  6. data/chronic.gemspec +85 -0
  7. data/lib/chronic.rb +21 -19
  8. data/lib/chronic/chronic.rb +59 -49
  9. data/lib/chronic/grabber.rb +2 -2
  10. data/lib/chronic/handlers.rb +167 -112
  11. data/lib/{numerizer → chronic/numerizer}/numerizer.rb +17 -23
  12. data/lib/chronic/ordinal.rb +6 -6
  13. data/lib/chronic/pointer.rb +3 -3
  14. data/lib/chronic/repeater.rb +26 -12
  15. data/lib/chronic/repeaters/repeater_day.rb +17 -12
  16. data/lib/chronic/repeaters/repeater_day_name.rb +17 -12
  17. data/lib/chronic/repeaters/repeater_day_portion.rb +13 -12
  18. data/lib/chronic/repeaters/repeater_fortnight.rb +14 -9
  19. data/lib/chronic/repeaters/repeater_hour.rb +15 -10
  20. data/lib/chronic/repeaters/repeater_minute.rb +15 -10
  21. data/lib/chronic/repeaters/repeater_month.rb +20 -15
  22. data/lib/chronic/repeaters/repeater_month_name.rb +21 -16
  23. data/lib/chronic/repeaters/repeater_season.rb +136 -9
  24. data/lib/chronic/repeaters/repeater_season_name.rb +38 -17
  25. data/lib/chronic/repeaters/repeater_second.rb +15 -10
  26. data/lib/chronic/repeaters/repeater_time.rb +49 -42
  27. data/lib/chronic/repeaters/repeater_week.rb +16 -11
  28. data/lib/chronic/repeaters/repeater_weekday.rb +77 -0
  29. data/lib/chronic/repeaters/repeater_weekend.rb +14 -9
  30. data/lib/chronic/repeaters/repeater_year.rb +19 -13
  31. data/lib/chronic/scalar.rb +16 -14
  32. data/lib/chronic/separator.rb +25 -10
  33. data/lib/chronic/time_zone.rb +4 -3
  34. data/test/helper.rb +7 -0
  35. data/test/test_Chronic.rb +17 -18
  36. data/test/test_DaylightSavings.rb +118 -0
  37. data/test/test_Handler.rb +37 -38
  38. data/test/test_Numerizer.rb +8 -5
  39. data/test/test_RepeaterDayName.rb +15 -16
  40. data/test/test_RepeaterFortnight.rb +16 -17
  41. data/test/test_RepeaterHour.rb +18 -19
  42. data/test/test_RepeaterMinute.rb +34 -0
  43. data/test/test_RepeaterMonth.rb +16 -17
  44. data/test/test_RepeaterMonthName.rb +17 -18
  45. data/test/test_RepeaterTime.rb +20 -22
  46. data/test/test_RepeaterWeek.rb +16 -17
  47. data/test/test_RepeaterWeekday.rb +55 -0
  48. data/test/test_RepeaterWeekend.rb +21 -22
  49. data/test/test_RepeaterYear.rb +17 -18
  50. data/test/test_Span.rb +5 -6
  51. data/test/test_Time.rb +11 -12
  52. data/test/test_Token.rb +5 -6
  53. data/test/test_parsing.rb +300 -204
  54. metadata +74 -52
  55. data/History.txt +0 -53
  56. data/README.txt +0 -149
  57. data/test/suite.rb +0 -9
@@ -0,0 +1,76 @@
1
+ # 0.3.0 / 2010-10-22
2
+
3
+ * Fix numerizer number combination bug (27 Oct 2006 7:30pm works now)
4
+ * Allow numeric timezone offset (e.g -0500)
5
+ * Disregard commas (so as to not return nil)
6
+ * Fix parse of (am|pm|oclock) separation to handle "Ham sandwich" properly
7
+ * Handle 'on' e.g. 5pm on Monday
8
+ * Support seasons
9
+ * Support weekend/weekday
10
+ * Add endianness option
11
+ * Update version number in the module
12
+ * Fix/improve logic checks in Ordinal, and Scalar
13
+ * Parse 'a' or 'p' as 'am' and 'pm' google-calendar style
14
+ * Dates < 1 are not valid
15
+ * Fix bugs related to timezone offset
16
+ * Use RakeGem for build management
17
+ * Reformat README and HISTORY to use Markdown
18
+ * Global whitespace removal
19
+
20
+ # 0.2.3
21
+
22
+ * Fix 12am/12pm
23
+
24
+ # 0.2.2
25
+
26
+ * Add missing files (damn you manifest)
27
+
28
+ # 0.2.1
29
+
30
+ * Fix time overflow issue
31
+ * Implement "next" for minute repeater
32
+ * Generalize time dealiasing to dealias regardless of day portion and
33
+ time position
34
+ * Add additional token match for cases like "friday evening at 7" and
35
+ "tomorrow evening at 7"
36
+ * Add support for Time#to_s output format: "Mon Apr 02 17:00:00 PDT 2007"
37
+
38
+ # 0.2.0 2007-03-20
39
+
40
+ * Implement numerizer, allowing the use of number words (e.g. five weeks ago)
41
+
42
+ # 0.1.6 2006-01-15
43
+
44
+ * Add 'weekend' support
45
+
46
+ # 0.1.5 2006-12-20
47
+
48
+ * Fix 'aug 20' returning next year if current month is august
49
+ * Modify behavior of 'from now'
50
+ * Add support for seconds on times, and thus db timestamp format:
51
+ "2006-12-20 18:04:23"
52
+ * Make Hoe compliant
53
+
54
+ # 0.1.4
55
+
56
+ * Remove verbose error checking code. oops. :-/
57
+
58
+ # 0.1.3
59
+
60
+ * improved regexes for word variations
61
+ * Fix a bug that caused "today at 3am" to return nil if current time is
62
+ after 3am
63
+
64
+ # 0.1.2
65
+
66
+ * Remove Date dependency (now works on windows properly without fiddling)
67
+
68
+ # 0.1.1
69
+
70
+ * Run to_s on incoming object
71
+ * Fix loop loading of repeaters files (out of order on some machines)
72
+ * Fix find_within to use this instead of next (was breaking "today at 6pm")
73
+
74
+ # 0.1.0
75
+
76
+ * Initial release
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) Tom Preston-Werner
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,165 @@
1
+ Chronic
2
+ =======
3
+
4
+ ## DESCRIPTION
5
+
6
+ Chronic is a natural language date/time parser written in pure Ruby. See below
7
+ for the wide variety of formats Chronic will parse.
8
+
9
+
10
+ ## INSTALLATION
11
+
12
+ The best way to install Gollum is with RubyGems:
13
+
14
+ $ [sudo] gem install chronic
15
+
16
+
17
+ ## USAGE
18
+
19
+ You can parse strings containing a natural language date using the
20
+ Chronic.parse method.
21
+
22
+ require 'rubygems'
23
+ require 'chronic'
24
+
25
+ Time.now #=> Sun Aug 27 23:18:25 PDT 2006
26
+
27
+ #---
28
+
29
+ Chronic.parse('tomorrow')
30
+ #=> Mon Aug 28 12:00:00 PDT 2006
31
+
32
+ Chronic.parse('monday', :context => :past)
33
+ #=> Mon Aug 21 12:00:00 PDT 2006
34
+
35
+ Chronic.parse('this tuesday 5:00')
36
+ #=> Tue Aug 29 17:00:00 PDT 2006
37
+
38
+ Chronic.parse('this tuesday 5:00', :ambiguous_time_range => :none)
39
+ #=> Tue Aug 29 05:00:00 PDT 2006
40
+
41
+ Chronic.parse('may 27th', :now => Time.local(2000, 1, 1))
42
+ #=> Sat May 27 12:00:00 PDT 2000
43
+
44
+ Chronic.parse('may 27th', :guess => false)
45
+ #=> Sun May 27 00:00:00 PDT 2007..Mon May 28 00:00:00 PDT 2007
46
+
47
+ See Chronic.parse for detailed usage instructions.
48
+
49
+
50
+ ## EXAMPLES
51
+
52
+ Chronic can parse a huge variety of date and time formats. Following is a
53
+ small sample of strings that will be properly parsed. Parsing is case
54
+ insensitive and will handle common abbreviations and misspellings.
55
+
56
+ Simple
57
+
58
+ * thursday
59
+ * november
60
+ * summer
61
+ * friday 13:00
62
+ * mon 2:35
63
+ * 4pm
64
+ * 6 in the morning
65
+ * friday 1pm
66
+ * sat 7 in the evening
67
+ * yesterday
68
+ * today
69
+ * tomorrow
70
+ * this tuesday
71
+ * next month
72
+ * last winter
73
+ * this morning
74
+ * last night
75
+ * this second
76
+ * yesterday at 4:00
77
+ * last friday at 20:00
78
+ * last week tuesday
79
+ * tomorrow at 6:45pm
80
+ * afternoon yesterday
81
+ * thursday last week
82
+
83
+ Complex
84
+
85
+ * 3 years ago
86
+ * 5 months before now
87
+ * 7 hours ago
88
+ * 7 days from now
89
+ * 1 week hence
90
+ * in 3 hours
91
+ * 1 year ago tomorrow
92
+ * 3 months ago saturday at 5:00 pm
93
+ * 7 hours before tomorrow at noon
94
+ * 3rd wednesday in november
95
+ * 3rd month next year
96
+ * 3rd thursday this september
97
+ * 4th day last week
98
+
99
+ Specific Dates
100
+
101
+ * January 5
102
+ * dec 25
103
+ * may 27th
104
+ * October 2006
105
+ * oct 06
106
+ * jan 3 2010
107
+ * february 14, 2004
108
+ * 3 jan 2000
109
+ * 17 april 85
110
+ * 5/27/1979
111
+ * 27/5/1979
112
+ * 05/06
113
+ * 1979-05-27
114
+ * Friday
115
+ * 5
116
+ * 4:00
117
+ * 17:00
118
+ * 0800
119
+
120
+ Specific Times (many of the above with an added time)
121
+
122
+ * January 5 at 7pm
123
+ * 1979-05-27 05:00:00
124
+ * etc
125
+
126
+
127
+ ## TIME ZONES
128
+
129
+ Chronic allows you to set which Time class to use when constructing times. By
130
+ default, the built in Ruby time class creates times in your system's local
131
+ time zone. You can set this to something like ActiveSupport's TimeZone class
132
+ to get full time zone support.
133
+
134
+ >> Time.zone = "UTC"
135
+ >> Chronic.time_class = Time.zone
136
+ >> Chronic.parse("June 15 2006 at 5:45 AM")
137
+ => Thu, 15 Jun 2006 05:45:00 UTC +00:00
138
+
139
+
140
+ ## LIMITATIONS
141
+
142
+ Chronic uses Ruby's built in Time class for all time storage and computation.
143
+ Because of this, only times that the Time class can handle will be properly
144
+ parsed. Parsing for times outside of this range will simply return nil.
145
+ Support for a wider range of times is planned for a future release.
146
+
147
+
148
+ ## CONTRIBUTE
149
+
150
+ If you'd like to hack on Chronic, start by forking the repo on GitHub:
151
+
152
+ http://github.com/github/chronic
153
+
154
+ To get all of the dependencies, install the gem first. The best way to get
155
+ your changes merged back into core is as follows:
156
+
157
+ 1. Clone down your fork
158
+ 1. Create a thoughtfully named topic branch to contain your change
159
+ 1. Hack away
160
+ 1. Add tests and make sure everything still passes by running `rake`
161
+ 1. If you are adding new functionality, document it in the README
162
+ 1. Do not change the version number, we will do that on our end
163
+ 1. If necessary, rebase your commits into logical chunks, without errors
164
+ 1. Push the branch up to GitHub
165
+ 1. Send a pull request for your branch
data/Rakefile CHANGED
@@ -1,19 +1,146 @@
1
- # -*- ruby -*-
2
-
3
1
  require 'rubygems'
4
- require 'hoe'
5
- require './lib/chronic.rb'
6
-
7
- Hoe.new('chronic', Chronic::VERSION) do |p|
8
- p.rubyforge_name = 'chronic'
9
- p.summary = 'A natural language date parser'
10
- p.author = 'Tom Preston-Werner'
11
- p.email = 'tom@rubyisawesome.com'
12
- p.description = p.paragraphs_of('README.txt', 2).join("\n\n")
13
- p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
14
- p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
15
- p.need_tar = false
16
- p.extra_deps = []
17
- end
18
-
19
- # vim: syntax=Ruby
2
+ require 'rake'
3
+ require 'date'
4
+
5
+ #############################################################################
6
+ #
7
+ # Helper functions
8
+ #
9
+ #############################################################################
10
+
11
+ def name
12
+ @name ||= Dir['*.gemspec'].first.split('.').first
13
+ end
14
+
15
+ def version
16
+ line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/]
17
+ line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
18
+ end
19
+
20
+ def date
21
+ Date.today.to_s
22
+ end
23
+
24
+ def rubyforge_project
25
+ name
26
+ end
27
+
28
+ def gemspec_file
29
+ "#{name}.gemspec"
30
+ end
31
+
32
+ def gem_file
33
+ "#{name}-#{version}.gem"
34
+ end
35
+
36
+ def replace_header(head, header_name)
37
+ head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
38
+ end
39
+
40
+ #############################################################################
41
+ #
42
+ # Standard tasks
43
+ #
44
+ #############################################################################
45
+
46
+ task :default => :test
47
+
48
+ require 'rake/testtask'
49
+ Rake::TestTask.new(:test) do |test|
50
+ test.libs << 'lib' << 'test'
51
+ test.pattern = 'test/**/test_*.rb'
52
+ test.verbose = true
53
+ end
54
+
55
+ desc "Generate RCov test coverage and open in your browser"
56
+ task :coverage do
57
+ require 'rcov'
58
+ sh "rm -fr coverage"
59
+ sh "rcov test/test_*.rb"
60
+ sh "open coverage/index.html"
61
+ end
62
+
63
+ require 'rake/rdoctask'
64
+ Rake::RDocTask.new do |rdoc|
65
+ rdoc.rdoc_dir = 'rdoc'
66
+ rdoc.title = "#{name} #{version}"
67
+ rdoc.rdoc_files.include('README*')
68
+ rdoc.rdoc_files.include('lib/**/*.rb')
69
+ end
70
+
71
+ desc "Open an irb session preloaded with this library"
72
+ task :console do
73
+ sh "irb -rubygems -r ./lib/#{name}.rb"
74
+ end
75
+
76
+ #############################################################################
77
+ #
78
+ # Custom tasks (add your own tasks here)
79
+ #
80
+ #############################################################################
81
+
82
+
83
+
84
+ #############################################################################
85
+ #
86
+ # Packaging tasks
87
+ #
88
+ #############################################################################
89
+
90
+ task :release => :build do
91
+ unless `git branch` =~ /^\* master$/
92
+ puts "You must be on the master branch to release!"
93
+ exit!
94
+ end
95
+ sh "git commit --allow-empty -a -m 'Release #{version}'"
96
+ sh "git tag v#{version}"
97
+ sh "git push origin master"
98
+ sh "git push origin v#{version}"
99
+ sh "gem push pkg/#{name}-#{version}.gem"
100
+ end
101
+
102
+ task :build => :gemspec do
103
+ sh "mkdir -p pkg"
104
+ sh "gem build #{gemspec_file}"
105
+ sh "mv #{gem_file} pkg"
106
+ end
107
+
108
+ task :gemspec => :validate do
109
+ # read spec file and split out manifest section
110
+ spec = File.read(gemspec_file)
111
+ head, manifest, tail = spec.split(" # = MANIFEST =\n")
112
+
113
+ # replace name version and date
114
+ replace_header(head, :name)
115
+ replace_header(head, :version)
116
+ replace_header(head, :date)
117
+ #comment this out if your rubyforge_project has a different name
118
+ replace_header(head, :rubyforge_project)
119
+
120
+ # determine file list from git ls-files
121
+ files = `git ls-files`.
122
+ split("\n").
123
+ sort.
124
+ reject { |file| file =~ /^\./ }.
125
+ reject { |file| file =~ /^(rdoc|pkg)/ }.
126
+ map { |file| " #{file}" }.
127
+ join("\n")
128
+
129
+ # piece file back together and write
130
+ manifest = " s.files = %w[\n#{files}\n ]\n"
131
+ spec = [head, manifest, tail].join(" # = MANIFEST =\n")
132
+ File.open(gemspec_file, 'w') { |io| io.write(spec) }
133
+ puts "Updated #{gemspec_file}"
134
+ end
135
+
136
+ task :validate do
137
+ libfiles = Dir['lib/*'] - ["lib/#{name}.rb", "lib/#{name}"]
138
+ unless libfiles.empty?
139
+ puts "Directory `lib` should only contain a `#{name}.rb` file and `#{name}` dir."
140
+ exit!
141
+ end
142
+ unless Dir['VERSION*'].empty?
143
+ puts "A `VERSION` file at root level violates Gem best practices."
144
+ exit!
145
+ end
146
+ end
@@ -0,0 +1,13 @@
1
+ require 'chronic'
2
+ require 'benchmark'
3
+
4
+ print "jan 3 2010: "
5
+ puts Benchmark.measure { Chronic.parse("jan 3 2010") }
6
+
7
+ print "7 hours before tomorrow at midnight: "
8
+ puts Benchmark.measure { Chronic.parse("7 hours before tomorrow at midnight") }
9
+
10
+ # n = 100
11
+ # Benchmark.bm(14) do |x|
12
+ # x.report("jan 3 2010:") { for i in 1..n; Chronic.parse("jan 3 2010"); end }
13
+ # end
@@ -0,0 +1,85 @@
1
+ Gem::Specification.new do |s|
2
+ s.specification_version = 2 if s.respond_to? :specification_version=
3
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
4
+ s.rubygems_version = '1.3.5'
5
+
6
+ s.name = 'chronic'
7
+ s.version = '0.3.0'
8
+ s.date = '2010-10-22'
9
+ s.rubyforge_project = 'chronic'
10
+
11
+ s.summary = "Natural language date/time parsing."
12
+ s.description = "Chronic is a natural language date/time parser written in pure Ruby."
13
+
14
+ s.authors = ["Tom Preston-Werner"]
15
+ s.email = 'tom@mojombo.com'
16
+ s.homepage = 'http://github.com/mojombo/chronic'
17
+
18
+ s.require_paths = %w[lib]
19
+
20
+ s.rdoc_options = ["--charset=UTF-8"]
21
+ s.extra_rdoc_files = %w[README.md HISTORY.md LICENSE]
22
+
23
+ # = MANIFEST =
24
+ s.files = %w[
25
+ HISTORY.md
26
+ LICENSE
27
+ Manifest.txt
28
+ README.md
29
+ Rakefile
30
+ benchmark/benchmark.rb
31
+ chronic.gemspec
32
+ lib/chronic.rb
33
+ lib/chronic/chronic.rb
34
+ lib/chronic/grabber.rb
35
+ lib/chronic/handlers.rb
36
+ lib/chronic/numerizer/numerizer.rb
37
+ lib/chronic/ordinal.rb
38
+ lib/chronic/pointer.rb
39
+ lib/chronic/repeater.rb
40
+ lib/chronic/repeaters/repeater_day.rb
41
+ lib/chronic/repeaters/repeater_day_name.rb
42
+ lib/chronic/repeaters/repeater_day_portion.rb
43
+ lib/chronic/repeaters/repeater_fortnight.rb
44
+ lib/chronic/repeaters/repeater_hour.rb
45
+ lib/chronic/repeaters/repeater_minute.rb
46
+ lib/chronic/repeaters/repeater_month.rb
47
+ lib/chronic/repeaters/repeater_month_name.rb
48
+ lib/chronic/repeaters/repeater_season.rb
49
+ lib/chronic/repeaters/repeater_season_name.rb
50
+ lib/chronic/repeaters/repeater_second.rb
51
+ lib/chronic/repeaters/repeater_time.rb
52
+ lib/chronic/repeaters/repeater_week.rb
53
+ lib/chronic/repeaters/repeater_weekday.rb
54
+ lib/chronic/repeaters/repeater_weekend.rb
55
+ lib/chronic/repeaters/repeater_year.rb
56
+ lib/chronic/scalar.rb
57
+ lib/chronic/separator.rb
58
+ lib/chronic/time_zone.rb
59
+ test/helper.rb
60
+ test/test_Chronic.rb
61
+ test/test_DaylightSavings.rb
62
+ test/test_Handler.rb
63
+ test/test_Numerizer.rb
64
+ test/test_RepeaterDayName.rb
65
+ test/test_RepeaterFortnight.rb
66
+ test/test_RepeaterHour.rb
67
+ test/test_RepeaterMinute.rb
68
+ test/test_RepeaterMonth.rb
69
+ test/test_RepeaterMonthName.rb
70
+ test/test_RepeaterTime.rb
71
+ test/test_RepeaterWeek.rb
72
+ test/test_RepeaterWeekday.rb
73
+ test/test_RepeaterWeekend.rb
74
+ test/test_RepeaterYear.rb
75
+ test/test_Span.rb
76
+ test/test_Time.rb
77
+ test/test_Token.rb
78
+ test/test_parsing.rb
79
+ ]
80
+ # = MANIFEST =
81
+
82
+ ## Test files will be grabbed from the file list. Make sure the path glob
83
+ ## matches what you actually use.
84
+ s.test_files = s.files.select { |path| path =~ /^test\/test_.*\.rb/ }
85
+ end