gitlab_chronic_duration 0.10.6.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2a08e26fec03acc92f7690fe833452920d67d6c28bda9b5d106e020fc3fef390
4
+ data.tar.gz: bcd9abbbb3e510283748ebc7566705c32cd97f18f0c052b92f3b262fcccf7d94
5
+ SHA512:
6
+ metadata.gz: c86d746140760a7785907f2e19d263bbfe53bbc2a10987246990035a0f391ec2995f534f9ab7c377ca6dccea41178063b600e65e54e0e26831c1bfd6141d60ea
7
+ data.tar.gz: 60f11fe463617d2853a37a3268af643e28785aad1d689f78b49ae718fc7d1db1e35cf230e240e18fbe367e571b58a51ad2b05a34e90adb8dcc89efe8f6dd27f7
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,12 @@
1
+ image: "ruby:2.5"
2
+
3
+ before_script:
4
+ - ruby -v
5
+ - which ruby
6
+ - gem install bundler
7
+ - bundle install --jobs $(nproc) "${FLAGS[@]}"
8
+
9
+ rspec:
10
+ stage: test
11
+ script:
12
+ - bundle exec rspec
@@ -0,0 +1,4 @@
1
+ ## v0.10.6.1
2
+
3
+ - Allow to pass `days_per_month`, `hours_per_day` as part of `opts={}` for `.parse` and `.output`. For internal calculations, use `days_per_month` to calculate `days_per_week`. Replace `days_per_week` getter/setter with `days_per_month` !2
4
+ - Configure GitLab CI !1
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) Henry Poydar
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,81 @@
1
+ [![Build Status](https://travis-ci.org/hpoydar/chronic_duration.png?branch=master)](https://travis-ci.org/hpoydar/chronic_duration)
2
+
3
+ # Chronic Duration
4
+
5
+ A simple Ruby natural language parser for elapsed time. (For example, 4 hours and 30 minutes, 6 minutes 4 seconds, 3 days, etc.) Returns all results in seconds. Will return an integer unless you get tricky and need a float. (4 minutes and 13.47 seconds, for example.)
6
+
7
+ The reverse can also be accomplished with the output method. So pass in seconds and you can get strings like 4 mins 31.51 secs (default format), 4h 3m 30s, or 4:01:29.
8
+
9
+ ## Usage
10
+
11
+ >> require 'chronic_duration'
12
+ => true
13
+ >> ChronicDuration.parse('4 minutes and 30 seconds')
14
+ => 270
15
+ >> ChronicDuration.parse('0 seconds')
16
+ => nil
17
+ >> ChronicDuration.parse('0 seconds', :keep_zero => true)
18
+ => 0
19
+ >> ChronicDuration.output(270)
20
+ => 4 mins 30 secs
21
+ >> ChronicDuration.output(0)
22
+ => nil
23
+ >> ChronicDuration.output(0, :keep_zero => true)
24
+ => 0 secs
25
+ >> ChronicDuration.output(270, :format => :short)
26
+ => 4m 30s
27
+ >> ChronicDuration.output(270, :format => :long)
28
+ => 4 minutes 30 seconds
29
+ >> ChronicDuration.output(270, :format => :chrono)
30
+ => 4:30
31
+ >> ChronicDuration.output(1299600, :weeks => true)
32
+ => 2 wks 1 day 1 hr
33
+ >> ChronicDuration.output(1299600, :weeks => true, :units => 2)
34
+ => 2 wks 1 day
35
+ >> ChronicDuration.output(45*24*60*60 + 15*60, :limit_to_hours => true)
36
+ => 1080 hrs 15 mins
37
+ >> ChronicDuration.output(1299600, :weeks => true, :units => 2, :joiner => ', ')
38
+ => 2 wks, 1 day
39
+ >> ChronicDuration.output(1296000)
40
+ => 15 days
41
+
42
+ Nil is returned if the string can't be parsed
43
+
44
+ Examples of parse-able strings:
45
+
46
+ * '12.4 secs'
47
+ * '1:20'
48
+ * '1:20.51'
49
+ * '4:01:01'
50
+ * '3 mins 4 sec'
51
+ * '2 hrs 20 min'
52
+ * '2h20min'
53
+ * '6 mos 1 day'
54
+ * '47 yrs 6 mos and 4d'
55
+ * 'two hours and twenty minutes'
56
+ * '3 weeks and 2 days'
57
+
58
+ ChronicDuration.raise_exceptions can be set to true to raise exceptions when the string can't be parsed.
59
+
60
+ >> ChronicDuration.raise_exceptions = true
61
+ => true
62
+ >> ChronicDuration.parse('4 elephants and 3 Astroids')
63
+ ChronicDuration::DurationParseError: An invalid word "elephants" was used in the string to be parsed.
64
+
65
+ ## Contributing
66
+
67
+ Fork and pull request after your specs are green. Add your handle to the list below.
68
+ Also looking for additional maintainers.
69
+
70
+ ## Contributors
71
+
72
+ errm,pdf, brianjlandau, jduff, olauzon, roboman, ianlevesque, bolandrm
73
+
74
+ ## TODO
75
+
76
+ * Benchmark, optimize
77
+ * Context specific matching (E.g., for '4m30s', assume 'm' is minutes not months)
78
+ * Smartly parse vacation-like durations (E.g., '4 days and 3 nights')
79
+ * :chrono output option should probably change to something like 4 days 4:00:12 instead of 4:04:00:12
80
+ * Other locale support
81
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new('spec')
5
+ task :default => :spec
6
+
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'chronic_duration/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+
8
+ gem.name = "gitlab_chronic_duration"
9
+ gem.version = ChronicDuration::VERSION
10
+ gem.authors = ["hpoydar"]
11
+ gem.email = ["henry@poydar.com"]
12
+ gem.description = %q{A simple Ruby natural language parser for elapsed time. (For example, 4 hours and 30 minutes, 6 minutes 4 seconds, 3 days, etc.) Returns all results in seconds. Will return an integer unless you get tricky and need a float. (4 minutes and 13.47 seconds, for example.) The reverse can also be performed via the output method.}
13
+ gem.summary = %q{A simple Ruby natural language parser for elapsed time}
14
+ gem.homepage = "https://gitlab.com/gitlab-org/gitlab-chronic-duration"
15
+ gem.license = "MIT"
16
+
17
+ gem.files = `git ls-files`.split($/)
18
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
19
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
20
+ gem.require_paths = ["lib"]
21
+
22
+ gem.add_runtime_dependency "numerizer", "~> 0.1.1"
23
+
24
+ gem.add_development_dependency "rake", "~> 10.0.3"
25
+ gem.add_development_dependency "rspec", "~> 2.12.0"
26
+
27
+
28
+ end
@@ -0,0 +1,5 @@
1
+ module ChronicDuration
2
+ # '0.10.6' is the versioning used in 'https://github.com/henrypoydar/chronic_duration',
3
+ # we are adding revision part to highlight GitLab changes, e.g. '0.10.6.N'
4
+ VERSION = '0.10.6.1'.freeze
5
+ end
@@ -0,0 +1,302 @@
1
+ require 'numerizer' unless defined?(Numerizer)
2
+
3
+ module ChronicDuration
4
+
5
+ extend self
6
+
7
+ class DurationParseError < StandardError
8
+ end
9
+
10
+ # On average, there's a little over 4 weeks in month.
11
+ FULL_WEEKS_PER_MONTH = 4
12
+
13
+ @@raise_exceptions = false
14
+ @@hours_per_day = 24
15
+ @@days_per_month = 30
16
+
17
+ def self.raise_exceptions
18
+ !!@@raise_exceptions
19
+ end
20
+
21
+ def self.raise_exceptions=(value)
22
+ @@raise_exceptions = !!value
23
+ end
24
+
25
+ def self.hours_per_day
26
+ @@hours_per_day
27
+ end
28
+
29
+ def self.hours_per_day=(value)
30
+ @@hours_per_day = value
31
+ end
32
+
33
+ def self.days_per_month
34
+ @@days_per_month
35
+ end
36
+
37
+ def self.days_per_month=(value)
38
+ @@days_per_month = value
39
+ end
40
+
41
+ # Given a string representation of elapsed time,
42
+ # return an integer (or float, if fractions of a
43
+ # second are input)
44
+ def parse(string, opts = {})
45
+ result = calculate_from_words(cleanup(string), opts)
46
+ (!opts[:keep_zero] and result == 0) ? nil : result
47
+ end
48
+
49
+ # Given an integer and an optional format,
50
+ # returns a formatted string representing elapsed time
51
+ def output(seconds, opts = {})
52
+ int = seconds.to_i
53
+ seconds = int if seconds - int == 0 # if seconds end with .0
54
+
55
+ opts[:format] ||= :default
56
+ opts[:keep_zero] ||= false
57
+
58
+ hours_per_day = opts[:hours_per_day] || ChronicDuration.hours_per_day
59
+ days_per_month = opts[:days_per_month] || ChronicDuration.days_per_month
60
+ days_per_week = days_per_month / FULL_WEEKS_PER_MONTH
61
+
62
+ years = months = weeks = days = hours = minutes = 0
63
+
64
+ decimal_places = seconds.to_s.split('.').last.length if seconds.is_a?(Float)
65
+
66
+ minute = 60
67
+ hour = 60 * minute
68
+ day = hours_per_day * hour
69
+ month = days_per_month * day
70
+ year = 31557600
71
+
72
+ if seconds >= 31557600 && seconds%year < seconds%month
73
+ years = seconds / year
74
+ months = seconds % year / month
75
+ days = seconds % year % month / day
76
+ hours = seconds % year % month % day / hour
77
+ minutes = seconds % year % month % day % hour / minute
78
+ seconds = seconds % year % month % day % hour % minute
79
+ elsif seconds >= 60
80
+ minutes = (seconds / 60).to_i
81
+ seconds = seconds % 60
82
+ if minutes >= 60
83
+ hours = (minutes / 60).to_i
84
+ minutes = (minutes % 60).to_i
85
+ if !opts[:limit_to_hours]
86
+ if hours >= hours_per_day
87
+ days = (hours / hours_per_day).to_i
88
+ hours = (hours % hours_per_day).to_i
89
+ if opts[:weeks]
90
+ if days >= days_per_week
91
+ weeks = (days / days_per_week).to_i
92
+ days = (days % days_per_week).to_i
93
+ if weeks >= FULL_WEEKS_PER_MONTH
94
+ months = (weeks / FULL_WEEKS_PER_MONTH).to_i
95
+ weeks = (weeks % FULL_WEEKS_PER_MONTH).to_i
96
+ end
97
+ end
98
+ else
99
+ if days >= days_per_month
100
+ months = (days / days_per_month).to_i
101
+ days = (days % days_per_month).to_i
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ joiner = opts.fetch(:joiner) { ' ' }
110
+ process = nil
111
+
112
+ case opts[:format]
113
+ when :micro
114
+ dividers = {
115
+ :years => 'y', :months => 'mo', :weeks => 'w', :days => 'd', :hours => 'h', :minutes => 'm', :seconds => 's' }
116
+ joiner = ''
117
+ when :short
118
+ dividers = {
119
+ :years => 'y', :months => 'mo', :weeks => 'w', :days => 'd', :hours => 'h', :minutes => 'm', :seconds => 's' }
120
+ when :default
121
+ dividers = {
122
+ :years => ' yr', :months => ' mo', :weeks => ' wk', :days => ' day', :hours => ' hr', :minutes => ' min', :seconds => ' sec',
123
+ :pluralize => true }
124
+ when :long
125
+ dividers = {
126
+ :years => ' year', :months => ' month', :weeks => ' week', :days => ' day', :hours => ' hour', :minutes => ' minute', :seconds => ' second',
127
+ :pluralize => true }
128
+ when :chrono
129
+ dividers = {
130
+ :years => ':', :months => ':', :weeks => ':', :days => ':', :hours => ':', :minutes => ':', :seconds => ':', :keep_zero => true }
131
+ process = lambda do |str|
132
+ # Pad zeros
133
+ # Get rid of lead off times if they are zero
134
+ # Get rid of lead off zero
135
+ # Get rid of trailing :
136
+ divider = ':'
137
+ str.split(divider).map { |n|
138
+ # add zeros only if n is an integer
139
+ n.include?('.') ? ("%04.#{decimal_places}f" % n) : ("%02d" % n)
140
+ }.join(divider).gsub(/^(00:)+/, '').gsub(/^0/, '').gsub(/:$/, '')
141
+ end
142
+ joiner = ''
143
+ end
144
+
145
+ result = [:years, :months, :weeks, :days, :hours, :minutes, :seconds].map do |t|
146
+ next if t == :weeks && !opts[:weeks]
147
+ num = eval(t.to_s)
148
+ num = ("%.#{decimal_places}f" % num) if num.is_a?(Float) && t == :seconds
149
+ keep_zero = dividers[:keep_zero]
150
+ keep_zero ||= opts[:keep_zero] if t == :seconds
151
+ humanize_time_unit( num, dividers[t], dividers[:pluralize], keep_zero )
152
+ end.compact!
153
+
154
+ result = result[0...opts[:units]] if opts[:units]
155
+
156
+ result = result.join(joiner)
157
+
158
+ if process
159
+ result = process.call(result)
160
+ end
161
+
162
+ result.length == 0 ? nil : result
163
+
164
+ end
165
+
166
+ private
167
+
168
+ def humanize_time_unit(number, unit, pluralize, keep_zero)
169
+ return nil if number == 0 && !keep_zero
170
+ res = "#{number}#{unit}"
171
+ # A poor man's pluralizer
172
+ res << 's' if !(number == 1) && pluralize
173
+ res
174
+ end
175
+
176
+ def calculate_from_words(string, opts)
177
+ val = 0
178
+ words = string.split(' ')
179
+ words.each_with_index do |v, k|
180
+ if v =~ float_matcher
181
+ val += (convert_to_number(v) * duration_units_seconds_multiplier(words[k + 1] || (opts[:default_unit] || 'seconds'), opts))
182
+ end
183
+ end
184
+ val
185
+ end
186
+
187
+ def cleanup(string)
188
+ res = string.downcase
189
+ res = filter_by_type(Numerizer.numerize(res))
190
+ res = res.gsub(float_matcher) {|n| " #{n} "}.squeeze(' ').strip
191
+ res = filter_through_white_list(res)
192
+ end
193
+
194
+ def convert_to_number(string)
195
+ string.to_f % 1 > 0 ? string.to_f : string.to_i
196
+ end
197
+
198
+ def duration_units_list
199
+ %w(seconds minutes hours days weeks months years)
200
+ end
201
+
202
+ def duration_units_seconds_multiplier(unit, opts)
203
+ return 0 unless duration_units_list.include?(unit)
204
+
205
+ hours_per_day = opts[:hours_per_day] || ChronicDuration.hours_per_day
206
+ days_per_month = opts[:days_per_month] || ChronicDuration.days_per_month
207
+ days_per_week = days_per_month / FULL_WEEKS_PER_MONTH
208
+
209
+ case unit
210
+ when 'years'; 31557600
211
+ when 'months'; 3600 * hours_per_day * days_per_month
212
+ when 'weeks'; 3600 * hours_per_day * days_per_week
213
+ when 'days'; 3600 * hours_per_day
214
+ when 'hours'; 3600
215
+ when 'minutes'; 60
216
+ when 'seconds'; 1
217
+ end
218
+ end
219
+
220
+ # Parse 3:41:59 and return 3 hours 41 minutes 59 seconds
221
+ def filter_by_type(string)
222
+ chrono_units_list = duration_units_list.reject {|v| v == "weeks"}
223
+ if string.gsub(' ', '') =~ /#{float_matcher}(:#{float_matcher})+/
224
+ res = []
225
+ string.gsub(' ', '').split(':').reverse.each_with_index do |v,k|
226
+ return unless chrono_units_list[k]
227
+ res << "#{v} #{chrono_units_list[k]}"
228
+ end
229
+ res = res.reverse.join(' ')
230
+ else
231
+ res = string
232
+ end
233
+ res
234
+ end
235
+
236
+ def float_matcher
237
+ /[0-9]*\.?[0-9]+/
238
+ end
239
+
240
+ # Get rid of unknown words and map found
241
+ # words to defined time units
242
+ def filter_through_white_list(string)
243
+ res = []
244
+ string.split(' ').each do |word|
245
+ if word =~ float_matcher
246
+ res << word.strip
247
+ next
248
+ end
249
+ stripped_word = word.strip.gsub(/^,/, '').gsub(/,$/, '')
250
+ if mappings.has_key?(stripped_word)
251
+ res << mappings[stripped_word]
252
+ elsif !join_words.include?(stripped_word) and ChronicDuration.raise_exceptions
253
+ raise DurationParseError, "An invalid word #{word.inspect} was used in the string to be parsed."
254
+ end
255
+ end
256
+ # add '1' at front if string starts with something recognizable but not with a number, like 'day' or 'minute 30sec'
257
+ res.unshift(1) if res.length > 0 && mappings[res[0]]
258
+ res.join(' ')
259
+ end
260
+
261
+ def mappings
262
+ {
263
+ 'seconds' => 'seconds',
264
+ 'second' => 'seconds',
265
+ 'secs' => 'seconds',
266
+ 'sec' => 'seconds',
267
+ 's' => 'seconds',
268
+ 'minutes' => 'minutes',
269
+ 'minute' => 'minutes',
270
+ 'mins' => 'minutes',
271
+ 'min' => 'minutes',
272
+ 'm' => 'minutes',
273
+ 'hours' => 'hours',
274
+ 'hour' => 'hours',
275
+ 'hrs' => 'hours',
276
+ 'hr' => 'hours',
277
+ 'h' => 'hours',
278
+ 'days' => 'days',
279
+ 'day' => 'days',
280
+ 'dy' => 'days',
281
+ 'd' => 'days',
282
+ 'weeks' => 'weeks',
283
+ 'week' => 'weeks',
284
+ 'wks' => 'weeks',
285
+ 'wk' => 'weeks',
286
+ 'w' => 'weeks',
287
+ 'months' => 'months',
288
+ 'mo' => 'months',
289
+ 'mos' => 'months',
290
+ 'month' => 'months',
291
+ 'years' => 'years',
292
+ 'year' => 'years',
293
+ 'yrs' => 'years',
294
+ 'yr' => 'years',
295
+ 'y' => 'years'
296
+ }
297
+ end
298
+
299
+ def join_words
300
+ ['and', 'with', 'plus']
301
+ end
302
+ end
@@ -0,0 +1,331 @@
1
+ require 'spec_helper'
2
+
3
+ describe ChronicDuration do
4
+
5
+ describe ".parse" do
6
+
7
+ @exemplars = {
8
+ '1:20' => 60 + 20,
9
+ '1:20.51' => 60 + 20.51,
10
+ '4:01:01' => 4 * 3600 + 60 + 1,
11
+ '3 mins 4 sec' => 3 * 60 + 4,
12
+ '3 Mins 4 Sec' => 3 * 60 + 4,
13
+ 'three mins four sec' => 3 * 60 + 4,
14
+ '2 hrs 20 min' => 2 * 3600 + 20 * 60,
15
+ '2h20min' => 2 * 3600 + 20 * 60,
16
+ '6 mos 1 day' => 6 * 30 * 24 * 3600 + 24 * 3600,
17
+ '1 year 6 mos 1 day' => 1 * 31557600 + 6 * 30 * 24 * 3600 + 24 * 3600,
18
+ '2.5 hrs' => 2.5 * 3600,
19
+ '47 yrs 6 mos and 4.5d' => 47 * 31557600 + 6 * 30 * 24 * 3600 + 4.5 * 24 * 3600,
20
+ 'two hours and twenty minutes' => 2 * 3600 + 20 * 60,
21
+ 'four hours and forty minutes' => 4 * 3600 + 40 * 60,
22
+ 'four hours, and fourty minutes' => 4 * 3600 + 40 * 60,
23
+ '3 weeks and, 2 days' => 3600 * 24 * 7 * 3 + 3600 * 24 * 2,
24
+ '3 weeks, plus 2 days' => 3600 * 24 * 7 * 3 + 3600 * 24 * 2,
25
+ '3 weeks with 2 days' => 3600 * 24 * 7 * 3 + 3600 * 24 * 2,
26
+ '1 month' => 3600 * 24 * 30,
27
+ '2 months' => 3600 * 24 * 30 * 2,
28
+ '18 months' => 3600 * 24 * 30 * 18,
29
+ '1 year 6 months' => (3600 * 24 * (365.25 + 6 * 30)).to_i,
30
+ 'day' => 3600 * 24,
31
+ 'minute 30s' => 90
32
+ }
33
+
34
+ context "when string can't be parsed" do
35
+
36
+ it "returns nil" do
37
+ ChronicDuration.parse('gobblygoo').should be_nil
38
+ end
39
+
40
+ it "cannot parse zero" do
41
+ ChronicDuration.parse('0').should be_nil
42
+ end
43
+
44
+ context "when @@raise_exceptions set to true" do
45
+
46
+ it "raises with ChronicDuration::DurationParseError" do
47
+ ChronicDuration.raise_exceptions = true
48
+ expect { ChronicDuration.parse('23 gobblygoos') }.to raise_error(ChronicDuration::DurationParseError)
49
+ ChronicDuration.raise_exceptions = false
50
+ end
51
+
52
+ end
53
+
54
+ end
55
+
56
+ it "should return zero if the string parses as zero and the keep_zero option is true" do
57
+ ChronicDuration.parse('0', :keep_zero => true).should == 0
58
+ end
59
+
60
+ it "should return a float if seconds are in decimals" do
61
+ ChronicDuration.parse('12 mins 3.141 seconds').is_a?(Float).should be_true
62
+ end
63
+
64
+ it "should return an integer unless the seconds are in decimals" do
65
+ ChronicDuration.parse('12 mins 3 seconds').is_a?(Integer).should be_true
66
+ end
67
+
68
+ it "should be able to parse minutes by default" do
69
+ ChronicDuration.parse('5', :default_unit => "minutes").should == 300
70
+ end
71
+
72
+ @exemplars.each do |k, v|
73
+ it "parses a duration like #{k}" do
74
+ ChronicDuration.parse(k).should == v
75
+ end
76
+ end
77
+
78
+ context 'with :hours_per_day and :days_per_month params' do
79
+ it 'uses provided :hours_per_day' do
80
+ ChronicDuration.parse('1d', hours_per_day: 24).should == 24 * 60 * 60
81
+ ChronicDuration.parse('1d', hours_per_day: 8).should == 8 * 60 * 60
82
+ end
83
+
84
+ it 'uses provided :days_per_month' do
85
+ ChronicDuration.parse('1mo', days_per_month: 30).should == 30 * 24 * 60 * 60
86
+ ChronicDuration.parse('1mo', days_per_month: 20).should == 20 * 24 * 60 * 60
87
+
88
+ ChronicDuration.parse('1w', days_per_month: 30).should == 7 * 24 * 60 * 60
89
+ ChronicDuration.parse('1w', days_per_month: 20).should == 5 * 24 * 60 * 60
90
+ end
91
+
92
+ it 'uses provided both :hours_per_day and :days_per_month' do
93
+ ChronicDuration.parse('1mo', days_per_month: 30, hours_per_day: 24).should == 30 * 24 * 60 * 60
94
+ ChronicDuration.parse('1mo', days_per_month: 20, hours_per_day: 8).should == 20 * 8 * 60 * 60
95
+
96
+ ChronicDuration.parse('1w', days_per_month: 30, hours_per_day: 24).should == 7 * 24 * 60 * 60
97
+ ChronicDuration.parse('1w', days_per_month: 20, hours_per_day: 8).should == 5 * 8 * 60 * 60
98
+ end
99
+ end
100
+ end
101
+
102
+ describe '.output' do
103
+
104
+ @exemplars = {
105
+ (60 + 20) =>
106
+ {
107
+ :micro => '1m20s',
108
+ :short => '1m 20s',
109
+ :default => '1 min 20 secs',
110
+ :long => '1 minute 20 seconds',
111
+ :chrono => '1:20'
112
+ },
113
+ (60 + 20.51) =>
114
+ {
115
+ :micro => '1m20.51s',
116
+ :short => '1m 20.51s',
117
+ :default => '1 min 20.51 secs',
118
+ :long => '1 minute 20.51 seconds',
119
+ :chrono => '1:20.51'
120
+ },
121
+ (60 + 20.51928) =>
122
+ {
123
+ :micro => '1m20.51928s',
124
+ :short => '1m 20.51928s',
125
+ :default => '1 min 20.51928 secs',
126
+ :long => '1 minute 20.51928 seconds',
127
+ :chrono => '1:20.51928'
128
+ },
129
+ (4 * 3600 + 60 + 1) =>
130
+ {
131
+ :micro => '4h1m1s',
132
+ :short => '4h 1m 1s',
133
+ :default => '4 hrs 1 min 1 sec',
134
+ :long => '4 hours 1 minute 1 second',
135
+ :chrono => '4:01:01'
136
+ },
137
+ (2 * 3600 + 20 * 60) =>
138
+ {
139
+ :micro => '2h20m',
140
+ :short => '2h 20m',
141
+ :default => '2 hrs 20 mins',
142
+ :long => '2 hours 20 minutes',
143
+ :chrono => '2:20'
144
+ },
145
+ (2 * 3600 + 20 * 60) =>
146
+ {
147
+ :micro => '2h20m',
148
+ :short => '2h 20m',
149
+ :default => '2 hrs 20 mins',
150
+ :long => '2 hours 20 minutes',
151
+ :chrono => '2:20:00'
152
+ },
153
+ (6 * 30 * 24 * 3600 + 24 * 3600) =>
154
+ {
155
+ :micro => '6mo1d',
156
+ :short => '6mo 1d',
157
+ :default => '6 mos 1 day',
158
+ :long => '6 months 1 day',
159
+ :chrono => '6:01:00:00:00' # Yuck. FIXME
160
+ },
161
+ (365.25 * 24 * 3600 + 24 * 3600 ).to_i =>
162
+ {
163
+ :micro => '1y1d',
164
+ :short => '1y 1d',
165
+ :default => '1 yr 1 day',
166
+ :long => '1 year 1 day',
167
+ :chrono => '1:00:01:00:00:00'
168
+ },
169
+ (3 * 365.25 * 24 * 3600 + 24 * 3600 ).to_i =>
170
+ {
171
+ :micro => '3y1d',
172
+ :short => '3y 1d',
173
+ :default => '3 yrs 1 day',
174
+ :long => '3 years 1 day',
175
+ :chrono => '3:00:01:00:00:00'
176
+ },
177
+ (3600 * 24 * 30 * 18) =>
178
+ {
179
+ :micro => '18mo',
180
+ :short => '18mo',
181
+ :default => '18 mos',
182
+ :long => '18 months',
183
+ :chrono => '18:00:00:00:00'
184
+ }
185
+ }
186
+
187
+ @exemplars.each do |k, v|
188
+ v.each do |key, val|
189
+ it "properly outputs a duration of #{k} seconds as #{val} using the #{key.to_s} format option" do
190
+ ChronicDuration.output(k, :format => key).should == val
191
+ end
192
+ end
193
+ end
194
+
195
+ @keep_zero_exemplars = {
196
+ (true) =>
197
+ {
198
+ :micro => '0s',
199
+ :short => '0s',
200
+ :default => '0 secs',
201
+ :long => '0 seconds',
202
+ :chrono => '0'
203
+ },
204
+ (false) =>
205
+ {
206
+ :micro => nil,
207
+ :short => nil,
208
+ :default => nil,
209
+ :long => nil,
210
+ :chrono => '0'
211
+ },
212
+ }
213
+
214
+ @keep_zero_exemplars.each do |k, v|
215
+ v.each do |key, val|
216
+ it "should properly output a duration of 0 seconds as #{val.nil? ? "nil" : val} using the #{key.to_s} format option, if the keep_zero option is #{k.to_s}" do
217
+ ChronicDuration.output(0, :format => key, :keep_zero => k).should == val
218
+ end
219
+ end
220
+ end
221
+
222
+ it "returns weeks when needed" do
223
+ ChronicDuration.output(45*24*60*60, :weeks => true).should =~ /.*wk.*/
224
+ end
225
+
226
+ it "returns hours and minutes only when :hours_only option specified" do
227
+ ChronicDuration.output(395*24*60*60 + 15*60, :limit_to_hours => true).should == '9480 hrs 15 mins'
228
+ end
229
+
230
+ context 'with :hours_per_day and :days_per_month params' do
231
+ it 'uses provided :hours_per_day' do
232
+ ChronicDuration.output(24 * 60 * 60, hours_per_day: 24).should == '1 day'
233
+ ChronicDuration.output(24 * 60 * 60, hours_per_day: 8).should == '3 days'
234
+ end
235
+
236
+ it 'uses provided :days_per_month' do
237
+ ChronicDuration.output(7 * 24 * 60 * 60, weeks: true, days_per_month: 30).should == '1 wk'
238
+ ChronicDuration.output(7 * 24 * 60 * 60, weeks: true, days_per_month: 20).should == '1 wk 2 days'
239
+ end
240
+
241
+ it 'uses provided both :hours_per_day and :days_per_month' do
242
+ ChronicDuration.output(7 * 24 * 60 * 60, weeks: true, days_per_month: 30, hours_per_day: 24).should == '1 wk'
243
+ ChronicDuration.output(5 * 8 * 60 * 60, weeks: true, days_per_month: 20, hours_per_day: 8).should == '1 wk'
244
+ end
245
+
246
+ it 'uses provided params alonside with :weeks when converting to months' do
247
+ ChronicDuration.output(30 * 24 * 60 * 60, days_per_month: 30, hours_per_day: 24).should == '1 mo'
248
+ ChronicDuration.output(30 * 24 * 60 * 60, days_per_month: 30, hours_per_day: 24, weeks: true).should == '1 mo 2 days'
249
+
250
+ ChronicDuration.output(20 * 8 * 60 * 60, days_per_month: 20, hours_per_day: 8).should == '1 mo'
251
+ ChronicDuration.output(20 * 8 * 60 * 60, days_per_month: 20, hours_per_day: 8, weeks: true).should == '1 mo'
252
+ end
253
+ end
254
+
255
+ it "returns the specified number of units if provided" do
256
+ ChronicDuration.output(4 * 3600 + 60 + 1, units: 2).should == '4 hrs 1 min'
257
+ ChronicDuration.output(6 * 30 * 24 * 3600 + 24 * 3600 + 3600 + 60 + 1, units: 3, format: :long).should == '6 months 1 day 1 hour'
258
+ end
259
+
260
+ context "when the format is not specified" do
261
+
262
+ it "uses the default format" do
263
+ ChronicDuration.output(2 * 3600 + 20 * 60).should == '2 hrs 20 mins'
264
+ end
265
+
266
+ end
267
+
268
+ @exemplars.each do |seconds, format_spec|
269
+ format_spec.each do |format, _|
270
+ it "outputs a duration for #{seconds} that parses back to the same thing when using the #{format.to_s} format" do
271
+ ChronicDuration.parse(ChronicDuration.output(seconds, :format => format)).should == seconds
272
+ end
273
+ end
274
+ end
275
+
276
+ it "uses user-specified joiner if provided" do
277
+ ChronicDuration.output(2 * 3600 + 20 * 60, joiner: ', ').should == '2 hrs, 20 mins'
278
+ end
279
+
280
+ end
281
+
282
+ describe ".filter_by_type" do
283
+
284
+ it "receives a chrono-formatted time like 3:14 and return a human time like 3 minutes 14 seconds" do
285
+ ChronicDuration.instance_eval("filter_by_type('3:14')").should == '3 minutes 14 seconds'
286
+ end
287
+
288
+ it "receives chrono-formatted time like 12:10:14 and return a human time like 12 hours 10 minutes 14 seconds" do
289
+ ChronicDuration.instance_eval("filter_by_type('12:10:14')").should == '12 hours 10 minutes 14 seconds'
290
+ end
291
+
292
+ it "returns the input if it's not a chrono-formatted time" do
293
+ ChronicDuration.instance_eval("filter_by_type('4 hours')").should == '4 hours'
294
+ end
295
+
296
+ end
297
+
298
+ describe ".cleanup" do
299
+
300
+ it "cleans up extraneous words" do
301
+ ChronicDuration.instance_eval("cleanup('4 days and 11 hours')").should == '4 days 11 hours'
302
+ end
303
+
304
+ it "cleans up extraneous spaces" do
305
+ ChronicDuration.instance_eval("cleanup(' 4 days and 11 hours')").should == '4 days 11 hours'
306
+ end
307
+
308
+ it "inserts spaces where there aren't any" do
309
+ ChronicDuration.instance_eval("cleanup('4m11.5s')").should == '4 minutes 11.5 seconds'
310
+ end
311
+
312
+ end
313
+
314
+ describe "work week" do
315
+ before(:all) do
316
+ ChronicDuration.hours_per_day = 8
317
+ ChronicDuration.days_per_month = 20
318
+ end
319
+
320
+ after(:all) do
321
+ ChronicDuration.hours_per_day = 24
322
+ ChronicDuration.days_per_month = 30
323
+ end
324
+
325
+ it "should parse knowing the work week" do
326
+ week = ChronicDuration.parse('5d')
327
+ ChronicDuration.parse('40h').should == week
328
+ ChronicDuration.parse('1w').should == week
329
+ end
330
+ end
331
+ end
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'gitlab_chronic_duration'
4
+
5
+ RSpec.configure do |config|
6
+ end
7
+
8
+
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gitlab_chronic_duration
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.10.6.1
5
+ platform: ruby
6
+ authors:
7
+ - hpoydar
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-09-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: numerizer
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.1.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.1.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 10.0.3
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 10.0.3
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 2.12.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 2.12.0
55
+ description: A simple Ruby natural language parser for elapsed time. (For example,
56
+ 4 hours and 30 minutes, 6 minutes 4 seconds, 3 days, etc.) Returns all results in
57
+ seconds. Will return an integer unless you get tricky and need a float. (4 minutes
58
+ and 13.47 seconds, for example.) The reverse can also be performed via the output
59
+ method.
60
+ email:
61
+ - henry@poydar.com
62
+ executables: []
63
+ extensions: []
64
+ extra_rdoc_files: []
65
+ files:
66
+ - ".gitignore"
67
+ - ".gitlab-ci.yml"
68
+ - CHANGELOG.md
69
+ - Gemfile
70
+ - LICENSE.txt
71
+ - README.md
72
+ - Rakefile
73
+ - gitlab_chronic_duration.gemspec
74
+ - lib/chronic_duration/version.rb
75
+ - lib/gitlab_chronic_duration.rb
76
+ - spec/lib/chronic_duration_spec.rb
77
+ - spec/spec_helper.rb
78
+ homepage: https://gitlab.com/gitlab-org/gitlab-chronic-duration
79
+ licenses:
80
+ - MIT
81
+ metadata: {}
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubygems_version: 3.0.3
98
+ signing_key:
99
+ specification_version: 4
100
+ summary: A simple Ruby natural language parser for elapsed time
101
+ test_files:
102
+ - spec/lib/chronic_duration_spec.rb
103
+ - spec/spec_helper.rb