Ruby: Math functions for Time Values - ruby-on-rails

How do I add/subtract/etc. time values in Ruby? For example, how would I add the following times?
00:00:59 + 00:01:43 + 00:20:15 = ?

Use ActiveSupport, which has a ton of built-in date extensions.
require 'active_support/core_ext'
t1 = "#{Date.today} 00:00:59".to_time
t2 = "#{Date.today} 00:01:43".to_time
t3 = "#{Date.today} 00:20:15".to_time
t1.since(t2.seconds_since_midnight+t3.seconds_since_midnight)
or, if you don't care about the date, only time:
t1.since(t2.seconds_since_midnight+t3.seconds_since_midnight).strftime("%H:%M:%S")
For a full list, check out http://guides.rubyonrails.org/active_support_core_extensions.html#extensions-to-date

Kind of ugly, but you could use DateTime.parse(each_interval) & calculate the number of seconds in each. Like this:
require 'date'
def calc_seconds(time_string)
date_time = DateTime.parse(time_string)
hour_part = date_time.hour * 60 * 60
minute_part = date_time.minute * 60
second_part = date_time.second
hour_part + minute_part + second_part
end
...which gives you your result in seconds, assuming valid inputs. At which point you can add them together.
You could reverse the process to get the interval in your original notation.
I really think there ought to be an easier method, but I don't know of one.

One way would be to convert everything to seconds and then performing the operations... Then you would need to convert it again to a time object with
Time.at(seconds_result).strftime('%H:%M:%S')
And you would get the time nicely formatted (as a string).
I am trying to find a gem that does this, and other operations.

You probably want to use a gem that does not concern itself with the actual day. You could perform acrobatics using DateTime and or Time, but you would constantly be battling how to handle days.
One gem that may be useful is tod (TimeOfDay), https://github.com/JackC/tod
With that you could directly do TimeOfDay.parse "00:01:43", add the values, and print the result using strftime("%H:%M:%S").

Related

How do I display fractions of a second in my time formatting function?

I'm using Rails 5. I have a method that is supposed to help display a duraiton in milliseconds in a more readable format (hours:minutes:seconds) ...
def time_formatted time_in_ms
regex = /^(0*:?)*0*/
Time.at(time_in_ms/1000).utc.strftime("%H:%M:%S").sub!(regex, '')
end
This works basically fine, but it doesn't work completely accurately when the time in milliseconds contains fractions of a second. That is, if the time in milliseconds is
2486300
The above displays
41:26
but really it should display
41:26.3
How can I adjust my function so it will also display fractions of a second, assuming there are any?
For accuracy make sure you're returning a float (I've used to_f to do this).
Append the argument to strftime with ".%1N" for 1-digit milliseconds.
def time_formatted time_in_ms
regex = /^(0*:?)*0*/
Time.at(time_in_ms.to_f/1000).utc.strftime("%H:%M:%S.%1N").sub!(regex, '')
end
time_formatted 2486300
#=> "41:26.3"
For more information see Time#strftime in the official Ruby documentation.

How to solve Mathematical Expressions in Rails 4 like 6000*70%?

I am using Dentaku gem to solve little complex expressions like basic salary is 70% of Gross salary. As the formulas are user editable so I worked on dentaku.
When I write calculator = Dentaku::Calculator.new to initialize and then enter the command calculator.evaluate("60000*70%") then error comes like below:
Dentaku::ParseError: Dentaku::AST::Modulo requires numeric operands
from /Users/sulman/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/dentaku-2.0.8/lib/dentaku/ast/arithmetic.rb:11:in `initialize'
I have array is which formula is stored like: ["EarningItem-5","*","6","7","%"] where EarningItem-5 is an object and has value 60000
How can I resolve such expressions?
For this particular case you can use basic_salary = gross_salary * 0.7
Next you need to create the number field in your views which accepts 0..100 range. At last, set up the after_save callback and use this code:
model
after_create :percent_to_float
protected
def percent_to_float
self.percent = percent / 100.0
self.save
end
edit:
Of course, you can simply use this formula without any callbacks:
basic_salary = gross_salary / 100.0 * 70
where 70 is user defined value.
Dentaku does not appear to support "percent". Try this instead
calculator.evaluate('60000 * 0.7')

Getting "argument out of range" when trying to turn a duration into milliseconds in Rails 4

I’m using Rails 4.2.4. I have the below method for converting a time (duration) to milliseconds …
Time.parse(convert_to_hrs(duration)).seconds_since_midnight * 1000
in which the method “convert_to_hrs” is defined as
def convert_to_hrs(string)
case string.count(':')
when 0
'00:00:' + string.rjust(2, '0')
when 1
'00:' + string
else
string
end
end
However, if the duration is something really big (e.g. “34:13:00” -- read: 34 hours, 13 minutes, and zero seconds), the above fails with the error
Error during processing: argument out of range
/Users/mikea/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/time.rb:302:in `local'
/Users/mikea/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/time.rb:302:in `make_time'
/Users/mikea/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/time.rb:366:in `parse'
/Users/mikea/Documents/workspace/myproject/app/services/my_service.rb:25:in `block in process_page_data'
/Users/mikea/Documents/workspace/myproject/app/services/my_service.rb:22:in `each'
/Users/mikea/Documents/workspace/myproject/app/services/my_service.rb:22:in `process_page_data'
How do I rewrite my first line to accurately convert duration into milliseconds?
If you know you're always going to be using a hours:minutes:seconds format, but the number in each field isn't guaranteed to be inside the 'normal' range (e.g. 0-23 for hours, 0-59 for minutes, etc), then you're probably best off doing it 'manually' using something like this:
def duration_in_milliseconds(input)
h, m, s = input.split(':').map(&:to_i)
(h.hours + m.minutes + s.seconds) * 1000
end
puts duration_in_milliseconds('34:13:00') #=> 123180000
Note that this only works with ActiveSupport, but you have that, since you've specified Rails. Also, this assumes you're always getting all three terms (e.g. 5 seconds is 00:00:05). The full setup that accepts shorter strings as well would want to also use your convert_to_hrs method.
Note also that this works even if formatting isn't strictly 'time-like', as long as you have consistent colons as seperators:
puts duration_in_milliseconds('1:1:5') #=> 3665000
The Numeric#hours, Numeric#minutes and Numeric#seconds methods are provided by ActiveSupport, as part of active_support/core-ext/time.rb. They aren't particularly documented, but they return ActiveSupport::Duration objects, which have fancy methods for interacting with Time and Date issues like 5.days.ago, but when treated as an integer are effectively a number of seconds.
Time.parse is throwing error, becuase values you passing in duration variable is out of range.
For Ex:
Time.parse(convert_to_hrs('59:59')) as per your written code, it's return 2016-07-27 00:59:59 +0530
Here the value 59:59 consider as minutes:seconds, so if you pass the value 60:60 then it will raise the error argument out of range
Here is the official documentation for parse method of Time
Hope this will help you.

Ruby on Rails - YouTube API - How to format duration ISO 8601 (PT45S)

I've searched high and low and can't seem to find a straight answer on this. Basically, I'm calling the YouTube API and getting a JSON document back, then parsing it. Everything else is good, but I don't understand how to parse the 'duration' property to display it as human readable.
The 'duration' field comes over as 'PT1H5M34S' - 1 hour 5 minutes 34 seconds
Or it could be 'PT24S' - 24 seconds
Or 'PT4M3S' - 4 minutes 3 seconds
There has to be a way in Ruby to parse this string and make it human readable so that I can just pass in the duration on the fly in my loop and convert it. Any help or guidance is greatly appreciated. I've tried using Date.parse, Time.parse, Date.strptime, along with many other things... Like just gsub-ing the PT out of the string and displaying it, but that doesn't seem right.
Try arnau's ISO8601 parser (https://github.com/arnau/ISO8601)
Usage:
d = ISO8601::Duration.new('PT1H5M34S')
d.to_seconds # => 3934.0
A simple approach to get the number of seconds for videos less than 24 hours:
dur = "PT34M5S"
pattern = "PT"
pattern += "%HH" if dur.include? "H"
pattern += "%MM" if dur.include? "M"
pattern += "%SS"
DateTime.strptime(dur, pattern).seconds_since_midnight.to_i
You can use Rails ActiveSupport::Duration
parsed_duration = ActiveSupport::Duration.parse(youtube_duration)
Time.at(parsed_duration).utc.strftime('%H:%M:%S')

How to judge whether a time/date is today, in ruby?

I have a settlement, I want to judge if the return_service_date is today, how to do this?
This is my code (rails):
if settlement.return_service_date &&
settlement.return_service_date.to_s(:date) == Time.now.to_s(:date)
Is there a better way to do this?
In Rails, you can do:
settlement.return_service_date.today?
For everyone who isn't using rails, my plain ruby solution with Time looks like the following, if you have Dates instead of Times you can convert them with .to_time()
# current time
time = Time.new()
# some time
some_time = Time.new() + 7550
# set beginning of today
today_start = Time.new(time.year,time.month,time.day)
# set ending of today
today_end = today_start + 86399
# check if some_time is in between today start and end
puts (today_start..today_end).cover?(some_time)
Depending on your current time it prints true (if your day still has at least 7550 seconds left) or false.

Resources