Rails messing up with my Postgres TIME columns - ruby-on-rails

I am using postgres TIME type to represent a branch office opening hour.
I am aware Rails does not handle the TIME column alone, initializing the values with a default date: 01 Jan 2000.
I have no problem with that except for the fact that my user timezone is -03, so when the user picks a time, let's say 22:00:00, Rails actually changes it to 01:00:00 and stores that value instead. Then, when I load it from the db it might even load a different date (the day before or after 01 Jan, which is a headache when I am comparing times).
How can I tell Rails to not change the original input to the server timezone? Or is there any other better workaround?

Rails always have TZ associated with times, it also stores everything in utc in DB. Out of the box, it does correct encoding/decoding. My assumption is - you have done your own decoding when you retrieve values from the DB. In this case, what you can do is to write helper like this:
def strip_tz(time)
Time.new(time.year, time.month, time.day, time.hour, time.min, time.sec, "+00:00")
end
And use it when you pass time value to the model creation.

Related

Postgresql date difference in table and on web

I have weird situation with my PostgreSQL db date value.
On my web site I have calendar for selecting a date and when I select some future date like "2018-09-23" in PostgreSQL table column it is saved as "2018-09-22 22:00:00"?
Obviously I am missing something. On web site all the time it shows okay time "2018-09-23" but at the table it is minus one day as you see above. Why?
Rails stores DateTime fields in UTC, but without marking their time zone as UTC. This corresponds to the timestamp without time zone type in postgres. So if your time zone is +2, it'll store the time as UTC (+0).
In Rails, Time.zone will return the current local timezone (you can add logic to change this by user, for example). When persisting a datetime, Rails will automatically convert the current Time.zone to UTC. However, it doesn't use the Postgres type that actually includes the time zone data, so it relies on convention to convert back and forth to the user's time zone.
If you really only care about the date, use the date type in your migration instead of Timestamp or DateTime.
Times and dates have a lot of subtle quirks and the "right" behavior depends on your use case. In some applications, you need to deal with "local" time when considering date transitions, and sometimes you need to finesse your application or database logic to think in terms of local time and sometimes you care about UTC time.

ActiveRecord's weird timezone handling when Daylight Saving Time changes

I have an application providing agenda functionalities.
Disclaimer: I really love Rails and AR's functionalities, this is just meant to ask how can I exploit it functionalities better the next time.
Since it has to be used by a european team it has been developed using +0200 timing informations. I know that the timezone stuff is handled by AR and the base data is always stored in UTC.
The issue I get (but I already solved, just wanted to get some feedback to think it better the next time) is that when I store a new appointment I also enforce "+0200" at the end of the DateTime.parse call (es. DateTime.parse("#{day} #{hour} +0200") and then I set it as record attribute. Of course 10:00 gets stored as 08:00 inside the data layer, and that's fine.
Then when I retrieve that data using the configured AR timezone (Rome for instance) using something like #appointment.start_at it correctly gets converted back when we are under the CEST daylight time. Issues start happening when start_at are set to be CET (+0100) dates (but I always store it inside the database enforcing CEST +0200).
Inside the database start_at is always the same, so the UTC representation is always 08:00 when saving an appointment with 10:00 but the AR conversion uses the wrong daylight conversion (CET).
I know I could skip this by simply working all on UTC so I don't have to carry out any consideration about daylight savings times, but it seems a bit messy that AR itself is not able to remember somehow that I stored it while CEST is running.
When performing queries I do the same as when storing, so I append "+0200" to all DateTimes, and all works back again, but when displaying I need to check whether the TimeZone is "CET" or "CEST" and add a +1.hour to CET (otherwise it results the appointment has been signed 1 hour before than the intended hour).
Is it right I should handle this by myself?
Shouldn't be AR able to perform this kind of check since it wraps the data-layer?
How should I design an application the next time to avoid this kind of issue while still using local timezones? (as I said using UTC may also not be applicable by some requirements)
I thought I should change "+0200" with "in_time_zone" the next time, but I also allow users to write by hand an hour to search, and of course this will always be the same time either it is "CET" or "CEST" and of course I cannot enforce users to write following the UTC format.
Thank you,

How to deal with UTC times and Rails/ActiveRecord

I'm using Rails 3.2.8. When I generate a scaffold with a time field or datetime field. The HTML form field gets pre-populated with the current date/time in UTC. Great that it's pre-populated with the current date/time, but because it's in UTC, we have to change the time back 7 hours every time (which will sometimes require setting the day back 1 also, possibly month and year too). And then it seems the UTC time gets stored in the database, so I'll have issues displaying/editing it as well if I recorded it in our local time.
I looked at the Ruby documentation for the form helpers to deal with local time better, but I don't see anything relevant.
What's the best way to deal with editing and displaying dates and times in Rails?
UPDATE: To clarify, I like the idea that UTC time is stored in the database. I just want the time_select and datetime_select form helpers to edit the times in the user's local timezone. And, of course, I want to easily display the time in the user's local timezone.
You have to set the time zone to display on a per-user basis somewhere in a before/around_filter. This has to be done like:
Time.zone = "Kyiv"
An example can be found in its API: http://api.rubyonrails.org/classes/Time.html#method-c-zone-3D.
Run the rake time:zones:all to see all of them.
All the conversions has to be handled for you behind the scene.
This has helped me in the past. You can do things like:
Time.now.in_time_zone("Central Time (US & Canada)")
See Rails: convert UTC DateTime to another time zone

Problem with Time Comparison in Rails AR Query

I'm running Rails 3.0.4 with a PostgreSQL Database and i created a DateTime field called ordered_at.
I set it by creation with Time.now and i want to query the ordered_at field with something like where("ordered_at > ?", params[:later_than], but if I put later than with something like 1.minute.from_now, I'm getting results where no results should be.
In Detail: ordered_at is 2011-03-08 11:11:49 and params[:later_than] is 2011-03-08 11:09:58 UTC.
I think that is because of the time zone in the Time object, which doesn't exists in the database (The ordered_At field is a timestamp without time zone Datatype).
Does anyone had the same problem and can help me?
Fixed it.
The root of evil was in my fixtures.
You have to set times in your fixtures like that:
ordered_at: <%= (30.minutes.ago).iso8601 %>
otherwise you will get the timezone of the database/server
The thing you have to remember is Rails store the time in your database in UTC. Rails convert the time into UTC and sets it as the standard to look at in the database. So if you have params[:later_than] as 2011-03-08 11:11:49 UTC or any time for that matter with any time zone, Rails will convert it into UTC time zone before inserting into the SQL query. If you are saving Time.now into the database, Time.now will be the server time or local machine time with the time zone in which the machine is set. Before saving to database, Rails will convert it to UTC and save it. So, at database level everything will be converted to UTC and then will be compared so avoiding confusion.

Odd time store behaviour in ActiveRecord

I have the following entry in my database:
t.time :my_time_stamp
In my controller I update it like this:
model.update_attributes(:my_time_stamp => Time.now.utc)
I can see that I'm writing:
Mon 9 November, 8:54.54 UTC 2009
However, when I later read this value I get:
Sat Jan 01 08:54:54 UTC 2000
It seems that the time part has been stored but not the date part. I'd expect that because it's a time field, but why do I end up storing and retrieving a date? I guess I must be misunderstanding how this works in some fundamental way.... what am I doing wrong?
What I need to do is calculate the time in seconds since the update to the database.... is there an easier way to do this?
Thanks!
A "time" field is storing only the time. Not the date.
So it's logic for you to get only a valid time and not a valid date.
If you wish to store date and time in the database, you should have the following migration :
t.datetime :my_time_stamp
Ruby does not have a dateless time object, so it converts the dateless time field in your database to that time on some arbitrary day. Ruby's Time objects encode a number of seconds since the unix epoch, so they include the date. The correct way to store them in the database is with a datetime field, or if you prefer, you could use an integer field and store Time.now.to_i.

Resources