简体   繁体   中英

Rails 5 how to save difference in hour and minutes between two datatime in postgres?

my table has 3 columns: data type timestamp,

|created_At  |  final_time| duracion(difference between created at and final_time)
|            |            |

the column difference should save the difference in hours and minutes, in this format HH:MM

this is my controller:

def horario
  horario.update(duracion: params[:duracion]) // this params is "00:59"
end

but in the table Horarios, in column duracion i have this:

2017-12-24 03:59:00 

so i want to save 00:59 (59 minutes) but postgres save all current date and add 3 hours more. i want to save so in the future i will be able tu sum column duracion. Or should i change data type for this column? In this case which datatype you recomend me for rails to save HH:MM??

thanks.

Rails 5 supports PostgreSQL's interval type to some extent. You can create interval columns in the usual way and they will be properly represented in db/schema.rb . You can also assign them values in the usual way so you can say things like:

model.some_interval = '6 hours'

and get 06:00:00 inside the database. However, there is nothing in Ruby or Rails that properly represents a time interval (we only have various timestamp and date classes) so when that interval comes out of the database, you'll have a string on your hands, ie:

> model = Model.find(some_id)
> model.some_interval.class
 => String

so you might end up having to manually parse some strings in Ruby. For simple intervals like '6 hours' , this will be easy but it won't be so easy with more complicated intervals like '6 years 23 days 11 hours' .

If you'll only be working with your time intervals inside the database then interval would be natural and easy, you can say things like:

select some_timestamp + some_interval

and

where some_timestamp + some_interval < some_other_timestamp

and everything will work nicely.

However, if you need to work with the intervals back in Ruby then you'd probably be better off storing the interval as a number of seconds in an integer column (or whatever resolution you need). Then you could say things like:

where some_timestamp + (some_interval_in_seconds || 'seconds')::interval < some_other_timestamp

inside the database and

some_time + model.some_interval_in_seconds

back in Ruby.

In any case, strings are probably the wrong approach unless you really like parsing strings everywhere all the time.

As others already pointed out, Rails handles the Postgres Interval type as a string. A string that, unfortunately, is not easy to parse.

If you do this:

u = Users.select('edited_at - created_at as time_dif')
puts u.first['time_dif']

You can get something like 168 days 12:51:20.851115 . Ugly right?

Well, using Ruby to convert this string into an useful number is not easy, but you can use Postgres to do the job for you. You will need to do a plain SQL query though, but it's the best method I've found so far:

query = ActiveRecord::Base.connection.execute("
  SELECT EXTRACT(epoch FROM time_dif)/3600 as hours_dif
  FROM
    (
      SELECT (edited_at - created_at) as time_dif
      FROM users
    ) AS MainQuery
")

In this example, Postgres' EXTRACT function will convert the Interval type into a number which represents the total seconds of the interval. If you divide this number by 3600 you will get the different in hours as in the example above.

Then, if you want to iterate over the results:

query.each do |r|
  puts r['hours_dif']
end

You could save duracion as a float type, where duracion would equal something like final_time - created_at and this value would be the difference in seconds. You can then perform arithmetic with these values and always convert back to minutes, hours, or whatever you need.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM