horai 0.7.0

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/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ -fs --color
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+
4
+ gem "i18n", ">= 0.6.0"
5
+ gem "activesupport", ">= 3.0.0"
6
+ gem "rparsec-ruby19", ">= 1.0"
7
+
8
+ # Add dependencies to develop your gem here.
9
+ # Include everything needed to run rake, tests, features, etc.
10
+ group :development do
11
+ gem "rspec", "~> 2.8.0"
12
+ gem "rdoc", "~> 3.12"
13
+ gem "jeweler", "~> 1.8.3"
14
+ gem 'guard-shell'
15
+ gem 'guard-rspec'
16
+ gem 'pry'
17
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,55 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activesupport (3.0.12)
5
+ coderay (1.0.6)
6
+ diff-lcs (1.1.3)
7
+ ffi (1.0.11)
8
+ git (1.2.5)
9
+ guard (1.0.1)
10
+ ffi (>= 0.5.0)
11
+ thor (~> 0.14.6)
12
+ guard-rspec (0.7.0)
13
+ guard (>= 0.10.0)
14
+ guard-shell (0.4.0)
15
+ guard (>= 0.2.0)
16
+ i18n (0.6.0)
17
+ jeweler (1.8.3)
18
+ bundler (~> 1.0)
19
+ git (>= 1.2.5)
20
+ rake
21
+ rdoc
22
+ json (1.6.6)
23
+ method_source (0.7.1)
24
+ pry (0.9.9.3)
25
+ coderay (~> 1.0.5)
26
+ method_source (~> 0.7.1)
27
+ slop (>= 2.4.4, < 3)
28
+ rake (0.9.2.2)
29
+ rdoc (3.12)
30
+ json (~> 1.4)
31
+ rparsec-ruby19 (1.0)
32
+ rspec (2.8.0)
33
+ rspec-core (~> 2.8.0)
34
+ rspec-expectations (~> 2.8.0)
35
+ rspec-mocks (~> 2.8.0)
36
+ rspec-core (2.8.0)
37
+ rspec-expectations (2.8.0)
38
+ diff-lcs (~> 1.1.2)
39
+ rspec-mocks (2.8.0)
40
+ slop (2.4.4)
41
+ thor (0.14.6)
42
+
43
+ PLATFORMS
44
+ ruby
45
+
46
+ DEPENDENCIES
47
+ activesupport (>= 3.0.0)
48
+ guard-rspec
49
+ guard-shell
50
+ i18n (>= 0.6.0)
51
+ jeweler (~> 1.8.3)
52
+ pry
53
+ rdoc (~> 3.12)
54
+ rparsec-ruby19 (>= 1.0)
55
+ rspec (~> 2.8.0)
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 AOKI Yuuto
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # What is Horai
2
+
3
+ Horai は日本語の時刻表現をパースし、DateTime型に変換することを目標としている Gem です。(beta)
data/Rakefile ADDED
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "horai"
18
+ gem.homepage = "http://github.com/wneko/horai"
19
+ gem.license = "MIT"
20
+ gem.summary = "日本語の時刻表現をパースし、DateTime型に変換する Gem"
21
+ gem.description = "Horai は日本語の時刻表現をパースし、DateTime型に変換することを目標としている Gem です。"
22
+ gem.email = "aoki@u-ne.co"
23
+ gem.authors = ["AOKI Yuuto"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rspec/core'
29
+ require 'rspec/core/rake_task'
30
+ RSpec::Core::RakeTask.new(:spec) do |spec|
31
+ spec.pattern = FileList['spec/**/*_spec.rb']
32
+ end
33
+
34
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
35
+ spec.pattern = 'spec/**/*_spec.rb'
36
+ spec.rcov = true
37
+ end
38
+
39
+ task :default => :spec
40
+
41
+ require 'rdoc/task'
42
+ Rake::RDocTask.new do |rdoc|
43
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
44
+
45
+ rdoc.rdoc_dir = 'rdoc'
46
+ rdoc.title = "horai #{version}"
47
+ rdoc.rdoc_files.include('README*')
48
+ rdoc.rdoc_files.include('lib/**/*.rb')
49
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.7.0
data/horai.gemspec ADDED
@@ -0,0 +1,76 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "horai"
8
+ s.version = "0.7.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["AOKI Yuuto"]
12
+ s.date = "2012-04-25"
13
+ s.description = "Horai \u{306f}\u{65e5}\u{672c}\u{8a9e}\u{306e}\u{6642}\u{523b}\u{8868}\u{73fe}\u{3092}\u{30d1}\u{30fc}\u{30b9}\u{3057}\u{3001}DateTime\u{578b}\u{306b}\u{5909}\u{63db}\u{3059}\u{308b}\u{3053}\u{3068}\u{3092}\u{76ee}\u{6a19}\u{3068}\u{3057}\u{3066}\u{3044}\u{308b} Gem \u{3067}\u{3059}\u{3002}"
14
+ s.email = "aoki@u-ne.co"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".rspec",
22
+ "Gemfile",
23
+ "Gemfile.lock",
24
+ "LICENSE.txt",
25
+ "README.md",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "horai.gemspec",
29
+ "lib/horai.rb",
30
+ "lib/ja_number.rb",
31
+ "spec/horai_spec.rb",
32
+ "spec/spec_helper.rb"
33
+ ]
34
+ s.homepage = "http://github.com/wneko/horai"
35
+ s.licenses = ["MIT"]
36
+ s.require_paths = ["lib"]
37
+ s.rubygems_version = "1.8.11"
38
+ s.summary = "\u{65e5}\u{672c}\u{8a9e}\u{306e}\u{6642}\u{523b}\u{8868}\u{73fe}\u{3092}\u{30d1}\u{30fc}\u{30b9}\u{3057}\u{3001}DateTime\u{578b}\u{306b}\u{5909}\u{63db}\u{3059}\u{308b} Gem"
39
+
40
+ if s.respond_to? :specification_version then
41
+ s.specification_version = 3
42
+
43
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
44
+ s.add_runtime_dependency(%q<i18n>, [">= 0.6.0"])
45
+ s.add_runtime_dependency(%q<activesupport>, [">= 3.0.0"])
46
+ s.add_runtime_dependency(%q<rparsec-ruby19>, [">= 1.0"])
47
+ s.add_development_dependency(%q<rspec>, ["~> 2.8.0"])
48
+ s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
49
+ s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
50
+ s.add_development_dependency(%q<guard-shell>, [">= 0"])
51
+ s.add_development_dependency(%q<guard-rspec>, [">= 0"])
52
+ s.add_development_dependency(%q<pry>, [">= 0"])
53
+ else
54
+ s.add_dependency(%q<i18n>, [">= 0.6.0"])
55
+ s.add_dependency(%q<activesupport>, [">= 3.0.0"])
56
+ s.add_dependency(%q<rparsec-ruby19>, [">= 1.0"])
57
+ s.add_dependency(%q<rspec>, ["~> 2.8.0"])
58
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
59
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
60
+ s.add_dependency(%q<guard-shell>, [">= 0"])
61
+ s.add_dependency(%q<guard-rspec>, [">= 0"])
62
+ s.add_dependency(%q<pry>, [">= 0"])
63
+ end
64
+ else
65
+ s.add_dependency(%q<i18n>, [">= 0.6.0"])
66
+ s.add_dependency(%q<activesupport>, [">= 3.0.0"])
67
+ s.add_dependency(%q<rparsec-ruby19>, [">= 1.0"])
68
+ s.add_dependency(%q<rspec>, ["~> 2.8.0"])
69
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
70
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
71
+ s.add_dependency(%q<guard-shell>, [">= 0"])
72
+ s.add_dependency(%q<guard-rspec>, [">= 0"])
73
+ s.add_dependency(%q<pry>, [">= 0"])
74
+ end
75
+ end
76
+
data/lib/horai.rb ADDED
@@ -0,0 +1,272 @@
1
+ # coding: utf-8
2
+
3
+ require 'active_support/time'
4
+ require "ja_number"
5
+
6
+ class Horai
7
+ NORMALIZE_FROM = '0-9a-zA-Z:'
8
+ NORMALIZE_TO = '0-9a-zA-Z:'
9
+
10
+ DATE_DELIMITER = (/[\/-]/)
11
+ TIME_DELIMITER = (/[:]/)
12
+
13
+ AFTERNOON_PATTERN = (/午後|ごご|夜|よる|(?<![a-z])pm(?![a-z])/i)
14
+ RELATIVE_KEYWORDS_PATTERN = (/(?<![明午])後(?![年月日])|(?:[経た]っ|し)たら/)
15
+ RELATIVE_PATTERN = (/(\d+)(?:(?:年|ねん)|(?:月|がつ)|(?:日|にち)|(?:分|ふん|ぷん)|(?:時間?|じ(?:かん)?)|(?:秒|びょう))#{RELATIVE_KEYWORDS_PATTERN}/)
16
+
17
+ def self.filter(pattern, type = :absolute, &block)
18
+ @filters = [] if @filters.nil?
19
+ @filters << { pattern: pattern, type => block }
20
+ end
21
+
22
+ def self.truncate_time(date)
23
+ datetime_delta(date, hour: 0, minute: 0, second: 0)
24
+ end
25
+
26
+ def self.datetime_delta(date, methods = {})
27
+ date = date.dup
28
+ methods.each_pair do |key, value|
29
+ converter = 1.0.respond_to?(key) ? :to_f : :to_i;
30
+ date += value.send(converter).send(key) - date.send(key).send(key)
31
+ end
32
+ date
33
+ end
34
+
35
+ def self.register_filters
36
+ dd = DATE_DELIMITER
37
+ td = TIME_DELIMITER
38
+
39
+ # 年 (絶対)
40
+ filter /(\d+)年/, :absolute do |text, matches, date|
41
+ year = year_normalize(matches[1].to_i)
42
+ truncate_time(datetime_delta(date, year: year, month: 1, day: 1))
43
+ end
44
+
45
+ filter /一昨[々昨]年|さきおととし|いっさくさくねん/, :absolute do |text, matches, date|
46
+ truncate_time(datetime_delta(date - 3.year, month: 1, day: 1))
47
+ end
48
+
49
+ filter /一昨年|おととし|いっさくねん/, :absolute do |text, matches, date|
50
+ truncate_time(datetime_delta(date - 2.year, month: 1, day: 1))
51
+ end
52
+
53
+ filter /去年|昨年|きょねん|さくねん/, :absolute do |text, matches, date|
54
+ truncate_time(datetime_delta(date - 1.year, month: 1, day: 1))
55
+ end
56
+
57
+ filter /今年|本年|ことし|ほんねん/, :absolute do |text, matches, date|
58
+ end
59
+
60
+ filter /来年|明年|らいねん|みょうねん/, :absolute do |text, matches, date|
61
+ truncate_time(datetime_delta(date + 1.year, month: 1, day: 1))
62
+ end
63
+
64
+ filter /再来年|明後年|さらいねん|みょうごねん/, :absolute do |text, matches, date|
65
+ truncate_time(datetime_delta(date + 2.year, month: 1, day: 1))
66
+ end
67
+
68
+ filter /明[々明]後年|みょうみょうごねん/, :absolute do |text, matches, date|
69
+ truncate_time(datetime_delta(date + 3.year, month: 1, day: 1))
70
+ end
71
+
72
+ # 月 (絶対)
73
+ filter /(\d+)月/, :absolute do |text, matches, date|
74
+ truncate_time(datetime_delta(date, month: matches[1].to_i, day: 1))
75
+ end
76
+
77
+ filter /[先前][々先前]{3}月|せんせんせんげつ/, :absolute do |text, matches, date|
78
+ truncate_time(datetime_delta(date - 3.month, day: 1))
79
+ end
80
+
81
+ filter /[先前][々先前]月|せんせんげつ/, :absolute do |text, matches, date|
82
+ truncate_time(datetime_delta(date - 2.month, day: 1))
83
+ end
84
+
85
+ filter /先月|前月|せんげつ/, :absolute do |text, matches, date|
86
+ truncate_time(datetime_delta(date - 1.month, day: 1))
87
+ end
88
+
89
+ filter /今月|こんげつ/, :absolute do |text, matches, date|
90
+ end
91
+
92
+ filter /来月|らいげつ/, :absolute do |text, matches, date|
93
+ truncate_time(datetime_delta(date + 1.month, day: 1))
94
+ end
95
+
96
+ filter /再来月|来[々来]月|さらいげつ|らいらいげつ/, :absolute do |text, matches, date|
97
+ truncate_time(datetime_delta(date + 2.month, day: 1))
98
+ end
99
+
100
+ filter /再[々再]来月|ささらいげつ/, :absolute do |text, matches, date|
101
+ truncate_time(datetime_delta(date + 3.month, day: 1))
102
+ end
103
+
104
+ # 日 (絶対)
105
+ filter /(\d+)日/, :absolute do |text, matches, date|
106
+ truncate_time(datetime_delta(date, day: matches[1]))
107
+ end
108
+
109
+ filter /一昨[々昨]日|さきおと[とつ]い|いっさくさくじつ/, :absolute do |text, matches, date|
110
+ truncate_time(date - 3.day)
111
+ end
112
+
113
+ filter /一昨日|おと[とつ]い|いっさくじつ/, :absolute do |text, matches, date|
114
+ truncate_time(date - 2.day)
115
+ end
116
+
117
+ filter /昨日|きのう|さくじつ/, :absolute do |text, matches, date|
118
+ truncate_time(date - 1.day)
119
+ end
120
+
121
+ filter /明日|あした|みょうじつ/, :absolute do |text, matches, date|
122
+ truncate_time(date + 1.day)
123
+ end
124
+
125
+ filter /明後日|あさって|みょうごにち/, :absolute do |text, matches, date|
126
+ truncate_time(date + 2.day)
127
+ end
128
+
129
+ filter /明[々明]後日|しあさって|みょうみょうごにち/, :absolute do |text, matches, date|
130
+ truncate_time(date + 3.day)
131
+ end
132
+
133
+ # 月日表現 (絶対)
134
+ filter /(?<![\d\/-])(?<!\d)(\d{1,2})#{dd}(\d{1,2})(?!#{dd})/, :absolute do |text, matches, date|
135
+ truncate_time(datetime_delta(date, month: matches[1], day: matches[2]))
136
+ end
137
+
138
+ # 年月日表現 (絶対)
139
+ filter /(?<![\d\/-])(\d{1,2}|\d{4})#{dd}(\d{1,2})#{dd}(\d{1,2})(?!#{dd})/, :absolute do |text, matches, date|
140
+ year = year_normalize(matches[1].to_i)
141
+ truncate_time(datetime_delta(date, year: year, month: matches[2], day: matches[3]))
142
+ end
143
+
144
+ # 時分表現 (絶対)
145
+ filter /(?<![\d:])(\d{1,2})#{td}(\d{2})(?!#{td})/, :absolute do |text, matches, date|
146
+ datetime_delta(date, hour: matches[1], minute: matches[2], second: 0)
147
+ end
148
+
149
+ # 時分秒表現 (絶対)
150
+ filter /(?<![\d:])(\d{1,2})#{td}(\d{2})#{td}(\d{2})(?!#{td})/, :absolute do |text, matches, date|
151
+ datetime_delta(date, hour: matches[1], minute: matches[2], second: matches[3])
152
+ end
153
+
154
+ # 時間 (絶対)
155
+ filter /正午/, :absolute do |text, matches, date|
156
+ datetime_delta(date, hour: 12, minute: 0, second: 0)
157
+ end
158
+
159
+ filter /(\d+)時(半)?/, :absolute do |text, matches, date|
160
+ hour = matches[1]
161
+ minute = matches[2] ? 30 : 0
162
+ date = datetime_delta(date, hour: hour, minute: minute, second: 0)
163
+ date += 12.hour if date.hour <= 12 && text =~ AFTERNOON_PATTERN
164
+ date
165
+ end
166
+
167
+ filter /(\d+)分(半)?/, :absolute do |text, matches, date|
168
+ minute = matches[1]
169
+ second = matches[2] ? 30 : 0
170
+ datetime_delta(date, minute: minute, second: second)
171
+ end
172
+
173
+ filter /(\d+)秒/, :absolute do |text, matches, date|
174
+ datetime_delta(date, second: matches[1])
175
+ end
176
+
177
+ # 年 (相対)
178
+ filter /(\d+)年/, :relative do |text, matches, date|
179
+ date + matches[1].to_i.year
180
+ end
181
+
182
+ # 月 (相対)
183
+ filter /(\d+)月/, :relative do |text, matches, date|
184
+ date + matches[1].to_i.month
185
+ end
186
+
187
+ # 日 (相対)
188
+ filter /(\d+)日/, :relative do |text, matches, date|
189
+ date + matches[1].to_f.day
190
+ end
191
+
192
+ # 時間 (相対)
193
+ filter /(\d+)時間(半)?/, :relative do |text, matches, date|
194
+ date + matches[1].to_f.hour + (matches[2] ? 0.5 : 0.0).hour
195
+ end
196
+
197
+ filter /(\d+)分(半)?/, :relative do |text, matches, date|
198
+ date + matches[1].to_f.minute + (matches[2] ? 0.5 : 0.0).minute
199
+ end
200
+
201
+ filter /(\d+)秒/, :relative do |text, matches, date|
202
+ date + matches[1].to_f.second
203
+ end
204
+
205
+ end
206
+
207
+ def self.now
208
+ @now ||= DateTime.now
209
+ @now.dup
210
+ end
211
+
212
+ def self.filters
213
+ return @filters if @filters
214
+ register_filters
215
+ @filters
216
+ end
217
+
218
+ def self.relative?(text)
219
+ text =~ RELATIVE_PATTERN
220
+ end
221
+
222
+ def self.parse(text)
223
+ normalized = normalize(text)
224
+ contexts = (normalized + "$").split(RELATIVE_KEYWORDS_PATTERN)
225
+ date = now
226
+
227
+ filtered = false
228
+
229
+ contexts.each_with_index do |context, index|
230
+ if contexts.size >= 2 && index + 1 != contexts.size
231
+ mode = :relative
232
+ else
233
+ mode = :absolute
234
+ end
235
+
236
+ self.filters.each do |filter|
237
+ if (matches = context.match(filter[:pattern])) && filter[mode]
238
+ date = filter[mode].call(normalized, matches, date)
239
+ filtered = true unless filtered
240
+ end
241
+ end
242
+ end
243
+
244
+ @now = nil
245
+
246
+ return nil unless filtered
247
+ return date
248
+ end
249
+
250
+ def self.normalize(text)
251
+ normalized = text
252
+ normalized.tr!(NORMALIZE_FROM, NORMALIZE_TO)
253
+ digits = "一二三四五六七八九十百千"
254
+ classes = "万億兆"
255
+ normalized.gsub!(/[#{digits}][#{digits}#{classes}]*/) do |match|
256
+ JaNumber::JaNumberParser::parse(match)
257
+ end
258
+ return normalized
259
+ end
260
+
261
+ def self.year_normalize(year)
262
+ if year < 100
263
+ year_xx = (now.year / 100).to_i
264
+ if (year_xx - year).abs < 50
265
+ year += year_xx * 100
266
+ else
267
+ year += (year_xx - 1) * 100
268
+ end
269
+ end
270
+ year
271
+ end
272
+ end
data/lib/ja_number.rb ADDED
@@ -0,0 +1,56 @@
1
+ # coding: utf-8
2
+ # original: http://d.hatena.ne.jp/terazzo/20101226/1293313464
3
+
4
+ module JaNumber
5
+ module Constants
6
+ DIGITS = "一二三四五六七八九"
7
+ CLASSES = "十百千"
8
+ UNITS = "万億兆" # 増やしたければどーぞ
9
+ end
10
+
11
+ module JaNumberParser
12
+ require 'rparsec'
13
+
14
+ # <digit> ::= "一" | "二" | "三" | "四" | "五" | "六" | "七" | "八" | "九"
15
+ digits =
16
+ Constants::DIGITS.split(//).map.with_index {
17
+ |c, i| RParsec::Parsers.char(c).map {|c| 1 + i}
18
+ }.inject(RParsec::Parsers.zero) {|result, p| result.plus(p)}
19
+
20
+ # <class> ::= "千" | "百" | "十"
21
+ classes =
22
+ Constants::CLASSES.split(//).map.with_index {
23
+ |c, i| RParsec::Parsers.char(c).map {|c| 10 ** (1 + i)}
24
+ }.inject(RParsec::Parsers.zero) {|result, p| result.plus(p)}
25
+
26
+ # <unit> ::= "万" | "億" | "兆"
27
+ units =
28
+ Constants::UNITS.split(//).map.with_index {
29
+ |c, i| RParsec::Parsers.char(c).map {|c| 10000 ** (1 + i)}
30
+ }.inject(RParsec::Parsers.zero) {|result, p| result.plus(p)}
31
+
32
+ # <singlet> ::= <digit>
33
+ singlet = digits
34
+
35
+ # <quadruplet> ::= <digit>? <class> <quadruplet>? | <singlet>
36
+ quadruplet = nil
37
+ lazy_quadruplet = RParsec::Parsers.lazy{quadruplet}
38
+ quadruplet =
39
+ RParsec::Parsers.sequence(digits.optional(1), classes, lazy_quadruplet.optional(0)) {
40
+ |digit_value, class_value, next_value| digit_value * class_value + next_value
41
+ } | singlet
42
+
43
+ # <number> ::= <quadruplet> <unit> <number>? | <quadruplet>
44
+ number = nil
45
+ lazy_number = RParsec::Parsers.lazy{number}
46
+ number =
47
+ RParsec::Parsers.sequence(quadruplet, units, lazy_number.optional(0)) {
48
+ |quadruplet_value, unit_value, next_value| quadruplet_value * unit_value + next_value
49
+ } | quadruplet
50
+
51
+ define_method :parse do |ja_number|
52
+ number.parse ja_number
53
+ end
54
+ module_function :parse
55
+ end
56
+ end
@@ -0,0 +1,222 @@
1
+ # coding: utf-8
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
4
+
5
+ def now (year = nil, month = nil, day = nil, hour = nil, minute = nil, second = nil)
6
+ @now ||= Horai::now
7
+
8
+ # puts "{" + [year || @now.year,
9
+ # month || @now.month,
10
+ # day || @now.day,
11
+ # hour || @now.hour,
12
+ # minute || @now.minute,
13
+ # second || @now.second,
14
+ # Rational(9, 24)].join(',') + "}"
15
+
16
+ DateTime.new(year || @now.year,
17
+ month || @now.month,
18
+ day || @now.day,
19
+ hour || @now.hour,
20
+ minute || @now.minute,
21
+ second || @now.second,
22
+ Rational(9, 24))
23
+ end
24
+
25
+ describe Horai do
26
+ context 'normalize' do
27
+ it "number" do
28
+ Horai.normalize('01234').should === '01234'
29
+ end
30
+ it "alphabet" do
31
+ Horai.normalize('abcde').should === 'abcde'
32
+ Horai.normalize('ABCDE').should === 'ABCDE'
33
+ end
34
+ it "numeric kanji" do
35
+ Horai.normalize('十五').should === '15'
36
+ Horai.normalize('十四万二千三百四十五').should === '142345'
37
+ Horai.normalize('百五十時間後').should === '150時間後'
38
+
39
+ pending "漢数字パースライブラリがだめ"
40
+ Horai.normalize('1万秒').should === '10000秒'
41
+ end
42
+ end
43
+
44
+ context 'invalid cases' do
45
+ it 'not even one filter passed' do
46
+ Horai.parse("なにもなし").should be_nil
47
+ end
48
+ end
49
+
50
+ context 'parse absolute' do
51
+ before :each do
52
+ @sample_date = DateTime.new(2012, 4, 11, 12, 45, 30, Rational(9, 24))
53
+ @sample_text = @sample_date.strftime('%Y年%m月%d日の%H時%M分%S秒')
54
+ end
55
+ it "year, month, day, hour, minute, second" do
56
+ time = Horai.parse(@sample_text)
57
+ time.to_s.should === @sample_date.to_s
58
+ end
59
+ it "year" do
60
+ time = Horai.parse("1999年")
61
+ time.to_s.should === now(1999, 1, 1, 0, 0, 0).to_s
62
+ end
63
+ it "month" do
64
+ time = Horai.parse("1月")
65
+ time.to_s.should === now(nil, 1, 1, 0, 0, 0).to_s
66
+ end
67
+ it "half time" do
68
+ time = Horai.parse("1時半")
69
+ time.to_s.should === now(nil, nil, nil, 1, 30, 0).to_s
70
+ end
71
+ it "half minute" do
72
+ time = Horai.parse("1分半後")
73
+ time.to_s.should === (now + 1.5.minute).to_s
74
+ end
75
+ it "half hour and minute" do
76
+ time = Horai.parse("5時間半と1分半後")
77
+ time.to_s.should === (now + 5.5.hour + 1.5.minute).to_s
78
+ end
79
+ it "at night" do
80
+ time = Horai.parse("夜の8時に")
81
+ time.to_s.should === (now(nil, nil, nil, 20, 0, 0)).to_s
82
+ end
83
+ it "at afternoon" do
84
+ time = Horai.parse("午後一時に")
85
+ time.to_s.should === (now(nil, nil, nil, 13, 0, 0)).to_s
86
+ end
87
+ it "at afternoon" do
88
+ time = Horai.parse("PM1時に")
89
+ time.to_s.should === (now(nil, nil, nil, 13, 0, 0)).to_s
90
+ end
91
+ it "at AM and contains *pm*" do
92
+ time = Horai.parse("rpm 6時に")
93
+ time.to_s.should === (now(nil, nil, nil, 6, 0, 0)).to_s
94
+ end
95
+ it "at noon" do
96
+ time = Horai.parse("10日の正午に")
97
+ time.to_s.should === (now(nil, nil, 10, 12, 0, 0)).to_s
98
+ end
99
+ it "at hh:mm" do
100
+ time = Horai.parse("10:20")
101
+ time.to_s.should === (now(nil, nil, nil, 10, 20, 0)).to_s
102
+ end
103
+ it "at hh:mm:ss" do
104
+ time = Horai.parse("10:20:30")
105
+ time.to_s.should === (now(nil, nil, nil, 10, 20, 30)).to_s
106
+ end
107
+ it "at MM/DD" do
108
+ time = Horai.parse("10/20")
109
+ time.to_s.should === now(nil, 10, 20, 0, 0, 0).to_s
110
+ end
111
+ it "at YYYY/MM/DD" do
112
+ time = Horai.parse("2000/10/20")
113
+ time.to_s.should === now(2000, 10, 20, 0, 0, 0).to_s
114
+ end
115
+ it "at YY/MM/DD" do
116
+ time = Horai.parse("10/10/20")
117
+ time.to_s.should === now(2010, 10, 20, 0, 0, 0).to_s
118
+ end
119
+ it "at YYYY/MM/DD hh:mm:ss" do
120
+ time = Horai.parse("2000/10/20 12:30:40")
121
+ time.to_s.should === now(2000, 10, 20, 12, 30, 40).to_s
122
+ end
123
+ it "at YY (near current year)" do
124
+ time = Horai.parse("10年")
125
+ time.to_s.should === now(2010, 1, 1, 0, 0, 0).to_s
126
+ end
127
+ it "at YY (near feature)" do
128
+ time = Horai.parse("30年")
129
+ time.to_s.should === now(2030, 1, 1, 0, 0, 0).to_s
130
+ end
131
+ it "at YY (not near feature)" do
132
+ time = Horai.parse("90年")
133
+ time.to_s.should === now(1990, 1, 1, 0, 0, 0).to_s
134
+ end
135
+ end
136
+
137
+ context 'parse relative' do
138
+ it "check" do
139
+ Horai.relative?("10分後").should be_true
140
+ Horai.relative?("10分経ったら").should be_true
141
+ Horai.relative?("10分したら").should be_true
142
+ Horai.relative?("10分").should be_false
143
+ Horai.relative?("10時10分").should be_false
144
+ end
145
+ it "next year" do
146
+ time = Horai.parse("来年")
147
+ time.to_s.should === (now(nil, 1, 1, 0, 0, 0) + 1.year).to_s
148
+ end
149
+ it "next month" do
150
+ time = Horai.parse("来月")
151
+ time.to_s.should === (now(nil, nil, 1, 0, 0, 0) + 1.month).to_s
152
+ end
153
+ it "tomorrow" do
154
+ time = Horai.parse("明日")
155
+ time.to_s.should === (now(nil, nil, nil, 0, 0, 0) + 1.day).to_s
156
+ end
157
+ it "day after tomorrow" do
158
+ time = Horai.parse("明後日")
159
+ time.to_s.should === (now(nil, nil, nil, 0, 0, 0) + 2.day).to_s
160
+ end
161
+ it "yesterday" do
162
+ time = Horai.parse("昨日")
163
+ time.to_s.should === (now(nil, nil, nil, 0, 0, 0) - 1.day).to_s
164
+ end
165
+ it "numeric year after and absolute month" do
166
+ time = Horai.parse("10年後の8月")
167
+ time.to_s.should === (now(nil, 8, 1, 0, 0, 0) + 10.year).to_s
168
+ end
169
+ it "numeric minute after" do
170
+ time = Horai.parse("10分後")
171
+ time.to_s.should === (now + 10.minute).to_s
172
+ end
173
+ it "numeric day after" do
174
+ time = Horai.parse("10日後")
175
+ time.to_s.should === (now + 10.day).to_s
176
+ end
177
+ it "tomorrow and absolute time" do
178
+ time = Horai.parse("明日の10時")
179
+ time.to_s.should === (now(nil, nil, nil, 10, 0, 0) + 1.day).to_s
180
+ end
181
+ it "tomorrow and absolute hh:mm:ss" do
182
+ time = Horai.parse("明日の10:20:30")
183
+ time.to_s.should === (now(nil, nil, nil, 10, 20, 30) + 1.day).to_s
184
+ end
185
+ it "tomorrow and afternoon" do
186
+ time = Horai.parse("明日の午後5時")
187
+ time.to_s.should === (now(nil, nil, nil, 17, 0, 0) + 1.day).to_s
188
+ end
189
+ it "tomorrow and noon" do
190
+ time = Horai.parse("明日の正午")
191
+ time.to_s.should === (now(nil, nil, nil, 12, 0, 0) + 1.day).to_s
192
+ end
193
+ it "numeric day after and absolute time" do
194
+ time = Horai.parse("3日後の12時")
195
+ time.to_s.should === (now(nil, nil, nil, 12, 0, 0) + 3.day).to_s
196
+ end
197
+ it "numeric day after and absolute time" do
198
+ time = Horai.parse("3日後の12時45分")
199
+ time.to_s.should === (now(nil, nil, nil, 12, 45, 0) + 3.day).to_s
200
+ end
201
+ it "numeric day after and absolute hh:mm:ss" do
202
+ time = Horai.parse("3日後の12:45:55")
203
+ time.to_s.should === (now(nil, nil, nil, 12, 45, 55) + 3.day).to_s
204
+ end
205
+ it "numeric day after and relative time" do
206
+ time = Horai.parse("3日12時間45分後")
207
+ time.to_s.should === (now + 3.day + 12.hour + 45.minute).to_s
208
+ end
209
+ it "half time after" do
210
+ time = Horai.parse("1時間半後")
211
+ time.to_s.should === (now + 1.5.hour).to_s
212
+ end
213
+ it "half minute after" do
214
+ time = Horai.parse("1分半後")
215
+ time.to_s.should === (now + 1.5.minute).to_s
216
+ end
217
+ it "half hour and half minute after" do
218
+ time = Horai.parse("5時間半と1分半後")
219
+ time.to_s.should === (now + 5.5.hour + 1.5.minute).to_s
220
+ end
221
+ end
222
+ end
@@ -0,0 +1,12 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'horai'
5
+
6
+ # Requires supporting files with custom matchers and macros, etc,
7
+ # in ./support/ and its subdirectories.
8
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
9
+
10
+ RSpec.configure do |config|
11
+
12
+ end
metadata ADDED
@@ -0,0 +1,162 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: horai
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.7.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - AOKI Yuuto
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-04-25 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: i18n
16
+ requirement: &2157979040 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.6.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2157979040
25
+ - !ruby/object:Gem::Dependency
26
+ name: activesupport
27
+ requirement: &2157978480 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 3.0.0
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *2157978480
36
+ - !ruby/object:Gem::Dependency
37
+ name: rparsec-ruby19
38
+ requirement: &2157977980 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '1.0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *2157977980
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec
49
+ requirement: &2157977440 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 2.8.0
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *2157977440
58
+ - !ruby/object:Gem::Dependency
59
+ name: rdoc
60
+ requirement: &2157976920 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: '3.12'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *2157976920
69
+ - !ruby/object:Gem::Dependency
70
+ name: jeweler
71
+ requirement: &2157976440 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ version: 1.8.3
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *2157976440
80
+ - !ruby/object:Gem::Dependency
81
+ name: guard-shell
82
+ requirement: &2157975860 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *2157975860
91
+ - !ruby/object:Gem::Dependency
92
+ name: guard-rspec
93
+ requirement: &2157975360 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ type: :development
100
+ prerelease: false
101
+ version_requirements: *2157975360
102
+ - !ruby/object:Gem::Dependency
103
+ name: pry
104
+ requirement: &2157974880 !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: *2157974880
113
+ description: Horai は日本語の時刻表現をパースし、DateTime型に変換することを目標としている Gem です。
114
+ email: aoki@u-ne.co
115
+ executables: []
116
+ extensions: []
117
+ extra_rdoc_files:
118
+ - LICENSE.txt
119
+ - README.md
120
+ files:
121
+ - .document
122
+ - .rspec
123
+ - Gemfile
124
+ - Gemfile.lock
125
+ - LICENSE.txt
126
+ - README.md
127
+ - Rakefile
128
+ - VERSION
129
+ - horai.gemspec
130
+ - lib/horai.rb
131
+ - lib/ja_number.rb
132
+ - spec/horai_spec.rb
133
+ - spec/spec_helper.rb
134
+ homepage: http://github.com/wneko/horai
135
+ licenses:
136
+ - MIT
137
+ post_install_message:
138
+ rdoc_options: []
139
+ require_paths:
140
+ - lib
141
+ required_ruby_version: !ruby/object:Gem::Requirement
142
+ none: false
143
+ requirements:
144
+ - - ! '>='
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ segments:
148
+ - 0
149
+ hash: -3122584729073349609
150
+ required_rubygems_version: !ruby/object:Gem::Requirement
151
+ none: false
152
+ requirements:
153
+ - - ! '>='
154
+ - !ruby/object:Gem::Version
155
+ version: '0'
156
+ requirements: []
157
+ rubyforge_project:
158
+ rubygems_version: 1.8.11
159
+ signing_key:
160
+ specification_version: 3
161
+ summary: 日本語の時刻表現をパースし、DateTime型に変換する Gem
162
+ test_files: []