dotiw 3.0 → 5.0.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.
- checksums.yaml +5 -5
- data/.github/workflows/ruby.yml +45 -0
- data/.gitignore +2 -1
- data/.rspec +2 -0
- data/Appraisals +19 -0
- data/CHANGELOG.md +24 -0
- data/CONTRIBUTING.md +34 -0
- data/Gemfile +2 -0
- data/MIT-LICENSE +1 -1
- data/README.markdown +139 -70
- data/Rakefile +3 -1
- data/dotiw.gemspec +22 -17
- data/gemfiles/.bundle/config +2 -0
- data/gemfiles/rails_4.gemfile +9 -0
- data/gemfiles/rails_5.0.gemfile +9 -0
- data/gemfiles/rails_5.1.gemfile +9 -0
- data/gemfiles/rails_5.2.gemfile +9 -0
- data/gemfiles/rails_6.0.gemfile +9 -0
- data/lib/dotiw.rb +22 -11
- data/lib/dotiw/action_view/helpers/date_helper.rb +24 -0
- data/lib/dotiw/locale/ar.yml +53 -0
- data/lib/dotiw/locale/da.yml +23 -0
- data/lib/dotiw/locale/fr.yml +25 -0
- data/lib/dotiw/locale/id.yml +25 -0
- data/lib/dotiw/locale/ko.yml +25 -0
- data/lib/dotiw/locale/pl.yml +7 -0
- data/lib/dotiw/locale/pt-BR.yml +25 -0
- data/lib/dotiw/locale/zh-CN.yml +25 -0
- data/lib/dotiw/methods.rb +91 -0
- data/lib/dotiw/time_hash.rb +65 -38
- data/lib/dotiw/version.rb +2 -2
- data/spec/lib/dotiw_spec.rb +206 -157
- data/spec/spec_helper.rb +2 -9
- metadata +76 -22
- data/.travis.yml +0 -5
- data/lib/dotiw/action_view_ext/helpers/date_helper.rb +0 -103
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DOTIW
|
4
|
+
module Methods
|
5
|
+
def distance_of_time_in_words_hash(from_time, to_time, options = {})
|
6
|
+
from_time = from_time.to_time if !from_time.is_a?(Time) && from_time.respond_to?(:to_time)
|
7
|
+
to_time = to_time.to_time if !to_time.is_a?(Time) && to_time.respond_to?(:to_time)
|
8
|
+
|
9
|
+
DOTIW::TimeHash.new(nil, from_time, to_time, options).to_hash
|
10
|
+
end
|
11
|
+
|
12
|
+
def distance_of_time(seconds, options = {})
|
13
|
+
options[:include_seconds] ||= true
|
14
|
+
_display_time_in_words DOTIW::TimeHash.new(seconds, nil, nil, options).to_hash, options
|
15
|
+
end
|
16
|
+
|
17
|
+
def distance_of_time_in_words(from_time, to_time = 0, include_seconds_or_options = {}, options = {})
|
18
|
+
if include_seconds_or_options.is_a?(Hash)
|
19
|
+
options = include_seconds_or_options
|
20
|
+
else
|
21
|
+
options[:include_seconds] ||= !!include_seconds_or_options
|
22
|
+
end
|
23
|
+
return distance_of_time(from_time, options) if to_time == 0
|
24
|
+
|
25
|
+
hash = distance_of_time_in_words_hash(from_time, to_time, options)
|
26
|
+
_display_time_in_words(hash, options)
|
27
|
+
end
|
28
|
+
|
29
|
+
def time_ago_in_words(from_time, include_seconds_or_options = {})
|
30
|
+
distance_of_time_in_words(from_time, Time.current, include_seconds_or_options)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def _display_time_in_words(hash, options = {})
|
36
|
+
options.reverse_merge!(
|
37
|
+
include_seconds: false
|
38
|
+
).symbolize_keys!
|
39
|
+
|
40
|
+
include_seconds = options.delete(:include_seconds)
|
41
|
+
hash.delete(:seconds) if !include_seconds && hash[:minutes]
|
42
|
+
|
43
|
+
options[:except] = Array.wrap(options[:except]).map!(&:to_sym) if options[:except]
|
44
|
+
options[:only] = Array.wrap(options[:only]).map!(&:to_sym) if options[:only]
|
45
|
+
|
46
|
+
# Remove all the values that are nil or excluded. Keep the required ones.
|
47
|
+
hash.delete_if do |key, value|
|
48
|
+
value.nil? || value.zero? ||
|
49
|
+
options[:except]&.include?(key) ||
|
50
|
+
(options[:only] && !options[:only].include?(key))
|
51
|
+
end
|
52
|
+
|
53
|
+
i18n_scope = options.delete(:scope) || DOTIW::DEFAULT_I18N_SCOPE
|
54
|
+
if hash.empty?
|
55
|
+
fractions = DOTIW::TimeHash::TIME_FRACTIONS
|
56
|
+
fractions &= options[:only] if options[:only]
|
57
|
+
fractions -= options[:except] if options[:except]
|
58
|
+
|
59
|
+
I18n.with_options locale: options[:locale], scope: i18n_scope do |locale|
|
60
|
+
# e.g. try to format 'less than 1 days', fallback to '0 days'
|
61
|
+
return locale.translate :less_than_x,
|
62
|
+
distance: locale.translate(fractions.first, count: 1),
|
63
|
+
default: locale.translate(fractions.first, count: 0)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
output = []
|
68
|
+
I18n.with_options locale: options[:locale], scope: i18n_scope do |locale|
|
69
|
+
output = hash.map { |key, value| locale.t(key, count: value) }
|
70
|
+
end
|
71
|
+
|
72
|
+
options.delete(:except)
|
73
|
+
options.delete(:only)
|
74
|
+
highest_measures = options.delete(:highest_measures)
|
75
|
+
highest_measures = 1 if options.delete(:highest_measure_only)
|
76
|
+
output = output[0...highest_measures] if highest_measures
|
77
|
+
|
78
|
+
options[:words_connector] ||= I18n.translate :'datetime.dotiw.words_connector',
|
79
|
+
default: :'support.array.words_connector',
|
80
|
+
locale: options[:locale]
|
81
|
+
options[:two_words_connector] ||= I18n.translate :'datetime.dotiw.two_words_connector',
|
82
|
+
default: :'support.array.two_words_connector',
|
83
|
+
locale: options[:locale]
|
84
|
+
options[:last_word_connector] ||= I18n.translate :'datetime.dotiw.last_word_connector',
|
85
|
+
default: :'support.array.last_word_connector',
|
86
|
+
locale: options[:locale]
|
87
|
+
|
88
|
+
output.to_sentence(options)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end # DOTIW
|
data/lib/dotiw/time_hash.rb
CHANGED
@@ -1,25 +1,25 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module DOTIW
|
4
4
|
class TimeHash
|
5
|
-
TIME_FRACTIONS = [
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
def initialize(distance, from_time
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
5
|
+
TIME_FRACTIONS = %i[seconds minutes hours days weeks months years].freeze
|
6
|
+
|
7
|
+
attr_reader :distance, :smallest, :largest, :from_time, :to_time
|
8
|
+
|
9
|
+
def initialize(distance, from_time, to_time = nil, options = {})
|
10
|
+
@output = {}
|
11
|
+
@options = options
|
12
|
+
@distance = distance
|
13
|
+
@from_time = from_time || Time.current
|
14
|
+
@to_time = to_time || (@to_time_not_given = true && @from_time + distance.seconds)
|
15
|
+
@smallest, @largest = [@from_time, @to_time].minmax
|
16
|
+
@to_time += 1.hour if @to_time_not_given && smallest.dst? && !largest.dst?
|
17
|
+
@to_time -= 1.hour if @to_time_not_given && !smallest.dst? && largest.dst?
|
18
|
+
@smallest, @largest = [@from_time, @to_time].minmax
|
19
|
+
@distance ||= begin
|
20
20
|
d = largest - smallest
|
21
|
-
d -= 1.hour if
|
22
|
-
d += 1.hour if !
|
21
|
+
d -= 1.hour if smallest.dst? && !largest.dst?
|
22
|
+
d += 1.hour if !smallest.dst? && largest.dst?
|
23
23
|
d
|
24
24
|
end
|
25
25
|
|
@@ -30,16 +30,16 @@ module DOTIW
|
|
30
30
|
output
|
31
31
|
end
|
32
32
|
|
33
|
-
|
34
|
-
|
33
|
+
private
|
34
|
+
|
35
|
+
attr_reader :options, :output
|
35
36
|
|
36
37
|
def build_time_hash
|
37
38
|
if accumulate_on = options.delete(:accumulate_on)
|
38
39
|
accumulate_on = accumulate_on.to_sym
|
39
|
-
if accumulate_on == :years
|
40
|
-
|
41
|
-
|
42
|
-
TIME_FRACTIONS.index(accumulate_on).downto(0) { |i| self.send("build_#{TIME_FRACTIONS[i]}") }
|
40
|
+
return build_time_hash if accumulate_on == :years
|
41
|
+
|
42
|
+
TIME_FRACTIONS.index(accumulate_on).downto(0) { |i| send("build_#{TIME_FRACTIONS[i]}") }
|
43
43
|
else
|
44
44
|
while distance > 0
|
45
45
|
if distance < 1.minute
|
@@ -48,10 +48,12 @@ module DOTIW
|
|
48
48
|
build_minutes
|
49
49
|
elsif distance < 1.day
|
50
50
|
build_hours
|
51
|
-
elsif distance <
|
51
|
+
elsif distance < 7.days
|
52
52
|
build_days
|
53
|
-
|
54
|
-
|
53
|
+
elsif distance < 28.days
|
54
|
+
build_weeks
|
55
|
+
else # greater than a week
|
56
|
+
build_years_months_weeks_days
|
55
57
|
end
|
56
58
|
end
|
57
59
|
end
|
@@ -61,43 +63,67 @@ module DOTIW
|
|
61
63
|
|
62
64
|
def build_seconds
|
63
65
|
output[:seconds] = distance.to_i
|
64
|
-
|
66
|
+
@distance = 0
|
65
67
|
end
|
66
68
|
|
67
69
|
def build_minutes
|
68
|
-
output[:minutes],
|
70
|
+
output[:minutes], @distance = distance.divmod(1.minute.to_i)
|
69
71
|
end
|
70
72
|
|
71
73
|
def build_hours
|
72
|
-
output[:hours],
|
74
|
+
output[:hours], @distance = distance.divmod(1.hour.to_i)
|
73
75
|
end
|
74
76
|
|
75
77
|
def build_days
|
76
|
-
output[:days],
|
78
|
+
output[:days], @distance = distance.divmod(1.day.to_i) unless output[:days]
|
79
|
+
end
|
80
|
+
|
81
|
+
def build_weeks
|
82
|
+
output[:weeks], @distance = distance.divmod(1.week.to_i) unless output[:weeks]
|
77
83
|
end
|
78
84
|
|
79
85
|
def build_months
|
80
|
-
|
86
|
+
build_years_months_weeks_days
|
81
87
|
|
82
88
|
if (years = output.delete(:years)) > 0
|
83
89
|
output[:months] += (years * 12)
|
84
90
|
end
|
85
91
|
end
|
86
92
|
|
87
|
-
def
|
93
|
+
def build_years_months_weeks_days
|
88
94
|
months = (largest.year - smallest.year) * 12 + (largest.month - smallest.month)
|
89
95
|
years, months = months.divmod(12)
|
90
96
|
|
91
97
|
days = largest.day - smallest.day
|
92
98
|
|
99
|
+
weeks, days = days.divmod(7)
|
100
|
+
|
93
101
|
# Will otherwise incorrectly say one more day if our range goes over a day.
|
94
102
|
days -= 1 if largest.hour < smallest.hour
|
95
103
|
|
96
104
|
if days < 0
|
97
|
-
# Convert
|
105
|
+
# Convert a week to days and add to total
|
106
|
+
weeks -= 1
|
107
|
+
days += 7
|
108
|
+
end
|
109
|
+
|
110
|
+
if weeks < 0
|
111
|
+
# Convert the last month to a week and add to total
|
98
112
|
months -= 1
|
99
|
-
last_month = largest.advance(:
|
100
|
-
|
113
|
+
last_month = largest.advance(months: -1)
|
114
|
+
days_in_month = Time.days_in_month(last_month.month, last_month.year)
|
115
|
+
weeks += days_in_month / 7
|
116
|
+
days += days_in_month % 7
|
117
|
+
if days >= 7
|
118
|
+
days -= 7
|
119
|
+
weeks += 1
|
120
|
+
end
|
121
|
+
|
122
|
+
if weeks == -1
|
123
|
+
months -= 1
|
124
|
+
weeks = 4
|
125
|
+
days -= 4
|
126
|
+
end
|
101
127
|
end
|
102
128
|
|
103
129
|
if months < 0
|
@@ -108,11 +134,12 @@ module DOTIW
|
|
108
134
|
|
109
135
|
output[:years] = years
|
110
136
|
output[:months] = months
|
137
|
+
output[:weeks] = weeks
|
111
138
|
output[:days] = days
|
112
139
|
|
113
|
-
total_days,
|
140
|
+
total_days, @distance = distance.abs.divmod(1.day.to_i)
|
114
141
|
|
115
|
-
[total_days,
|
142
|
+
[total_days, @distance]
|
116
143
|
end
|
117
144
|
end # TimeHash
|
118
145
|
end # DOTIW
|
data/lib/dotiw/version.rb
CHANGED
data/spec/lib/dotiw_spec.rb
CHANGED
@@ -1,13 +1,17 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
|
-
describe
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
describe 'A better distance_of_time_in_words' do
|
6
|
+
if defined?(ActionView)
|
7
|
+
include ActionView::Helpers::DateHelper
|
8
|
+
include ActionView::Helpers::TextHelper
|
9
|
+
include ActionView::Helpers::NumberHelper
|
10
|
+
else
|
11
|
+
include DOTIW::Methods
|
12
|
+
end
|
9
13
|
|
10
|
-
START_TIME =
|
14
|
+
START_TIME = '01-08-2009'.to_time
|
11
15
|
|
12
16
|
before do
|
13
17
|
I18n.locale = :en
|
@@ -15,269 +19,314 @@ describe "A better distance_of_time_in_words" do
|
|
15
19
|
allow(Time.zone).to receive(:now).and_return(START_TIME)
|
16
20
|
end
|
17
21
|
|
18
|
-
describe
|
19
|
-
|
20
|
-
[0.5.minutes,
|
21
|
-
[4.5.minutes,
|
22
|
-
[5.minutes
|
23
|
-
[10.minutes
|
24
|
-
[1.hour
|
25
|
-
[1.hour + 30.seconds,
|
26
|
-
[4.weeks
|
27
|
-
[
|
28
|
-
|
29
|
-
|
22
|
+
describe 'distance of time' do
|
23
|
+
[
|
24
|
+
[0.5.minutes, '30 seconds'],
|
25
|
+
[4.5.minutes, '4 minutes and 30 seconds'],
|
26
|
+
[5.minutes, '5 minutes'],
|
27
|
+
[10.minutes, '10 minutes'],
|
28
|
+
[1.hour, '1 hour'],
|
29
|
+
[1.hour + 30.seconds, '1 hour and 30 seconds'],
|
30
|
+
[4.weeks, '4 weeks'],
|
31
|
+
[4.weeks + 2.days, '4 weeks and 2 days'],
|
32
|
+
[24.weeks, '5 months, 2 weeks, and 1 day']
|
33
|
+
].each do |number, result|
|
30
34
|
it "#{number} == #{result}" do
|
31
35
|
expect(distance_of_time(number)).to eq(result)
|
32
36
|
end
|
33
37
|
end
|
34
38
|
|
35
|
-
describe
|
36
|
-
it
|
37
|
-
expect(distance_of_time(1.2.minute, except: 'seconds')).to eq(
|
38
|
-
expect(distance_of_time(2.5.hours + 30.seconds, except: 'seconds')).to eq(
|
39
|
+
describe 'with options' do
|
40
|
+
it 'except:seconds should skip seconds' do
|
41
|
+
expect(distance_of_time(1.2.minute, except: 'seconds')).to eq('1 minute')
|
42
|
+
expect(distance_of_time(2.5.hours + 30.seconds, except: 'seconds')).to eq('2 hours and 30 minutes')
|
39
43
|
end
|
40
44
|
|
41
|
-
it
|
45
|
+
it 'except:seconds has higher precedence than include_seconds:true' do
|
42
46
|
expect(distance_of_time(1.2.minute, include_seconds: true, except: 'seconds')).to eq('1 minute')
|
43
47
|
end
|
44
48
|
end
|
45
|
-
|
46
49
|
end
|
47
50
|
|
48
|
-
describe
|
49
|
-
describe
|
50
|
-
|
51
|
-
[:years, :months, :days, :minutes, :seconds].each do |name|
|
51
|
+
describe 'hash version' do
|
52
|
+
describe 'giving correct numbers of' do
|
53
|
+
%i[years months weeks days minutes seconds].each do |name|
|
52
54
|
describe name do
|
53
|
-
it
|
55
|
+
it 'exactly' do
|
54
56
|
hash = distance_of_time_in_words_hash(START_TIME, START_TIME + 1.send(name))
|
55
57
|
expect(hash[name]).to eq(1)
|
56
58
|
end
|
57
59
|
|
58
|
-
it
|
60
|
+
it 'two' do
|
59
61
|
hash = distance_of_time_in_words_hash(START_TIME, START_TIME + 2.send(name))
|
60
62
|
expect(hash[name]).to eq(2)
|
61
63
|
end
|
62
64
|
end
|
63
65
|
end
|
64
66
|
|
65
|
-
it
|
66
|
-
hash = distance_of_time_in_words_hash(
|
67
|
-
|
67
|
+
it 'should be happy with lots of measurements' do
|
68
|
+
hash = distance_of_time_in_words_hash(
|
69
|
+
START_TIME,
|
70
|
+
START_TIME + 1.year + 2.months + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds
|
71
|
+
)
|
68
72
|
expect(hash[:years]).to eq(1)
|
69
73
|
expect(hash[:months]).to eq(2)
|
70
|
-
expect(hash[:
|
71
|
-
expect(hash[:
|
72
|
-
expect(hash[:
|
73
|
-
expect(hash[:
|
74
|
+
expect(hash[:weeks]).to eq(3)
|
75
|
+
expect(hash[:days]).to eq(4)
|
76
|
+
expect(hash[:hours]).to eq(5)
|
77
|
+
expect(hash[:minutes]).to eq(6)
|
78
|
+
expect(hash[:seconds]).to eq(7)
|
74
79
|
end
|
75
80
|
end
|
76
81
|
end
|
77
82
|
|
78
|
-
describe
|
79
|
-
it
|
80
|
-
expect(distance_of_time_in_words(START_TIME, START_TIME + 1.days, :
|
81
|
-
expect(distance_of_time_in_words(START_TIME, START_TIME + 5.days, :
|
83
|
+
describe 'real version' do
|
84
|
+
it 'debe hablar español' do
|
85
|
+
expect(distance_of_time_in_words(START_TIME, START_TIME + 1.days, locale: :es)).to eq('un día')
|
86
|
+
expect(distance_of_time_in_words(START_TIME, START_TIME + 5.days, locale: :es)).to eq('5 días')
|
82
87
|
end
|
83
88
|
|
84
89
|
it "deve parlare l'italiano" do
|
85
|
-
expect(distance_of_time_in_words(START_TIME, START_TIME + 1.days, true, :
|
86
|
-
expect(distance_of_time_in_words(START_TIME, START_TIME + 5.days, true, :
|
90
|
+
expect(distance_of_time_in_words(START_TIME, START_TIME + 1.days, true, locale: :it)).to eq('un giorno')
|
91
|
+
expect(distance_of_time_in_words(START_TIME, START_TIME + 5.days, true, locale: :it)).to eq('5 giorni')
|
87
92
|
end
|
88
93
|
|
89
|
-
|
90
|
-
[START_TIME, START_TIME + 5.days + 3.minutes,
|
91
|
-
[START_TIME, START_TIME + 1.minute,
|
92
|
-
[START_TIME, START_TIME + 3.years,
|
93
|
-
[START_TIME, START_TIME + 10.years,
|
94
|
-
[START_TIME, START_TIME +
|
95
|
-
[START_TIME, START_TIME + 3.hour,
|
96
|
-
[START_TIME, START_TIME + 13.months,
|
94
|
+
[
|
95
|
+
[START_TIME, START_TIME + 5.days + 3.minutes, '5 days and 3 minutes'],
|
96
|
+
[START_TIME, START_TIME + 1.minute, '1 minute'],
|
97
|
+
[START_TIME, START_TIME + 3.years, '3 years'],
|
98
|
+
[START_TIME, START_TIME + 10.years, '10 years'],
|
99
|
+
[START_TIME, START_TIME + 8.months, '8 months'],
|
100
|
+
[START_TIME, START_TIME + 3.hour, '3 hours'],
|
101
|
+
[START_TIME, START_TIME + 13.months, '1 year and 1 month'],
|
97
102
|
# Any numeric sequence is merely coincidental.
|
98
|
-
[START_TIME, START_TIME + 1.year + 2.months + 3.
|
99
|
-
[
|
100
|
-
[
|
101
|
-
[
|
102
|
-
[
|
103
|
-
[
|
104
|
-
|
105
|
-
|
103
|
+
[START_TIME, START_TIME + 1.year + 2.months + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds, '1 year, 2 months, 3 weeks, 4 days, 5 hours, 6 minutes, and 7 seconds'],
|
104
|
+
['2009-3-16'.to_time, '2008-4-14'.to_time, '11 months and 2 days'],
|
105
|
+
['2009-3-16'.to_time + 1.minute, '2008-4-14'.to_time, '11 months, 2 days, and 1 minute'],
|
106
|
+
['2009-4-14'.to_time, '2008-3-16'.to_time, '1 year, 4 weeks, and 1 day'],
|
107
|
+
['2009-2-01'.to_time, '2009-3-01'.to_time, '1 month'],
|
108
|
+
['2008-2-01'.to_time, '2008-3-01'.to_time, '1 month'],
|
109
|
+
[Date.parse('31.03.2015').to_time, Time.parse('01.03.2016'), '10 months, 4 weeks, and 2 days'],
|
110
|
+
[Date.new(2014, 1, 31), Date.new(2014, 3, 1), '4 weeks and 1 day'],
|
111
|
+
['2008-2-01'.to_time, '2008-3-01'.to_time, '1 month'],
|
112
|
+
['2014-1-31'.to_time, '2014-3-01'.to_time, '4 weeks and 1 day'],
|
113
|
+
['2014-1-31'.to_time, '2014-3-02'.to_time, '4 weeks and 2 days'],
|
114
|
+
['2016-1-31'.to_time, '2016-3-01'.to_time, '4 weeks and 2 days'],
|
115
|
+
['2016-1-31'.to_time, '2016-3-02'.to_time, '1 month']
|
116
|
+
].each do |start, finish, output|
|
106
117
|
it "should be #{output}" do
|
107
118
|
expect(distance_of_time_in_words(start, finish, true)).to eq(output)
|
119
|
+
expect(distance_of_time_in_words(finish, start, true)).to eq(output)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
[
|
124
|
+
[Time.zone.now, Time.zone.now + 1.day - 1.minute, '23 hours and 59 minutes'],
|
125
|
+
[Time.zone.now, Time.zone.now + 15.days - 1.minute, '14 days, 23 hours, and 59 minutes'],
|
126
|
+
[Time.zone.now, Time.zone.now + 29.days - 1.minute, '28 days, 23 hours, and 59 minutes'],
|
127
|
+
[Time.zone.now, Time.zone.now + 30.days - 1.minute, '29 days, 23 hours, and 59 minutes'],
|
128
|
+
[Time.zone.now, Time.zone.now + 31.days - 1.minute, '30 days, 23 hours, and 59 minutes'],
|
129
|
+
[Time.zone.now, Time.zone.now + 32.days - 1.minute, '31 days, 23 hours, and 59 minutes'],
|
130
|
+
[Time.zone.now, Time.zone.now + 33.days - 1.minute, '32 days, 23 hours, and 59 minutes']
|
131
|
+
].each do |start, finish, output|
|
132
|
+
it "should be #{output}" do
|
133
|
+
expect(distance_of_time_in_words(start, finish, accumulate_on: 'days')).to eq(output)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
[
|
138
|
+
[Time.at(1), Time.at(100), '1 minute'],
|
139
|
+
[DateTime.now, DateTime.now + 1.minute, '1 minute'],
|
140
|
+
[Date.new(2000, 1, 2), Date.new(2000, 1, 3), '1 day'],
|
141
|
+
[Time.at(DateTime.now), DateTime.now + 1.minute, '1 minute']
|
142
|
+
].each do |start, finish, output|
|
143
|
+
it "should be #{output}" do
|
144
|
+
expect(distance_of_time_in_words(start, finish)).to eq(output)
|
108
145
|
end
|
109
146
|
end
|
110
147
|
|
111
|
-
describe
|
112
|
-
|
148
|
+
describe 'accumulate on' do
|
149
|
+
[
|
113
150
|
[START_TIME,
|
114
151
|
START_TIME + 10.minute,
|
115
152
|
:seconds,
|
116
|
-
|
153
|
+
'600 seconds'],
|
117
154
|
[START_TIME,
|
118
155
|
START_TIME + 10.hour + 10.minute + 1.second,
|
119
156
|
:minutes,
|
120
|
-
|
157
|
+
'610 minutes and 1 second'],
|
121
158
|
[START_TIME,
|
122
|
-
START_TIME + 2.day +
|
159
|
+
START_TIME + 2.day + 10_000.hour + 10.second,
|
123
160
|
:hours,
|
124
|
-
|
161
|
+
'10048 hours and 10 seconds'],
|
125
162
|
[START_TIME,
|
126
|
-
START_TIME + 2.day +
|
163
|
+
START_TIME + 2.day + 10_000.hour + 10.second,
|
127
164
|
:days,
|
128
|
-
|
165
|
+
'418 days, 16 hours, and 10 seconds'],
|
166
|
+
[START_TIME,
|
167
|
+
START_TIME + 2.day + 10_000.hour + 10.second,
|
168
|
+
:weeks,
|
169
|
+
'59 weeks, 5 days, 16 hours, and 10 seconds'],
|
129
170
|
[START_TIME,
|
130
|
-
START_TIME + 2.day +
|
171
|
+
START_TIME + 2.day + 10_000.hour + 10.second,
|
131
172
|
:months,
|
132
|
-
|
133
|
-
[
|
134
|
-
|
135
|
-
]
|
136
|
-
fragments.each do |start, finish, accumulator, output|
|
173
|
+
'13 months, 3 weeks, 1 day, 16 hours, and 10 seconds'],
|
174
|
+
['2015-1-15'.to_time, '2016-3-15'.to_time, :months, '14 months']
|
175
|
+
].each do |start, finish, accumulator, output|
|
137
176
|
it "should be #{output}" do
|
138
|
-
expect(distance_of_time_in_words(start, finish, true, :
|
177
|
+
expect(distance_of_time_in_words(start, finish, true, accumulate_on: accumulator)).to eq(output)
|
139
178
|
end
|
140
179
|
end
|
141
180
|
end # :accumulate_on
|
142
181
|
|
143
|
-
describe
|
182
|
+
describe 'without finish time' do
|
144
183
|
# A missing finish argument should default to zero, essentially returning
|
145
184
|
# the equivalent of distance_of_time in order to be backwards-compatible
|
146
185
|
# with the original rails distance_of_time_in_words helper.
|
147
|
-
|
148
|
-
[5.minutes.to_i,
|
149
|
-
[10.minutes.to_i,
|
150
|
-
[1.hour.to_i,
|
151
|
-
[
|
152
|
-
[
|
153
|
-
|
154
|
-
|
186
|
+
[
|
187
|
+
[5.minutes.to_i, '5 minutes'],
|
188
|
+
[10.minutes.to_i, '10 minutes'],
|
189
|
+
[1.hour.to_i, '1 hour'],
|
190
|
+
[6.days.to_i, '6 days'],
|
191
|
+
[4.weeks.to_i, '4 weeks'],
|
192
|
+
[24.weeks.to_i, '5 months, 2 weeks, and 1 day']
|
193
|
+
].each do |start, output|
|
155
194
|
it "should be #{output}" do
|
156
195
|
expect(distance_of_time_in_words(start)).to eq(output)
|
157
196
|
end
|
158
197
|
end
|
159
198
|
end
|
160
|
-
|
161
199
|
end
|
162
200
|
|
163
|
-
describe
|
164
|
-
|
201
|
+
describe 'with output options' do
|
202
|
+
[
|
165
203
|
# Any numeric sequence is merely coincidental.
|
166
204
|
[START_TIME,
|
167
|
-
START_TIME + 1.year + 2.months + 3.
|
168
|
-
{ :
|
169
|
-
|
205
|
+
START_TIME + 1.year + 2.months + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds,
|
206
|
+
{ words_connector: ' - ' },
|
207
|
+
'1 year - 2 months - 3 weeks - 4 days - 5 hours - 6 minutes, and 7 seconds'],
|
170
208
|
[START_TIME,
|
171
209
|
START_TIME + 5.minutes + 6.seconds,
|
172
|
-
{ :
|
173
|
-
|
210
|
+
{ two_words_connector: ' - ' },
|
211
|
+
'5 minutes - 6 seconds'],
|
174
212
|
[START_TIME,
|
175
|
-
START_TIME + 4.hours +
|
176
|
-
{ :
|
177
|
-
|
213
|
+
START_TIME + 4.hours + 5.minutes + 6.seconds,
|
214
|
+
{ last_word_connector: ' - ' },
|
215
|
+
'4 hours, 5 minutes - 6 seconds'],
|
178
216
|
[START_TIME,
|
179
217
|
START_TIME + 1.year + 2.months + 3.days + 4.hours + 5.minutes + 6.seconds,
|
180
|
-
{ :
|
181
|
-
|
218
|
+
{ except: 'minutes' },
|
219
|
+
'1 year, 2 months, 3 days, 4 hours, and 6 seconds'],
|
182
220
|
[START_TIME,
|
183
221
|
START_TIME + 1.hour + 1.minute,
|
184
|
-
{ :
|
222
|
+
{ except: 'minutes' }, '1 hour'],
|
185
223
|
[START_TIME,
|
186
224
|
START_TIME + 1.hour + 1.day + 1.minute,
|
187
|
-
{ :
|
188
|
-
|
225
|
+
{ except: %w[minutes hours] },
|
226
|
+
'1 day'],
|
189
227
|
[START_TIME,
|
190
228
|
START_TIME + 1.hour + 1.day + 1.minute,
|
191
|
-
{ :
|
192
|
-
|
229
|
+
{ only: %w[minutes hours] },
|
230
|
+
'1 hour and 1 minute'],
|
193
231
|
[START_TIME,
|
194
|
-
START_TIME + 1.year + 2.months + 3.
|
195
|
-
{ :
|
196
|
-
|
232
|
+
START_TIME + 1.year + 2.months + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds,
|
233
|
+
{ except: 'minutes' },
|
234
|
+
'1 year, 2 months, 3 weeks, 4 days, 5 hours, and 7 seconds'],
|
197
235
|
[START_TIME,
|
198
|
-
START_TIME + 1.
|
199
|
-
{ :
|
200
|
-
|
201
|
-
[START_TIME,
|
202
|
-
START_TIME + 1.year + 2.months + 3.days + 4.hours + 5.minutes + 6.seconds,
|
203
|
-
{ :vague => false },
|
204
|
-
"1 year, 2 months, 3 days, 4 hours, 5 minutes, and 6 seconds"],
|
205
|
-
[START_TIME,
|
206
|
-
START_TIME + 1.year + 2.months + 3.days + 4.hours + 5.minutes + 6.seconds,
|
207
|
-
{ :vague => nil },
|
208
|
-
"1 year, 2 months, 3 days, 4 hours, 5 minutes, and 6 seconds"],
|
209
|
-
[START_TIME,
|
210
|
-
START_TIME + 1.year + 2.months + 3.days + 4.hours + 5.minutes + 6.seconds,
|
211
|
-
{ :except => "minutes" },
|
212
|
-
"1 year, 2 months, 3 days, 4 hours, and 6 seconds"],
|
213
|
-
[START_TIME,
|
214
|
-
START_TIME + 1.hour + 2.minutes + 3.seconds,
|
215
|
-
{ :highest_measure_only => true },
|
216
|
-
"1 hour"],
|
236
|
+
START_TIME + 1.hour + 2.minutes + 3.seconds,
|
237
|
+
{ highest_measure_only: true },
|
238
|
+
'1 hour'],
|
217
239
|
[START_TIME,
|
218
240
|
START_TIME + 1.hours + 2.minutes + 3.seconds,
|
219
|
-
{ :
|
220
|
-
|
241
|
+
{ highest_measures: 1 },
|
242
|
+
'1 hour'],
|
221
243
|
[START_TIME,
|
222
244
|
START_TIME + 2.year + 3.months + 4.days + 5.hours + 6.minutes + 7.seconds,
|
223
|
-
{ :
|
224
|
-
|
245
|
+
{ highest_measures: 3 },
|
246
|
+
'2 years, 3 months, and 4 days'],
|
225
247
|
[START_TIME,
|
226
248
|
START_TIME + 2.year + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds,
|
227
|
-
{ :
|
228
|
-
|
249
|
+
{ highest_measures: 2 },
|
250
|
+
'2 years and 3 weeks'],
|
229
251
|
[START_TIME,
|
230
252
|
START_TIME + 4.days + 6.minutes + 7.seconds,
|
231
|
-
{ :
|
232
|
-
|
253
|
+
{ highest_measures: 3 },
|
254
|
+
'4 days, 6 minutes, and 7 seconds'],
|
233
255
|
[START_TIME,
|
234
256
|
START_TIME + 1.year + 2.weeks,
|
235
|
-
{ :
|
236
|
-
|
257
|
+
{ highest_measures: 3 },
|
258
|
+
'1 year and 2 weeks'],
|
237
259
|
[START_TIME,
|
238
260
|
START_TIME + 1.days,
|
239
|
-
{ :
|
240
|
-
|
261
|
+
{ only: %i[years months] },
|
262
|
+
'less than 1 month'],
|
241
263
|
[START_TIME,
|
242
264
|
START_TIME + 5.minutes,
|
243
|
-
{ :
|
244
|
-
|
265
|
+
{ except: %i[hours minutes seconds] },
|
266
|
+
'less than 1 day'],
|
245
267
|
[START_TIME,
|
246
268
|
START_TIME + 1.days,
|
247
|
-
{ :
|
248
|
-
|
249
|
-
]
|
250
|
-
fragments.each do |start, finish, options, output|
|
269
|
+
{ highest_measures: 1, only: %i[years months] },
|
270
|
+
'less than 1 month']
|
271
|
+
].each do |start, finish, options, output|
|
251
272
|
it "should be #{output}" do
|
252
273
|
expect(distance_of_time_in_words(start, finish, true, options)).to eq(output)
|
253
274
|
end
|
254
275
|
end
|
255
276
|
|
256
|
-
|
257
|
-
|
258
|
-
|
277
|
+
if defined?(ActionView)
|
278
|
+
describe 'ActionView' do
|
279
|
+
[
|
280
|
+
[START_TIME,
|
281
|
+
START_TIME + 1.year + 2.months + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds,
|
282
|
+
{ vague: true },
|
283
|
+
'about 1 year'],
|
284
|
+
[START_TIME,
|
285
|
+
START_TIME + 1.year + 2.months + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds,
|
286
|
+
{ vague: 'Yes please' },
|
287
|
+
'about 1 year'],
|
288
|
+
[START_TIME,
|
289
|
+
START_TIME + 1.year + 2.months + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds,
|
290
|
+
{ vague: false },
|
291
|
+
'1 year, 2 months, 3 weeks, 4 days, 5 hours, 6 minutes, and 7 seconds'],
|
292
|
+
[START_TIME,
|
293
|
+
START_TIME + 1.year + 2.months + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds,
|
294
|
+
{ vague: nil },
|
295
|
+
'1 year, 2 months, 3 weeks, 4 days, 5 hours, 6 minutes, and 7 seconds']
|
296
|
+
].each do |start, finish, options, output|
|
297
|
+
it "should be #{output}" do
|
298
|
+
expect(distance_of_time_in_words(start, finish, true, options)).to eq(output)
|
299
|
+
end
|
300
|
+
end
|
259
301
|
end
|
302
|
+
end
|
260
303
|
|
261
|
-
|
304
|
+
describe 'include_seconds' do
|
305
|
+
it 'is ignored if only seconds have passed' do
|
306
|
+
expect(distance_of_time_in_words(START_TIME, START_TIME + 1.second, false)).to eq('1 second')
|
307
|
+
end
|
308
|
+
|
309
|
+
it 'removes seconds in all other cases' do
|
262
310
|
expect(distance_of_time_in_words(START_TIME,
|
263
|
-
|
264
|
-
|
311
|
+
START_TIME + 1.year + 2.months + 3.weeks + 4.days + 5.hours + 6.minutes + 7.seconds,
|
312
|
+
false)).to eq('1 year, 2 months, 3 weeks, 4 days, 5 hours, and 6 minutes')
|
265
313
|
end
|
266
314
|
end # include_seconds
|
267
315
|
end
|
268
316
|
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
317
|
+
if defined?(ActionView)
|
318
|
+
describe 'percentage of time' do
|
319
|
+
def time_in_percent(options = {})
|
320
|
+
distance_of_time_in_percent('04-12-2009'.to_time, '29-01-2010'.to_time, '04-12-2010'.to_time, options)
|
321
|
+
end
|
273
322
|
|
274
|
-
|
275
|
-
|
276
|
-
|
323
|
+
it 'calculates 15%' do
|
324
|
+
expect(time_in_percent).to eq('15%')
|
325
|
+
end
|
277
326
|
|
278
|
-
|
279
|
-
|
327
|
+
it 'calculates 15.3%' do
|
328
|
+
expect(time_in_percent(precision: 1)).to eq('15.3%')
|
329
|
+
end
|
280
330
|
end
|
281
331
|
end
|
282
|
-
|
283
332
|
end
|