Rails timestamp different than mysql timestamp - ruby-on-rails

My rails application has a Photo model which has a DATETIME column called taken_at.
I'm trying to understand why I get a different timestamp (num of secs since epoch) using the following methods:
Photo.find(3095).taken_at.to_i # => 1307292495
Photo.select("id, UNIX_TIMESTAMP(taken_at) as timestamp").where(:id => 3095).first['timestamp'] # => 1307317695
Photo.find(3095).taken_at.strftime('%s') # => "1307267295"
I'm using MySQL as the database and I'm using rails 4.1.7
thanks!

So the reason that using UNIX_TIMESTAMP(taken_at) in MYSQL gives a different result is because MySQL assumes that taken_at is a date stored in the server's time zone and converts it to an internal value in UTC before making the timestamp calculation. But taken_at isn't a date stored in the server's time zone, it is a date stored in UTC because ActiveRecord stores all datetimes as UTC in the database and then converts them back to the local time zone when you read the record back from the database.
references:
http://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_utc-timestamp

Related

Using datetime from Rails in Phoenix with Ecto

I have a database created in a Rails application and now I want use this same database in a Phoenix application (using Ecto). The major problem at this time is with datetime values.
The Rails application show me things like this:
Tue, 06 Feb 2018 00:13:36 -02 -02:00
And the Ecto query on the same table show:
[{{2018, 2, 6}, {2, 13, 36, 22082}}]
Note the timezone lost.
The DB is schemaless on phoenix application and this is necessary.
So, I want keep the timezone information and convert the data to a more useful format for calculations.
Is there a way to do this?
Ecto ~ 3.0
Phoenix ~ 1.2.1
Rails ~ 5
Rails does not store time zone data in the database. All datetime values are stored in UTC. When you query a datetime field on an ActiveRecord object, it converts the output to the time zone set in your application.rb file using config.time_zone =.

Save only time in rails

I have two time fields in the table i.e. start_time and end_time .
When I execute MyModel.save(start_time: '12:34'), it gets saved with appending a date(Sat, 01 Jan 2000 07:25:00 UTC +00:00).
I want to save time only. I am using Rails5
When we execute, a = MyModel.save(start_time: '12:34'), it saves only time in the database. But when we display the value in the console(a.start_time), we gets time with date. So we have to retrieve time from it by the following:
a.start_time.strftime("%H:%M")
Data gets correctly saved in the database
Rails will always append date with time even its type is "time".
You can achieve storing only time using sql query.
But i would suggest you to use one of the following options:
1)store time in column of type fixnum or decimal in 24hrs format like "22.44"
2) Store number of seconds in column of type integer & then write a helper method to confirm it into time.
3)Use column type :time and ignore the date whole querying.

Using default application timezone with DateTime.strptime in Rails 5

I read similar questions but did not find a solution.
My Rails 5 App is in
'America/Sao_Paulo'
time zone but saves all times in UTC to the database.
When saving a datetime
'2017-01-01 19:00:00'
to my database it will convert it to UTC and save the UTS time to the database.
Now I need to convert a string to datetime using:
DateTime.strptime(date + ' ' + time, '%d/%m/%Y %H:%M:%S')
And this will take the date and time values as they are and save them to the database without converting to UTC. How can I let my app know that my the datetime is not in UTC yet?
ActiveRecord (by default) will save any datetime to UTC. So, in my opinion you don't need to worry if the time in the database isn't yet in UTC, because ActiveRecord will convert it to UTC.
So, when saving to database, ActiveRecord will always convert it to UTC (whether it is in UTC or not).
And when fetching from database, it follows your configuration in config/application.rb
You can find in the file config.time_zone = 'YOUR_TIMEZONE'.
Reference:
http://api.rubyonrails.org/classes/ActiveRecord/Timestamp.html
Hope it helps, cheers!

rails group by utc date

I have a time field in table "timestamp without time zone". When record is saved to database, the utc time might be a different day compared to the local time. However, I need to group the records by date. Hence, I am doing something like this:
result = transmissions.joins(:report).where('reports.time::timestamp::date = ?', record.time.to_date)
The problem is if the utc date is on a different date than local time, then that record is not included in result. Any ideas how to get the right result?
And apparently I cannot change the "without time zone" either:
Rails database-specific data type
It says:
"concluded that the default ActiveRecord datetime and timestamp column types in schema migrations cannot be modified to force PostgreSQL to use timestamp with time zone."
So I have no idea how to group by date, as obviously something like this is wrong:
Unit.where(id: 1100).first.reports.order("DATE(time)").group("DATE(time)").count
=> {"2013-12-14"=>19, "2013-12-15"=>5}
That return value is completely wrong. All 25 records should be on 2013-12-14 and 0 records on 2013-12-15.
Assuming your records are timestamped with a particular UTC offset, you can try passing in the start and end times of the date in question in UTC format to your query:
result = transmissions.joins(:report).where('reports.time >= ? AND reports.time < ?', record.time.midnight.utc, (record.time.midnight + 1.day).utc)
Explanation:
midnight is a Rails method on an instance of Time that returns the Time object that represents midnight on the date of the original Time object. Similarly, record.time.midnight + 1.day returns the Time object representing midnight of the following day. Then, converting both Time objects – which are presumably timestamped in a standard UTC offset – to UTC creates a time period representing midnight-to-midnight for the system timezone in UTC format (not midnight in UTC time), which is precisely what you're seeking to query.
How about something like result = transmissions.joins(:report).where('reports.time >= ? AND reports.time <= ?', record.time.beginning_of_day.utc, record.time.end_of_day.utc)
The .utc part may not be necessary.

Rails. How to store time of day (for schedule)?

I'm writing an app that keeps track of school classes.
I need to store the schedule. For example: Monday-Friday from 8:am-11am.
I was thinking about using a simple string column but I'm going to need to make time calculations later.
For example, I need to store a representation of 8am, such as start_at:8am end_at:11am
So how should I store the time? What datatype should I use? Should I store start time and number of seconds or minutes and then calculate from there? or is there an easier way?
I use MySQL for production and SQLite for development.
I made an app recently that had to tackle this problem. I decided to store open_at and closed_at in seconds from midnight in a simple business hour model. ActiveSupport includes this handy helper for finding out the time in seconds since midnight:
Time.now.seconds_since_midnight
This way I can do a simple query to find out if a venue is open:
BusinessHour.where("open_at > ? and close_at < ?", Time.now.seconds_since_midnight, Time.now.seconds_since_midnight)
Any tips for making this better would be appreciated =)
If you're using Postgresql you can use a time column type which is just the time of day and no date. You can then query
Event.where("start_time > '10:00:00' and end_time < '12:00:00'")
Maybe MySQL has something similar
Check out the gem 'tod' for Rails 4 or Time_of_Day for Rails 3. They both solve the problem of storing time in a database while using an an Active Record model.
SQL has a time data type but Ruby does not. Active Record addresses this difference by representing time attributes using Ruby’s Time class on the canonical date 2000-01-01. All Time attributes are arbitrarily assigned the same dates. While the attributes can be compared with one another without an issue, (the dates are the same), errors arise when you attempt to compare them with other Time instances. Simply using Time.parse on a string like ”10:05” adds today’s date to the output.
Lailson Bandeira created a created solution for this problem, the Time_of_Day gem for Rails 3. Unfortunately the gem is no longer maintained. Use Jack Christensen’s ‘tod’ gem instead. It works like a charm.
This ruby gem converts time of day to seconds since midnight and back. The seconds value is stored in the database and can be used for calculations and validations.
Define the time of day attributes:
class BusinessHour < ActiveRecord::Base
time_of_day_attr :opening, :closing
end
Converts time of day to seconds since midnight when a string was set:
business_hour = BusinessHour.new(opening: '9:00', closing: '17:00')
business_hour.opening
=> 32400
business_hour.closing
=> 61200
To convert back to time of day:
TimeOfDayAttr.l(business_hour.opening)
=> '9:00'
TimeOfDayAttr.l(business_hour.closing)
=> '17:00'
You could also omit minutes at full hour:
TimeOfDayAttr.l(business_hour.opening, omit_minutes_at_full_hour: true)
=> '9'
I would store the starting hour and the duration within the database, using two integer columns.
By retrieving both values, you could convert the starting hour as in (assuming that you know the day already:
# assuming date is the date of the day, datetime will hold the start time
datetime = date.change({:hour => your_stored_hour_value , :min => 0 , :sec => 0 })
# calculating the end time
end_time = datetime + your_stored_duration.seconds
Otherwise, hava a look at Chronic. The gem makes handling time a little bit easier. Note that the changemethod is part of rails, and not available in plain Ruby.
The documentation on DateTime for plain Ruby can be found here.
Also, whatever you do, don't start storing your dates/time in 12-hour format, you can use I18nin Rails to convert the time:
I18n.l Time.now, :format => "%I.%m %p", :locale => :"en"
I18n.l Time.now + 12.hours, :format => "%I.%m %p", :locale => :"en"
You can also get from this notation, that you can store you duration in hours, if you want, you can then convert them rather easily by:
your_stored_value.hours
if stored as an integer, that is.
Suggestion:
Don’t worry about a specific datatype for that. A simple solution would be:
In the database, add an integer type column for start_time and another for end_time. Each will store the number of minutes since midnight.
Ex: 8:30am would be stored as 510 (8*60+30)
In the form, create a select field (dropdown) that displays all available times in time format:Ex.: 10am, 10:30am and so on.
But the actual field values that get saved in the database are their integer equivalents:
Ex: 600, 630 and so on (following the example above)
I assume you are using some kind of database for this. If you are using MySQL or Postgresql, you can use the datetime column type, which Ruby/Rails will automatically convert to/from a Time object when reading/writing to the database. I'm not sure if sqlite has something similar, but I imagine it probably does.
From the SQLite 3 website,
"SQLite does not have a storage class set aside for storing dates and/or times. Instead, the built-in Date And Time Functions of SQLite are capable of storing dates and times as TEXT, REAL, or INTEGER values:
TEXT as ISO8601 strings ("YYYY-MM-DD HH:MM:SS.SSS").
REAL as Julian day numbers, the number of days since noon in Greenwich on November 24, 4714 B.C. according to the proleptic Gregorian calendar.
INTEGER as Unix Time, the number of seconds since 1970-01-01 00:00:00 UTC.
Applications can chose to store dates and times in any of these formats and freely convert between formats using the built-in date and time functions."
You can then manipulate the values using the Date and Time functions outlined here.

Resources