preserving datetime values in rails while saving in utc(default) - ruby-on-rails

I have a a checkin:datetime field in rails and is using the default utc.
But the issue is that when the user submits the form the checkin date is coming with his local timezone info. So rails will automatically convert this to utc and depending on the difference with his timezone and utc there might be an off of one day.
So how can I change the date to utc without changing values?
Update
This is the only code I use for saving to database.(the utc conversion is done by activerecord(i think) if the passed in value is not utc)
reservation=current_user.reservations.create(reservation_params)
reservation.save

I have found a way to do that. It is not direct conversion as I expected but it works. I changed the form input field to sent a string with just the date and time(without utc).
eg: 2016-07-13 00:00:00
Then in my controller before saving I used below code to parse it to utc.
reservation.check_in= Time.zone.parse(value_from_view)
example:
reservation.check_in= Time.zone.parse('2016-07-13 00:00:00')
This returns Wed, 13 Jul 2016 00:00:00 UTC +00:00 as expected.

Related

Comparing dates in 2 different formats to match or not with Ruby on Rails 6

I'm trying to compare date that comes from ActionMailbox mail.date with a date field in my PostgreSQL DB Table to check if a post for the same date already exists. The dates comes in different format I guess, how canI format them in same way to compare? The time section is irrelevant.
Date format that comes from email as below I guess. Looking at the Logs on server
Date: Wed, 24 Mar 2021 09:57:57 +0000
Date format I have in the DB is as below. Output of Post.last in rails c
date: "2021-03-24 09:57:57.000000000 +0000
I need to check if dates matches or not?
Btw the interesting thing is, I can just save mail.date to db without any particular formatting, I guess it is formatting itself before saving.
Date format I have in the DB is as below.
Databases don't store timestamps nor dates as strings, they're stored as numbers. The string format is just for humans. Unless you're storing the date as a string.
I'm trying to compare date that comes from ActionMailbox mail.date with a date field in my PostgreSQL DB Table to check if a post for the same date already exists.
Those are standard date formats, RFC 2822 and ISO 8601. So long as your date column has a date type you don't need to convert them. Rails or Postgres will take care of the conversion.
Thing.where(date: mail.date)
However, your "date" field is storing a timestamp. It might be misnamed, or it might be mistyped. If you only want to store the date, use t.date in your migration.
If you did, you'd parse them into Time objects, then compare.
t1 = Time.zone.parse("Wed, 24 Mar 2021 09:57:57 +0000")
t2 = Time.zone.parse("2021-03-24 09:57:57.000000000 +0000")
p t1 == t2
Btw the interesting thing is, I can just save mail.date to db without any particular formatting, I guess it is formatting itself before saving.
Rails type conversion is parsing the String into an ActiveSupport::TimeWithZone object.
> thing = Thing.new
> thing.created_at = "Wed, 24 Mar 2021 09:57:57 +0000"
> thing.created_at
=> Wed, 24 Mar 2021 04:57:57.000000000 CDT -05:00
> thing.created_at.class
=> ActiveSupport::TimeWithZone

Changing ActiveRecord timezone for a session

I've been trying to change the timezone for a user session using the Time.zone method. I get the correct date and time when I do Time.zone.now but when I do a select query using ActiveRecord::Base.connection.select_all, the datetime columns in the result are always in UTC. What do I need to do so that all the select queries return data in the timezone that was set using the Time.zone method, without changing the views? Please help.
It's easiest to convert the Time to the local timezone with ruby.
Time.now.in_time_zone()
use it as such.
Time.now.in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
You can give the function a timezone string, or an UTC offset
http://apidock.com/rails/Time/in_time_zone

Which should I always parse date times with? DateTime, Time, Time.zone?

In Rails, I see I have a few options to parse a date and a date-time. What is the best practice for parsing dates for the sole purpose of persisting them into the DB via ActiveRecord? And why?
Time.zone.parse(...)
Time.parse(...)
DateTime.parse(...)
Go with Time.zone.parse if you just want to write into ActiveRecord.
DateTime should be avoided. If you're handling dates, you should use Date.parse instead.
Beyond that, it depends on whether the input comes with timezone information, what the current timezone is set to, and whether you want timezones in your data.
Time.zone.parse will return an ActiveSupport::TimeWithZone, defaulting to UTC.
> Time.zone.parse("12:30")
=> Thu, 10 May 2012 12:30:00 UTC +00:00
Time.parse will return a Time, with a zone if it's specified in the input, or the local TZ.
> Time.parse("12:30")
=> 2012-05-09 12:30:00 -0700
For a more detailed explanation of Ruby time comparisons and precision, read this blog post:
http://blog.solanolabs.com/rails-time-comparisons-devil-details-etc/
Times that are automatically set by ActiveRecord (e.g. created_at and updated_at) come back as ActiveSupport::TimeWithZone instances.
According to the documentation, ActiveSupport::TimeWithZone and Time have the same API, so Time is what I would use.
On an unrelated note, there are methods to_time and to_datetime that you can call on strings:
"2012-05-09".to_time # => 2012-05-09 00:00:00 UTC
"2012-05-09".to_datetime # => Wed, 09 May 2012 00:00:00 +0000

Rails: how to create Time object in specific time zone

My app is working in "Moscow" (+04:00) timezone. But sometimes I need to create time object by only local time (for example "01 may 2012 13:45") and name of ActiveSupport::TimeZone object (for example "Berlin": +02:00 in Summer Time and +01:00 otherwise).
For example if I get "01 may 2012 13:45" and "Berlin" as input I want to yield "2012-05-01 13:45:00 +0200" or "2012-05-01 11:45:00 +0000". I create following function:
def from_local_datetime(local_datetime, time_zone)
offset = Time.now.in_time_zone(time_zone).formatted_offset
datetime = case local_datetime
when String
DateTime.parse(local_datetime)
else
DateTime.new(local_datetime)
end.change(:offset => offset)
return datetime
end
And at the first look it works as I expected. But is it a best practice for this kind of task? May be in some situation It works with errors. I'm not definitely sure.
I would be greatful to any comments.
UPD: I think bug may occur about time when DST changing the time. For example 26 march 2011 was GMT+1 in Berlin time zone and Time.now.in_time_zone("Berlin").formatted_offset returns "GMT+1", but it would be GMT+2 in 27 march 2011. So if I call from_local_datetime("28 march 2011", "Berlin") before 27 march it returns 28 march 2011 00:00:00 +0100, but If I call it after changing the time my function returns 28 march 2011 00:00:00 +0200 :(
Your conversion method is the right approach.
With web sites, you should make sure times are stored as UTC in the database. If you can get the UTC value out of the database, instead of the local time (or maybe you can set your web server's time zone to UTC) it won't have to convert the time from UTC to local time, when you are going to then convert it to the user's timezone anyway.
And, of course, you will have to store the user's time zone preference.
TZInfo::Timezone.get('Europe/London')
Find the time zone
http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html

ActiveRecord DateTime field not matching object

i've got a Course model, which has a datetime attribute. If I look at it from the database i get one time, and if i look at it from the object i get a different date & time.
>> Course.last.attribute_for_inspect :datetime
=> "\"2012-01-04 01:00:00\""
>> Course.last.datetime
=> Tue, 03 Jan 2012 19:00:00 CST -06:00
Does anyone know why this value is different, and what I can do to fix it? The time from Course.last.datetime is correct, but my queries on the course table aren't working correctly due to the mix-up.
From the fine manual:
attribute_for_inspect(attr_name)
Returns an #inspect-like string for the value of the attribute attr_name.
[...]
Date and Time attributes are returned in the :db format.
So, when attribute_for_inspect is used for a datetime, it will return the string that the database uses for that value. Rails stores times in the database in UTC and any sensible database will use ISO 8601 for formatting timestamps on the way in and out.
2012-01-04 01:00:00 is the UTC timestamp in ISO 8601 format. When Rails returns a datetime, it converts it to an ActiveSupport::TimeWithZone instance and includes a timezone adjustment based on the timezone that you have configured in your application.rb. You're using the CST timezone and that's six hours behind UTC; subtracting six hours from 01:00 gives you 19:00 and you lose a day from crossing midnight. The human friendly format, Tue, 03 Jan 2012 19:00:00 CST -06:00, is just how ActiveSupport::TimeWithZone represents itself when inspected and the console uses x.inspect to display x.
As far as fixing your queries goes, just use t.utc before sending in a time t and you should be good.
Configuring your Rails App Timezone in application.rb
set config.active_record.default_timezone to :local as it is set to :utc by default in the application.rb
paste this code in your application.rb
config.active_record.default_timezone = :local #or :utc
config.time_zone = "Singapore" #your timezone

Resources