diff --git a/.travis.yml b/.travis.yml index 3916015..d306305 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,7 @@ language: ruby rvm: - 2.6 + - 2.7 matrix: fast_finish: true before_install: diff --git a/CHANGELOG.md b/CHANGELOG.md index 2aee828..ad469a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased + +* [TT-6441] Due to TimeDifference being unmaintained bring it into the timely library + ## 0.6.0 * [TT-6402] Require date group weekdays bit field to be not null/nil diff --git a/gemfiles/rails6.gemfile b/gemfiles/rails6.gemfile index cbfd927..f5d8fd6 100644 --- a/gemfiles/rails6.gemfile +++ b/gemfiles/rails6.gemfile @@ -4,5 +4,5 @@ source 'https://rubygems.org' gemspec path: '../' group :development, :test do - gem 'activerecord', '~> 6.0.0.rc1' + gem 'activerecord', '~> 6.0' end diff --git a/lib/timely/rails.rb b/lib/timely/rails.rb index f4afcd8..22e5cb2 100644 --- a/lib/timely/rails.rb +++ b/lib/timely/rails.rb @@ -12,3 +12,4 @@ require 'timely/rails/date' require 'timely/rails/period' require 'timely/rails/time' +require 'timely/rails/time_difference' diff --git a/lib/timely/rails/time_difference.rb b/lib/timely/rails/time_difference.rb new file mode 100644 index 0000000..7ba1c59 --- /dev/null +++ b/lib/timely/rails/time_difference.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true + +require 'rubygems' +require 'active_support/all' + +module Timely + class TimeDifference + private_class_method :new + + TIME_COMPONENTS = %i[years months weeks days hours minutes seconds].freeze + + def self.between(start_time, end_time) + new(start_time, end_time) + end + + def in_years + in_component(:years) + end + + def in_months + (@time_diff / (1.day * 30.42)).round(2) + end + + def in_weeks + in_component(:weeks) + end + + def in_days + in_component(:days) + end + + def in_hours + in_component(:hours) + end + + def in_minutes + in_component(:minutes) + end + + def in_seconds + @time_diff + end + + def in_each_component + Hash[TIME_COMPONENTS.map do |time_component| + [time_component, public_send("in_#{time_component}")] + end] + end + + def in_general + remaining = @time_diff + Hash[TIME_COMPONENTS.map do |time_component| + if remaining > 0 + rounded_time_component = (remaining / 1.send(time_component).seconds).round(2).floor + remaining -= rounded_time_component.send(time_component) + [time_component, rounded_time_component] + else + [time_component, 0] + end + end] + end + + def humanize + diff_parts = [] + in_general.each do |part, quantity| + next if quantity <= 0 + + part = part.to_s.humanize + + part = part.singularize if quantity <= 1 + + diff_parts << "#{quantity} #{part}" + end + + last_part = diff_parts.pop + if diff_parts.empty? + last_part + else + [diff_parts.join(', '), last_part].join(' and ') + end + end + + private + + def initialize(start_time, end_time) + start_time = time_in_seconds(start_time) + end_time = time_in_seconds(end_time) + + @time_diff = (end_time - start_time).abs + end + + def time_in_seconds(time) + time.to_time.to_f + end + + def in_component(component) + (@time_diff / 1.send(component)).round(2) + end + end +end diff --git a/spec/rails/time_difference_spec.rb b/spec/rails/time_difference_spec.rb new file mode 100644 index 0000000..32b0584 --- /dev/null +++ b/spec/rails/time_difference_spec.rb @@ -0,0 +1,185 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Timely::TimeDifference do + def self.with_each_class(&block) + classes = [Time, Date, DateTime] + + classes.each do |clazz| + context "with a #{clazz.name} class" do + instance_exec clazz, &block + end + end + end + + describe '.between' do + with_each_class do |clazz| + it 'returns a new TimeDifference instance in each component' do + start_time = clazz.new(2011, 1) + end_time = clazz.new(2011, 12) + + expect(Timely::TimeDifference.between(start_time, end_time)).to be_a(Timely::TimeDifference) + end + end + end + + describe '#in_each_component' do + with_each_class do |clazz| + it 'returns time difference in each component' do + start_time = clazz.new(2011, 1) + end_time = clazz.new(2011, 12) + + expect(Timely::TimeDifference.between(start_time, end_time).in_each_component).to eql(years: 0.91, months: 10.98, weeks: 47.71, days: 334.0, hours: 8016.0, minutes: 480_960.0, seconds: 28_857_600.0) + end + end + end + + describe '#in_general' do + with_each_class do |clazz| + it 'returns time difference in general that matches the total seconds' do + start_time = clazz.new(2009, 11) + end_time = clazz.new(2011, 1) + + expect(Timely::TimeDifference.between(start_time, end_time).in_general).to eql(years: 1, months: 2, weeks: 0, days: 0, hours: 0, minutes: 0, seconds: 0) + end + end + end + + describe '#humanize' do + with_each_class do |clazz| + it 'returns a string representing the time difference from in_general' do + start_time = clazz.new(2009, 11) + end_time = clazz.new(2011, 1) + + expect(Timely::TimeDifference.between(start_time, end_time).humanize).to eql('1 Year and 2 Months') + end + end + end + + describe '#in_years' do + with_each_class do |clazz| + it 'returns time difference in years based on Wolfram Alpha' do + start_time = clazz.new(2011, 1) + end_time = clazz.new(2011, 12) + + expect(Timely::TimeDifference.between(start_time, end_time).in_years).to eql(0.91) + end + + it 'returns an absolute difference' do + start_time = clazz.new(2011, 12) + end_time = clazz.new(2011, 1) + + expect(Timely::TimeDifference.between(start_time, end_time).in_years).to eql(0.91) + end + end + end + + describe '#in_months' do + with_each_class do |clazz| + it 'returns time difference in months based on Wolfram Alpha' do + start_time = clazz.new(2011, 1) + end_time = clazz.new(2011, 12) + + expect(Timely::TimeDifference.between(start_time, end_time).in_months).to eql(10.98) + end + + it 'returns an absolute difference' do + start_time = clazz.new(2011, 12) + end_time = clazz.new(2011, 1) + + expect(Timely::TimeDifference.between(start_time, end_time).in_months).to eql(10.98) + end + end + end + + describe '#in_weeks' do + with_each_class do |clazz| + it 'returns time difference in weeks based on Wolfram Alpha' do + start_time = clazz.new(2011, 1) + end_time = clazz.new(2011, 12) + + expect(Timely::TimeDifference.between(start_time, end_time).in_weeks).to eql(47.71) + end + + it 'returns an absolute difference' do + start_time = clazz.new(2011, 12) + end_time = clazz.new(2011, 1) + + expect(Timely::TimeDifference.between(start_time, end_time).in_weeks).to eql(47.71) + end + end + end + + describe '#in_days' do + with_each_class do |clazz| + it 'returns time difference in weeks based on Wolfram Alpha' do + start_time = clazz.new(2011, 1) + end_time = clazz.new(2011, 12) + + expect(Timely::TimeDifference.between(start_time, end_time).in_days).to eql(334.0) + end + + it 'returns an absolute difference' do + start_time = clazz.new(2011, 12) + end_time = clazz.new(2011, 1) + + expect(Timely::TimeDifference.between(start_time, end_time).in_days).to eql(334.0) + end + end + end + + describe '#in_hours' do + with_each_class do |clazz| + it 'returns time difference in hours based on Wolfram Alpha' do + start_time = clazz.new(2011, 1) + end_time = clazz.new(2011, 12) + + expect(Timely::TimeDifference.between(start_time, end_time).in_hours).to eql(8016.0) + end + + it 'returns an absolute difference' do + start_time = clazz.new(2011, 12) + end_time = clazz.new(2011, 1) + + expect(Timely::TimeDifference.between(start_time, end_time).in_hours).to eql(8016.0) + end + end + end + + describe '#in_minutes' do + with_each_class do |clazz| + it 'returns time difference in minutes based on Wolfram Alpha' do + start_time = clazz.new(2011, 1) + end_time = clazz.new(2011, 12) + + expect(Timely::TimeDifference.between(start_time, end_time).in_minutes).to eql(480_960.0) + end + + it 'returns an absolute difference' do + start_time = clazz.new(2011, 12) + end_time = clazz.new(2011, 1) + + expect(Timely::TimeDifference.between(start_time, end_time).in_minutes).to eql(480_960.0) + end + end + end + + describe '#in_seconds' do + with_each_class do |clazz| + it 'returns time difference in seconds based on Wolfram Alpha' do + start_time = clazz.new(2011, 1) + end_time = clazz.new(2011, 12) + + expect(Timely::TimeDifference.between(start_time, end_time).in_seconds).to eql(28_857_600.0) + end + + it 'returns an absolute difference' do + start_time = clazz.new(2011, 12) + end_time = clazz.new(2011, 1) + + expect(Timely::TimeDifference.between(start_time, end_time).in_seconds).to eql(28_857_600.0) + end + end + end +end diff --git a/spec/support/coverage_loader.rb b/spec/support/coverage_loader.rb index 372ad1e..b6cf05c 100644 --- a/spec/support/coverage_loader.rb +++ b/spec/support/coverage_loader.rb @@ -3,4 +3,4 @@ require 'simplecov-rcov' require 'coveralls' require 'coverage/kit' -Coverage::Kit.setup(minimum_coverage: 70.15) +Coverage::Kit.setup(minimum_coverage: 72.0)