ActiveRecord returns “argument out of range” for Time of 26:12
-
07-03-2021 - |
Question
It seems that ActiveRecord and Ruby's Time object doesn't play nice with times that are over 24:00. However my database has a lot of those, as they represent a time after midnight but considered to be on the same day. This is a design decision of my data provider:
Note: Trips that span multiple dates will have stop times greater than 24:00:00. For example, if a trip begins at 10:30:00 p.m. and ends at 2:15:00 a.m. on the following day, the stop times would be 22:30:00 and 26:15:00. Entering those stop times as 22:30:00 and 02:15:00 would not produce the desired results.
How do I deactivate this "validation" of my Time objects? It was suggested that I write my time field as a varchar within the MySQL query but I'm looking for a solution that doesn't make the database intervene.
UPDATE: Regarding the relevance of keeping the > 24:00 format, querying and asc ordering all the stop_times for a stop using that format would return something like: 23:45, 23:54, 24:35, 24:55, 25:15, which is the actual order in which the next buses are coming.
Whereas going the other way would return: 0:35, 0:55, 1:15, 23:45, 23:54 (this is wrong, the first 3 stop_times are supposed to be sorted after the last 3).
Solution 2
Although @Tilo is right that this is probably not the best time format there is, I had to stick to them and so ended up using raw SQL to convert the time column into a string:
sql = "SELECT CONVERT(stop_time, CHAR) as stop_time from stop_times"
stop_times = ActiveRecord::Base.connection.select_all sql
time = stop_times[0]['stop_time'] # => returned as a string
OTHER TIPS
the note in your question is talking about a TimeDifference, not a Time object.
I think you should model the TimeDifference as a decimal or float field in the DB, with a value equal to the time difference in numbers of seconds.. and then when you print it out, you can re-format it as Days, Hours, Minutes.
Please note that this is how subtracting Time objects works in Ruby:
> t1 = Time.now
=> 2011-11-22 20:54:54 -0800
> t2 = Time.now.tomorrow
=> 2011-11-23 20:54:59 -0800
> t2 - t1
=> 86404.928009862 # these are seconds! just store this in a float or decimal field
> (t2 - t1).class
=> Float # NOT a Time object
Trying to store the time difference in a Time object or Time DB-field seems wrong.
If you get weird input values from an outside source, like "26:12" , better to convert them to seconds:
hours,minutes = "26:12".split(/:/).map(&:to_i)
seconds = hours * 3600 + minutes * 60
... and then store the time-difference in seconds
wow, this comes from a Google API?? YIKES!!!
Still, don't store it in a Time object.
you can use divmod() to clean things up:
tomorrow,real_hour = hour.divmod(24)
where "tomorrow" stores the number of days in the future, and real_hour is a valid value for a 24-hour clock.
How about making a separate Class "TripTime" for those start and stop times, and have your own attributes: hours, minutes, days ? Airlines typically show arrival times as "03:12 +1" .. meaning +1 day in the future.
... or store just those weird values as a string, that keeps the sort-order, and to calculate the difference in hours/minutes/seconds, you can then convert them as needed as shown above with split
and map