compare times with milliseconds - ruby

I have two dates a start date and an end date. I want to get a new time object which is the difference between the two. The differences I am concerned with are Hours, Minutes, Seconds and Milliseconds. I need to be able to create a new Time object from the result that includes the milliseconds difference
>> require 'time'
=> true
>> start_time = Time.parse '1970-01-01T00:00:00.200'
=> 1970-01-01 00:00:00 +0000
>> end_time = Time.parse '1970-01-01T01:01:01.400'
=> 1970-01-01 01:01:01 +0000
>> difference = Time.at(end_time - start_time)
=> 1970-01-01 01:01:01 +0000
my problem is that difference does not have the milliseconds
I can see that the Time has milliseconds by running
>> difference.strftime('%H:%M:%S.%L')
=> "01:01:01.199"
but how do I access the milliseconds that are in the Time difference object.
it is critical I have milliseconds as I am working in sub-second calculations?
UPDATE
I don't think my first attempt at this question was as descriptive as it should of been, my apologies for that.

require 'time'
a = Time.now
sleep(0.5)
b = Time.now
b - a
# => 0.505087
Milliseconds!
EDIT: Microseconds!

my problem is that difference does not have the milliseconds
It does have the milliseconds, Time#to_s / Time#inspect just doesn't show it. Its output is equivalent to: strftime "%Y-%m-%d %H:%M:%S %z"
how do I access the milliseconds that are in the Time difference object.
usec returns the microseconds and nsec returns the nanoseconds:
time = Time.at(0.2)
time.usec #=> 200000
time.nsec #=> 200000000
For milliseconds you could use
time.usec / 1000 #=> 200

Ruby's Time class has nanosecond precision: you can use Time#to_f to get a fractional number of seconds since the Unix epoch. If you subtract two Time objects, you'll get a fractional number of seconds between them. Thus, to get the number of milliseconds between two times, try:
((time2 - time1) * 1000).to_i

Related

Ruby Date Time Subtraction

I am trying to calculate the exact duration a process took from some log file result. After parsing the log, I reached at the following stage:
my_array = ["Some_xyz_process", "Start", "2018-07-12", "12:59:53,397", "End", "2018-07-12", "12:59:55,913"]
How can I subtract the start date and time from the end date and time in order to retrieve the exact duration the process took?
my_array = ["Some_xyz_process",
"Start", "2018-07-12", "12:59:53,397",
"End", "2018-07-12", "12:59:55,913"]
require 'date'
fmt = '%Y-%m-%d%H:%M:%S,%L'
is = my_array.index('Start')
#=> 1
ie = my_array.index('End')
#=> 4
DateTime.strptime(my_array[ie+1] + my_array[ie+2], fmt).to_time -
DateTime.strptime(my_array[is+1] + my_array[is+2], fmt).to_time
#=> 2.516 (seconds)
See DateTime#strptime and DateTime# (the latter for format directives). As long as the date and time formats are known I always prefer strptime to parse. Here's an example of why:
DateTime.parse 'Theresa May has had a bad week over Brexit'
#=> #<DateTime: 2018-05-01T00:00:00+00:00 ((2458240j,0s,0n),+0s,2299161j)>`.
You can concat the date and time field and use Time.parse to convert it to a time object and then calculate the difference in number of seconds
Time.parse('2018-07-12 12:59:55,397').to_i - Time.parse('2018-07-12 12:59:53,913').to_i
Hope this helps

In ruby, how to convert epoch milli seconds into Time Object without losing milli seconds information?

I want to convert epoch milli seconds into Time without losing mill second information. Is it doable?
You should use Time.at and then usec method to get the microseconds of time.
epoch_milli = 1479383961245
t = Time.at(epoch_milli / 1000.0) # => 2016-11-17 09:59:21 -0200
micro = t.usec # => 244999
milli = micro / 1000.0 # => 244.999

Get seconds from the day to Thursday 10:00 at that week

How do I get the seconds from the day to Thursday 10:00 at that week? If later than Thursday 10:00, I want to get zero. For example:
seconds = (Thursday 10:00) - Time.now
Use Chronic:
require 'chronic'
Chronic.parse('this Thursday at 10:00 am') - Time.now
#=> 98688.251918432
You can subtract two time to get difference in seconds (see docs):
require 'time'
Time.parse(end_time) - Time.parse(time)
# => 57600.0
Update
To calculate difference between two time getting two fixed time is an absolute must. You can get time for next week simply by adding numeric time difference in seconds to existing time. Here:
next_week_time = Time.parse(end_time) + (1*7*24*60*60)
Or if you are on Rails, with ActiveSupport you can simply do:
next_week_time = Time.parse(end_time) + 1.weeks
(4-Time.now.wday-1)*24*3600: get the number of days from the day morning to Thursday of the week.
Time.now.seconds_until_end_of_day: get the rest seconds of the day.
seconds = (4-Time.now.wday-1)*24*3600 + Time.now.seconds_until_end_of_day + 10*3600
seconds = seconds > 0 ? seconds : 0

How do I calculate the offset, in hours, of a given timezone from UTC in ruby?

I need to calculate the offset, in hours, of a given timezone from UTC in Ruby. This line of code had been working for me, or so I thought:
offset_in_hours = (TZInfo::Timezone.get(self.timezone).current_period.offset.utc_offset).to_f / 3600.0
But, it turns out that was returning to me the Standard Offset, not the DST offset. So for example, assume
self.timezone = "America/New_York"
If I run the above line, offset_in_hours = -5, not -4 as it should, given that the date today is April 1, 2012.
Can anyone advise me how to calculate offset_in_hours from UTC given a valid string TimeZone in Ruby that accounts for both standard time and daylight savings?
Thanks!
Update
Here is some output from IRB. Note that New York is 4 hours behind UTC, not 5, because of daylight savings:
>> require 'tzinfo'
=> false
>> timezone = "America/New_York"
=> "America/New_York"
>> offset_in_hours = TZInfo::Timezone.get(timezone).current_period.utc_offset / (60*60)
=> -5
>>
This suggests that there is a bug in TZInfo or it is not dst-aware
Update 2
Per joelparkerhender's comments, the bug in the above code is that I was using utc_offset, not utc_total_offset.
Thus, per my original question, the correct line of code is:
offset_in_hours = (TZInfo::Timezone.get(self.timezone).current_period.offset.utc_total_offset).to_f / 3600.0
Yes, use TZInfo like this:
require 'tzinfo'
tz = TZInfo::Timezone.get('America/Los_Angeles')
To get the current period:
current = tz.current_period
To find out if daylight savings time is active:
current.dst?
#=> true
To get the base offset of the timezone from UTC in seconds:
current.utc_offset
#=> -28800 which is -8 hours; this does NOT include daylight savings
To get the daylight savings offset from standard time:
current.std_offset
#=> 3600 which is 1 hour; this is because right now we're in daylight savings
To get the total offset from UTC:
current.utc_total_offset
#=> -25200 which is -7 hours
The total offset from UTC is equal to utc_offset + std_offset.
This is the offset from the local time where daylight savings is in effect, in seconds.

Number of days between two Time instances

How can I determine the number of days between two Time instances in Ruby?
> earlyTime = Time.at(123)
> laterTime = Time.now
> time_difference = laterTime - earlyTime
I'd like to determine the number of days in time_difference (I'm not worried about fractions of days. Rounding up or down is fine).
Difference of two times is in seconds. Divide it by number of seconds in 24 hours.
(t1 - t2).to_i / (24 * 60 * 60)
require 'date'
days_between = (Date.parse(laterTime.to_s) - Date.parse(earlyTime.to_s)).round
Edit ...or more simply...
require 'date'
(laterTime.to_date - earlyTime.to_date).round
earlyTime = Time.at(123)
laterTime = Time.now
time_difference = laterTime - earlyTime
time_difference_in_days = time_difference / 1.day # just divide by 1.day
[1] pry(main)> earlyTime = Time.at(123)
=> 1970-01-01 01:02:03 +0100
[2] pry(main)> laterTime = Time.now
=> 2014-04-15 11:13:40 +0200
[3] pry(main)> (laterTime.to_date - earlyTime.to_date).to_i
=> 16175
To account for DST (Daylight Saving Time), you'd have to count it by the days. Note that this assumes less than a day is counted as 1 (rounded up):
num = 0
cur = start_time
while cur < end_time
num += 1
cur = cur.advance(:days => 1)
end
return num
Here is a simple answer that works across DST:
numDays = ((laterTime - earlyTime)/(24.0*60*60)).round
60*60 is the number of seconds in an hour
24.0 is the number of hours in a day. It's a float because some days are a little more than 24 hours, some are less. So when we divide by the number of seconds in a day we still have a float, and round will round to the closest integer.
So if we go across DST, either way, we'll still round to the closest day. Even if you're in some weird timezone that changes more than an hour for DST.
in_days (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:
date_1 = Time.parse('2020-10-18 00:00:00 UTC')
date_2 = Time.parse('2020-08-13 03:35:38 UTC')
(date_2 - date_1).seconds.in_days.to_i.abs
# => 65
Here is a link to the corresponding PR.
None of these answers will actually work if you don't want to estimate and you want to take into account daylight savings time.
For instance 10 AM on Wednesday before the fall change of clocks and 10 AM the Wednesday afterwards, the time between them would be 1 week and 1 hour. During the spring it would be 1 week minus 1 hour.
In order to get the accurate time you can use the following code
def self.days_between_two_dates later_time, early_time
days_between = (later_time.to_date-early_time.to_date).to_f
later_time_time_of_day_in_seconds = later_time.hour*3600+later_time.min*60+later_time.sec
earlier_time_time_of_day_in_seconds = early_time.hour*3600+early_time.min*60+early_time.sec
days_between + (later_time_time_of_day_in_seconds - early_time_time_of_day_in_seconds)/1.0.day
end

Resources