How to evaluate a date difference in years, months and days (ruby)? - ruby-on-rails

I have to make a simple difference between two dates:
Date.parse("2009-06-20") - Date.today
This gives me the difference of the dates in days.
Anyone know a way to easily convert that to the following format:
The event occurred X years, Y months and Z days ago
Thank you.

Found the answer here:
More precise distance_of_time_in_words
with this gem:
https://github.com/radar/dotiw

You may be looking for distance_of_times_in_words

There is a RubyGem which returns Time difference in a hash like {:year => 0, :month => 0,...}
The link is : https://rubygems.org/gems/time_diff

This is an example for difference in days, hours, seconds. Add the fields that you need.
def calculate_difference
minutes = (Date.parse("2009-06-20") - Date.today).to_i / 60
days = minutes / (24*60)
minutes -= days * 24*60
hours = minutes / 60
minutes -= hours * 60
"#{days}d#{hours}h#{minutes}m"
end

I find these generic date differences useful in many cases:
time_a = Time.zone.parse('2020-05-30')
time_b = Time.zone.parse('2010-01-18')
time_diff = {
years: time_a.year - time_b.year,
months: time_a.month - time_b.month,
days: time_a.day - time_b.day,
}
=> {:years=>10, :months=>4, :days=>12}

I don't know of any standard, correct way to do this. As odd as it may seem the standard Ruby library has a Date class but no DateSpan functionality. Rails has a solution but it feels somewhat of a pain for me to require this whole mammoth for such a trivial task.

Related

How to output days as hours in Ruby on Rails?

In my Ruby on Rails application I have a constant like this:
TIME = 3.days
Is there a way to output this value in hours?
# => 72
Thanks for any help.
Ruby or Rails has no direct method for this but if you google you can find a lot of way to do this.you can try followings:
3.days/1.hour
also take a look at Is there any method in Rails to convert minutes as integer to days, months, etc
> 3.days / 3600
=> 72
Not very intuitive, I know.
TIME = 3.days
=> 259200
TIME/3600
=> 72
We have to covert it in hours (60 * 60 = 3600) :P
in_hours (Rails 6.1+)
Rails 6.1 introduces new ActiveSupport::Duration conversion methods like in_seconds, in_minutes, in_hours, in_days, in_weeks, in_months, and in_years.
As a result, now, your problem can be solved as simple as:
3.days.in_hours
# => 72.0
Here is a link to the corresponding PR.

How to convert X months to Y days in Rails in one step?

I have a configuration value stored as 3.months, and another value returning from a calculation as 7.days.
I need to find their difference. What is the easiest & Railsiest way to normalise them so that I can subtract 7.days from 3.months? I can turn .months into days by using .to_i & multiplying by seconds-in-a-day, but it feels pretty long-winded to have to calculate from months to seconds to days like this:
x = (3.months.to_i/86400).days
=> 90 days
x - 7.days
=> 83 days
Is there a snappier way?
If you want to keep each term with units of time I would recommend this:
(3.months - 7.days)/1.day
I feel like its the cleanest and easiest for another dev to realize what you are trying to describe.
Not sure it's any better but:
end_date = Date.today.advance(months: 3) - 7.days
(end_date - Date.today).to_i #=> 83
I'f I understand correctly, you can subtract them one from another like so:
(3.months - 7.days)/86400 #=> 83
Both months and days methods return an integer of seconds.

Ruby strptime with greater than 24 hours

I have a string of the following format:
"136:16:11.862504"
(hours:minutes:seconds:milliseconds)
Whenever I try to use Ruby's strptime to parse this string, it throws an ArgumentError: invalid strptime format - '%H:%M:%S'
I've actually searched quite extensively and cannot figure out an elegant way to parse this (besides the rather clunky solution of splitting the string by its colons and periods, and doing it all manually). Is there a way of doing this that I'm overlooking?
EDIT: I'm not looking to get a timestamp out of this, I'm looking to get a time duration.
What is your expected output? '136' is not a valid hour, and since you don't have a date portion, we can't simply turn those 'extra' hours into days. If you don't care about the date portion, this solution may work for you:
time = "136:16:11.862504"
hours, minutes, seconds = time.split(":").map(&:to_f)
hours %= 24
minutes %= 60
seconds %= 60
Time.new(0, 1, 1, hours, minutes, seconds, 0)
=> 0000-01-01 16:16:11 +0000
In case if nothing blocks you from using Regexp, you could use something based on this answer, for example:
/^(\d+):([0-5][0-9]):([0-5][0-9])\.\d+$/ =~ "136:16:11.862504"
puts "#{$1} : #{$2} : #{$3}"
136 : 16 : 11

How to just show minutes or hours or weeks in rails time ago?

I would like to get the following result based on created_at:
1-59M
1-24H
1-999+W
E.g, if a post is 5 minutes old it will say 5M. If it is 15 hours old it will say: 15H and lastly it will say 52W if it is 52 weeks old.
Bonus: how would I make it work with: https://github.com/basecamp/local_time
You just want it in weeks, hours or minutes? How about this (it would go in a helpers file)
def short_age_string(time)
diff = Time.now - time #value is seconds (float)
if diff >= 0
result = "1-"
else
result = "1+"
end
diff = diff.abs.to_i
if diff >= 604800 #seconds in a week
weeks = diff/604800
return "#{result}#{weeks}#{"+" if weeks >= 999}W"
elsif diff > 3600 #seconds in an hour
return "#{result}#{diff/3600}H"
else
return "#{diff/60}#{minutes}M"
end
end
I took the liberty of making it return "1+..." for times in the future.
I believe you'd use strftime to manage it with i18n
According to strftimer, you'd need to use %-dH, %-dM, %-dD, %-dW to get the format you desire:
#view
<%=l record.created_at, format: :small %>
#config/locales/en.yml
time:
small: %-dH
I've tried testing this & it will only bring back the initial number. More testing is needed, but should set you on the right track

How do I get the number of seconds between two DateTimes in Ruby on Rails

I've got code that does time tracking for employees. It creates a counter to show the employee how long they have been clocked in for.
This is the current code:
start_time = Time.parse(self.settings.first_clock_in)
total_seconds = Time.now - start_time
hours = (total_seconds/ 3600).to_i
minutes = ((total_seconds % 3600) / 60).to_i
seconds = ((total_seconds % 3600) % 60).to_i
This works fine. But because Time is limited to the range of 1970 - 2038 we are trying to replace all Time uses with DateTimes. I can't figure out how to get the number of seconds between two DateTimes. Subtracting them yields a Rational which I don't know how to interpret, whereas subtracting Times yields the difference in seconds.
NOTE: Since Ruby 1.9.2, the hard limit of Time is removed. However, Time is optimized for values between 1823-11-12 and 2116-02-20.
Subtracting two DateTimes returns the elapsed time in days, so you could just do:
elapsed_seconds = ((end_time - start_time) * 24 * 60 * 60).to_i
Or, more readably:
diff = datetime_1 - datetime_2
diff * 1.days # => difference in seconds; requires Ruby on Rails
Note, what you or some other searchers might really be looking for is this:
diff = datetime_1 - datetime_2
Date.day_fraction_to_time(diff) # => [h, m, s, frac_s]
You can convert them to floats with to_f, though this will incur the usual loss of precision associated with floats. If you're just casting to an integer for whole seconds it shouldn't be big enough to be a worry.
The results are in seconds:
>> end_time.to_f - start_time.to_f
=> 7.39954495429993
>> (end_time.to_f - start_time.to_f).to_i
=> 7
Otherwise, you could look at using to_formatted_s on the DateTime object and seeing if you can coax the output into something the Decimal class will accept, or just formatting it as plain Unix time as a string and calling to_i on that.
Others incorrectly rely on fractions or helper functions. It's much simpler than that. DateTime itself is integer underneath. Here's the Ruby way:
stop.to_i - start.to_i
Example:
start = Time.now
=> 2016-06-21 14:55:36 -0700
stop = start + 5.seconds
=> 2016-06-21 14:55:41 -0700
stop.to_i - start.to_i
=> 5
I am using ruby-2.1.4 and for me the following worked
Time.now - Time.new(2014,11,05,17,30,0)
gave me the time difference in seconds
reference: ruby doc
there's a method made for that:
Time.now.minus_with_coercion(10.seconds.ago)
equals 10.
Source: http://apidock.com/rails/Time/minus_with_coercion
Hope I helped.
Define a Ruby function like this,
def time_diff(start_time, end_time)
seconds_diff = (start_time - end_time).to_i.abs
days = seconds_diff / 86400
seconds_diff -= days * 86400
hours = seconds_diff / 3600
seconds_diff -= hours * 3600
minutes = seconds_diff / 60
seconds_diff -= minutes * 60
seconds = seconds_diff
"#{days} Days #{hours} Hrs #{minutes} Min #{seconds} Sec"
end
And Call this function,
time_diff(Time.now, Time.now-4.days-2.hours-1.minutes-53.seconds)

Resources