Weird time inconsistencies between production and development - ruby-on-rails

For some reason times are appearing differently in development (my local Mac) and production (Heroku). Take a look: (Just prior to doing this I did a heroku db:pull, so the databases should be identical)
Production (Heroku)
>> Annotation.last.id
=> 2028
>> Annotation.last.created_at
=> Sat, 12 Sep 2009 06:51:33 UTC +00:00
>> Time.zone
=> #<ActiveSupport::TimeZone:0x2b4972a4e2f0 #tzinfo=#<TZInfo::DataTimezone: Etc/UTC>, #utc_offset=0, #name="UTC">
>> Time.now.zone
=> "PDT"
Development (my Macbook Pro)
>> Annotation.last.id
=> 2028
>> Annotation.last.created_at
=> Sat, 12 Sep 2009 09:51:33 UTC +00:00
>> Time.zone
=> #<ActiveSupport::TimeZone:0x23c92c0 #tzinfo=#<TZInfo::DataTimezone: Etc/UTC>, #utc_offset=0, #name="UTC">
>> Time.now.zone
=> "EDT"
Since the created_at times differ by 3 hours, I assume this is related to the 3 hour difference between EDT and PDT, but I'm not sure what's going on.
EDIT: Here's what the raw data looks like:
sqlite> Select created_at from annotations where id = 2028;
2009-09-12T09:51:33-04:00

It looks like this is a problem when moving databases between Heroku and your development machine. Running the SQL query directly gives me this:
Local: {"created_at"=>"2009-10-30 22:34:55.919586"}
Remote: {"created_at"=>"2009-10-31 01:34:55.919586"}
Same problem, exactly a three hour shift. Looking through the source for Taps, the Gem heroku uses for DB sync, does not provide any clues into what could be going wrong here. I've opened a heroku support ticket, and I'll update this post with what I find.
UPDATE:
Ricardo and Morten from Heroku replied. Specify the TZ on the command line like so:
TZ=America/Los_Angeles heroku db:pull

Looks like it assumes the dates in the DB are stored in your local time-zone which is different for the 2 environments and then it translates it to UTC. since the value in DB is essentially the same you get 2 different UTC values.
What is the value of config.time_zone from your "config/environment.rb"?
Also what is the value of "Select created_at from annotations where id= 2028" ?

This is a problem I've encountered before as well.
If you do something like Annotation.last.created_at on the rails console, the result already has the timezone applied to it. You should look at the "pure" date in mysql, via the mysql console :-)

Perhaps one or both of the machines has it's system time setting set to local timezone, instead of UTC. If that's the case, then it would be confused on what value the UTC time (int) actually is.
There are a number of areas to check on both machines:
system timezone
local(user) process timezone
database system timezone
database local timezone

This doesn't seem explicable by one or the other reporting its local time as the UTC time: Eastern time is 4 hours away from UTC, and Pacific time is 7 hours away, but you're seeing a three-hour difference, not a 4- or 7-hour difference.
It seems like both of them are producing incorrect output. SQLite is clearly saying that the 09:51 time is supposed to be an -04:00 time, i.e. Eastern time, but Rails is incorrectly claiming that it's UTC in one case — and in the other case, it's translating it from Eastern to Pacific, then incorrectly claiming that the result is UTC.
Maybe this is a bug in the SQLite backend for ActiveRecord? Because it seems like the kind of thing that would have been caught and squished long ago if it were in widely-used code.

Related

Mongoid 1.month.ago is returning 01 for 1st of March

I have a ROR app: ruby 2.3.0, rails 4.2.5.1 and mongoid 5.0, and in one of my models I have :
field :statement_month, default: 1.month.ago.strftime('%m') , but only on 1st of March it saves a wrong result: "01" instead of "02" .
I have no problems for other months in first day of the month.
I also added some logs, before_create and after_create , printing:
"-------1_month_ago_month------------------------" + 1.month.ago.strftime('%m') => in logs it show "02" but in DB object is "01". It is a mongoid issue, or maybe a TimeZone issue ?
The correct syntax for dynamic defaults uses Procs. See https://docs.mongodb.com/mongoid/master/tutorials/mongoid-documents/#defaults.
MongoDB stores times as UTC timestamps, your program does not explicitly convert to UTC thus it is 1) potentially misbehaving with respect to time zones and 2) potentially misbehaving with respect to daylight savings time. Date math generally must be explicitly performed in either local time (and you should know the time zone you are operating in) or in UTC. Mixing the two eventually causes problems.
To troubleshoot the wrong month, set your system time to March 1 and debug the program. In particular, try March 1 01:00 and March 1 23:00. Those are often different dates in UTC for the same local date.

Timezones in Rails 5 when using beginnig_of_month

My Rails 5 app is set to BRT (Brazilian timezone) and my Postgres database to UTC. I never had to worry about that because Rails always calculated the correct time when reading from or writing to the database. So for example when I had an input field with time = 15:00 it would write 18:00 to the database and when reading from the database it would return 15:00 again. Perfect!
But now I want to write the beginning of the month to the database:
Time.now.beginning_of_month which is 2017-04-01 00:00:00
Payment.create(
:time => Time.now.beginning_of_month
)
Now it writes exactly this (in BRT) to the database without converting to UTC. When I read this from the database later it converts it to BRT 2017-03-31 21:00:00 which is wrong.
Of course I could convert the time to UTC before saving it to the database. But I find it strange that Rails always took care of converting and in this case it does not.
I hope the problem became clear.
Any ideas?
I found a very good article about this subject:
https://www.varvet.com/blog/working-with-time-zones-in-ruby-on-rails/
Basically what I had to to is change Time.now to Time.current as Time.now ignores the timezone settings.

activerecord and timezones

Here's the situation. We have a rails 3.2 app running on heroku, and developers all over the world. We want to standardise on UTC for everything. Our team speaks UTC even among ourselves (eg. when planning meeting times). We want the app to do the same. We can convert things back into people's local timezones for display at some later point.
The problem is that when I save something into the database (for instance, creating a new post in the forums) it thinks that my local time (as reported by my computer) is UTC, and saves it as such.
For example, it's lunchtime on Thursday 14th February here in sunny Melbourne, Australia (UTC+11). I make a new post, then look in the console:
1.9.3p194 :009 > p = Post.first
=> #<Post id: 12, author_id: 1, subject: "Another post ", body: "a very recent one!", created_at: "2013-02-14 12:13:53", updated_at: "2013-02-14 12:13:53", slug: "test1-20130214-another-post", forum_id: 1>
1.9.3p194 :010 > p.created_at
=> Thu, 14 Feb 2013 12:13:53 UTC +00:00
As you can see, the created_at timestamp is saved as per lunchtime on the 14th, and flagged as being that time IN UTC. What I actually want is for it to look at the local time, be aware that I'm in Melbourne, and convert to UTC by (in this case) subtracting 11 hours.
Now, I know I can edit these lines in my config/application.rb:
config.time_zone = 'UTC'
config.active_record.default_timezone = 'UTC'
(I think changing config.time_zone to 'Australia/Melbourne' would fix it? Probably?)
However, if I change any of that to "Australia/Melbourne" (or whatever), then what happens to our deployed app (on Heroku), and my co-developers in Maryland and Oregon and Glasgow? Do we all have to set our times individually? Should we do it via environment variables, perhaps? Any other suggestions?
OK, so it looks like this fixes it for us:
config.time_zone = 'UTC'
config.active_record.default_timezone = :local
We're a little dubious and will wait and see how it goes when we deploy and/or run our test suite in everyone's timezones, but for now it seems like it works... in Melbourne at least ;)

Problems with saving and querying dates in Rails

I have an Appointment model, and for one particular appointment, I saved it having a start_time of 12:15am on 3/5/2011. Look at this:
irb(main):002:0> a = Appointment.find(15)
=> #<Appointment id: 15, start_time: "2011-03-05 05:15:00", created_at: "2011-03-05 03:42:03", updated_at: "2011-03-05 03:42:03", stylist_id: 13, client_id: 8>
irb(main):003:0> a.start_time
=> Sat, 05 Mar 2011 00:15:00 EST -05:00
As you can see, the date got saved wrong. Interestingly, though, Rails compensates for it when the data comes back out.
I assume my app has always behaved this way and I just didn't notice. Recently, though, I wrote a query that pulls the dates out with raw SQL, so I'm getting the wrong time and it's causing problems.
Can anyone shed some light on why this is happening and what I can do to get around the problem?
Rails does this on purpose. Check your time zone settings:
config.active_record.default_timezone
config.time_zone
http://guides.rubyonrails.org/configuring.html#configuring-active-record
Time zone features were introduced in 2.1 and haven't changed much. This article gives a good explanation:
http://mad.ly/2008/04/09/rails-21-time-zone-support-an-overview/
Rails save datetime in database in UTC time(zero offset) and converts the time to the time zone which we intend when it displays it. So, when we parse directly from the database, you will be getting UTC time. If you try to convert it into time like this:
time_string = #raw string obtained from database(like "2011-03-05 05:15:00")
time_object = Time.parse(time_string)
You will get the time with offset according to the timezone of your machine or server. Ruby parses time like that. It will take the time and give the timezone of your machine(which is incorrect here as the timezone is actually UTC as it is from database). I ran into this problem and solved it by adding UTC when I parse time from raw sql strings from database.
time_string << " UTC"
time_object = Time.parse(time_string) # gives time zone as UTC
then if you use it, the result will be correct.

Rails timestamps don't use the right timezone

I'm a bit confused about timezones in rails. I want my rails app to use British Summer Time (like daylight savings in the US) for the timestamps set in updated_at and created_at in my models. I changed my environment.rb to say
config.time_zone = 'London'
The ubuntu server my app is on seems to use BST for it's time: in the command line, for example, if i type 'date' i get the current time (not offset by an hour). In the rails console, i see the following:
>> time = Time.now
=> Wed Oct 27 16:29:17 +0100 2010
>> time.zone
=> "BST"
All fine. However, if i make a new AR model object and save it, the timestamps are from an hour ago. So, it looks like this is using UTC. Now, i can see the logic in this: since the timestamps might be used in the model logic, you want them to be based on an unvarying yardstick time, ie UTC. But, this is a weird bit of behaviour that i don't understand:
#change a record and save it
>> someobj.save
=> true
#object's updated_at is one hour ago
>> someobj.updated_at
=> Wed, 27 Oct 2010 15:34:22 UTC +00:00
>> Time.now
=> Wed Oct 27 16:34:31 +0100 2010
#however, Time.now - object's updated at is just a few seconds.
>> Time.now - someobj.updated_at
=> 15.305549
So, before doing the subtraction, updated_at is converted into the current time zone.
The reason i want to show the date in the current time zone is just for status reports etc in the views: if someone updates something i want them to see that it was updated '1 minute ago' not 'one hour ago'.
Can anyone unconfuse me? cheers, max
EDIT: My immediate problem, of showing the right time in the status, is solved by using the 'time_ago_in_words' helper, which adjusts for time zone. I'd still like someone to explain what's going on with the timestamps though :)
Timestamps are stored in UTC by default, and this is probably the best way to do it. If you move from one server environment to another, you don't want all of your times shifting around just because you switched time zones.
If you want to know what the timestamp is in your local time zone, you just have to ask for it that way:
someobj.updated_at.localtime
Note the offset listed at the end of the times -- the first offset is 0, the second is 1. When the time calculation occurs, the offset is included automatically, so that the subtraction gives you the correct result. someobj.updated_at and Time.now each displays its value in a different time zone, so they are really only 9 seconds apart, not 1 hour and 9 seconds.

Resources