Rails Time.zone.name is not the same Time.now.getlocal - ruby-on-rails

In rails console if I run the following commands I get:
>> Time.zone.name
=> "UTC"
>> Time.now.getlocal
=> 2015-08-06 16:34:57 -0400
I'm in NYC and the utc offset is -4 hours so the getlocal seems to get the system time zone rather than the environment time zone. This becomes an issue when using the String to_time method:
>> s = "2015-08-06 16:34:57"
=> "2015-08-06 16:34:57"
>> s.to_time(:utc)
=> 2015-08-06 16:34:57 UTC
>> s.to_time(:local)
=> 2015-08-06 16:34:57 -0400
>> s.to_time
=> 2015-08-06 16:34:57 -0400
Any ideas why? Can I force it to look at the environment time zone rather than the system time zone?
My main motivation is that my tests are run locally (so system time is in NYC), but my app is hosted on heroku which has UTC server time, so this caused an issue where a bug slipped through my tests and into production =/

You can do the following:
"2015-08-06 16:23:25".to_time.in_time_zone
The default behavior of to_time is to use :local according to the API doc (either :local or :utc are valid). For whatever reason, using in_time_zone appears to respect your configured timezone.
To expand on your above posted examples (from my machine):
» Time.zone.name
=> "UTC"
» Time.now
=> "2015-08-06T13:49:05.195-07:00"
» Time.zone.now
=> Thu, 06 Aug 2015 20:49:17 UTC +00:00
» time_string = "2015-08-06 16:34:57"
=> "2015-08-06 16:34:57"
» time_string.to_time
=> "2015-08-06T16:34:57.000-07:00"
» time_string.to_time.in_time_zone
=> Thu, 06 Aug 2015 23:34:57 UTC +00:00
EDIT: Based on the comment below, you can also do the following to not have any time shift added:
» DateTime.parse(time_string)
=> Thu, 06 Aug 2015 16:34:57 +0000
This will use the configured timezone (UTC in my case)

Related

Converting time zone of Rails ActiveRecord timestamps

I extended the Time class in my Rails projects so I can easily get the time in NYC:
/lib/extensions.rb .
class Time
# Get NYC time:
def nyc
self.in_time_zone('Eastern Time (US & Canada)')
end
end
Testing it out, looks good:
time_a = Time.now.utc.nyc
=> Sun, 21 Apr 2019 18:42:12 EDT -04:00
The problem is when I pull timestamps from the DB:
time_b = object.created_at.in_time_zone('Eastern Time (US & Canada)')
=> Sun, 21 Apr 2019 17:22:04 EDT -04:00
time_c = object.created_at.nyc
=> Sun, 21 Apr 2019 17:22:04 UTC +00:00
Super confused. Converting the timestamp to EDT works when I use in_time_zone in the console, but not when I use the extension? Even though my extension method works on Time objects I create in console? What's happening here?
(Note: Time instances in Rails are in fact instances of ActiveSupport::TimeWithZone. "TimeWithZone instances implement the same API as Ruby Time instances, so that Time and TimeWithZone instances are interchangeable." - ActiveSupportTimeWithZone)
you would need to patch ActiveSupport::TimeWithZone instead of Time, e.g.
class ActiveSupport::TimeWithZone
def nyc
in_time_zone('Eastern Time (US & Canada)')
end
end
Time.zone.now.nyc # => Mon, 22 Apr 2019 06:44:41 EDT -04:00
User.last.created_at.nyc # => Sun, 21 Apr 2019 13:34:45 EDT -04:00
https://api.rubyonrails.org/classes/ActiveSupport/TimeWithZone.html
(edit: I previously said "DateTime" instead of "ActiveSupport::TimeWithZone")

Ruby upgrade (from 1.8.7) on Rails 3.0.20 application causing time zone weirdness

I'm upgrading an big, old, clunky Rails application's Ruby (mostly so I don't have to re-install REE on my reformatted laptop) and I'm getting bitten pretty badly by timezone issues. Basically, pulling datetimes out of the database is not converting them to local time correctly:
New System - Ruby 2.1.2, Ubuntu 14.04
>> Time.zone.name
=> "Central Time (US & Canada)"
>> ActiveRecord::Base.time_zone_aware_attributes
=> true
>> ActiveRecord::Base.default_timezone
=> :utc
>> Transaction.last.created_at
=> 2014-07-15 02:09:02 UTC
>> Transaction.last.created_at_before_type_cast
=> 2014-07-15 02:09:02 UTC
>> Transaction.last.created_at.localtime
=> 2014-07-14 21:09:02 -0500
>> exit
$ date
Mon Jul 14 22:27:50 CDT 2014
Old System - REE, Ubuntu 12.04
>> Transaction.last.created_at
=> Mon, 14 Jul 2014 22:03:11 CDT -05:00
>> Transaction.last.created_at_before_type_cast
=> Tue Jul 15 03:03:11 UTC 2014
>> Transaction.last.created_at.localtime
=> Mon Jul 14 22:03:11 -0500 2014
As you can see, I've ensured that time_zone_aware_attributes is set, the zone is set (I set it in the environment.rb), and ActiveRecord is storing the times in UTC (as expected). I'm so stumped on this. Anyone have any ideas?
Update
before :all do
#current_tz = Time.zone
Time.zone = 'Pacific Time (US & Canada)'
end
after :all do
Time.zone = #current_tz
end
it 'should report time as Pacific time' do
shift = FactoryGirl.create(:shift)
new_obj = Transaction.create(shift: shift, time: DateTime.new(2013, 3, 31, 0, 0, 0, 0), amount: 0)
new_obj.reload
I18n.localize(new_obj.time, format: :timeonly).should_not == '12:00 am' #We created it with a UTC date, but it should get I18n'd to Pacific time
end
#en.yml
time:
formats:
timeonly: "%l:%M %p"
The above test is also failing. The I18n stuff seems totally broken.
Update 2
I seem to have isolated the problem to 1.9.3 -> 2.1. Using 1.9.3 is fine, I suppose, and I guess I'll just run that on the new server until I upgrade Rails. Sad. I'd still love to hear any suggestions about fixes.
Try using the I18n helpers to format/offset your time output - and force timezone (if needed)
puts I18n.localize(Transaction.last.created_at, format: :long)
Or
Time.use_zone("Central Time (US & Canada)") do
puts I18n.localize(Transaction.last.created_at, format: :long)
end
I recommend you set your default Time.zone to UTC as well and update it for each user explicitly as needed - here is a blog post that might be helpful: http://jessehouse.com/blog/2013/11/15/working-with-timezones-and-ruby-on-rails/

Comparison between two ActiveSupport::TimeWithZone objects fails

In my test suite, I have a failing test.
expected[0]['date'] comes from SomeModel.first.created_at
In a debugging console, I have the following:
> expected[0]['date']
=> Tue, 25 Mar 2014 16:01:45 UTC +00:00
> res[0]['date']
=> Tue, 25 Mar 2014 16:01:45 UTC +00:00
> res[0]['date'] == expected[0]['date']
=> false # wtf
> res[0]['date'].class
=> ActiveSupport::TimeWithZone
> expected[0]['date'].class
=> ActiveSupport::TimeWithZone
>
How is this possible ?
I've tried to reproduce this problem (I tought maybe the == operator on TimeWithZone checks the reference, or something like this, but no...) :
> t1 = Time.zone.at(0)
=> Thu, 01 Jan 1970 00:00:00 UTC +00:00
> t2 = Time.zone.parse(t1.to_s)
=> Thu, 01 Jan 1970 00:00:00 UTC +00:00
> t1 == t2
=> true
> t1.class
=> ActiveSupport::TimeWithZone
> t2.class
=> ActiveSupport::TimeWithZone
Edit: More tests...
> res[0]['date'].eql?(expected[0]['date'])
=> false
> res[0]['date'].zone
=> "UTC"
> expected[0]['date'].zone
=> "UTC"
> expected[0]['date'].getlocal
=> 2014-03-25 16:01:45 +0000
> res[0]['date'].getlocal
=> 2014-03-25 16:01:45 +0000
> res[0]['date'].hash
=> -3455877575500291788
> expected[0]['date'].hash
=> -3819233736262144515
>
> t1.hash
=> 2279159074362527997
> t2.hash
=> 2279159074362527997
# inspect...
> expected[0]['date'].inspect
=> "Tue, 25 Mar 2014 16:39:01 UTC +00:00"
> res[0]['date'].inspect
=> "Tue, 25 Mar 2014 16:39:01 UTC +00:00"
Looks like the comparison is based on the hash object. Why res and expected have different hashes ?
Answer #1 rake db:test:prepare
First, attempt dropping the test database and recreating it, and then running rake db:test:prepare. This has resolved this issue for me in the past I know this is a bit of a lame answer, but it is worth a shot.
Answer #2 Spring + Rspec + Shoulda matchers
If having this issue after installing Spring, please checkout this Github Thread, which can cause tests to fail:
https://github.com/rails/spring/issues/209
This issue only started occurring for me after adding Spring to my project.
Adding gem 'shoulda', require: false and manually adding require 'shoulda/matchers' to my spec_helper.rb resolved the issues
Answer #3 Timecop
If still having issues, checkout the Timecop gem and freeze time around date comparisons.
https://github.com/travisjeffery/timecop

how to change server time to client time on rails?

So my end goal is to have Heroku server's Time.now to be same as the time that you do new Date.now() from JavaScript. What I have done so far is catching the timezone offset from JavaScript, and set Time.zone on Rails with the appropriate client timezone, so now, Time.zone is the right time zone. But however, Time.now is still reflecting the real timezone instead of the one feeding into Time.zone. I guess that's not what I suppose to do at the first place.
So once again, all I want is, the server time to reflect the client time, so any operations such as Time.now or DateTime.now or Date.today will be shown with the client time.
You should apply the UTC offset when displaying the times.
All server-side time calculation or storage should be done in UTC.
Check this
1.8.7 :001 > Time.zone
=> #<ActiveSupport::TimeZone:0xb740d1b8 #tzinfo=#<TZInfo::TimezoneProxy: Etc/UTC>, #utc_offset=nil, #name="UTC", #current_period=nil>
1.8.7 :002 > Time.now
=> Fri Apr 20 13:13:53 +0530 2012
1.8.7 :003 > Time.zone.now
=> Fri, 20 Apr 2012 07:43:59 UTC +00:00
1.8.7 :004 > Time.zone = "Helsinki"
=> "Helsinki"
1.8.7 :005 > Time.zone
=> #<ActiveSupport::TimeZone:0xb70ab830 #tzinfo=#<TZInfo::TimezoneProxy: Europe/Helsinki>, #utc_offset=nil, #name="Helsinki", #current_period=nil>
1.8.7 :006 > Time.now
=> Fri Apr 20 13:14:48 +0530 2012
1.8.7 :007 > Time.zone.now
=> Fri, 20 Apr 2012 10:45:10 EEST +03:00
1.8.7 :008 > Time.zone.now.to_time.strftime("%c").to_datetime
=> Fri, 20 Apr 2012 10:47:01 +0000
1.8.7 :009 >
So Time.zone.now.to_time.strftime("%c").to_datetime will give you current time in user's timezone as UTC
Time.now always gives time in your server's timezone. Time.zone.now gives time in specified timezone.
Thanks,
Amit Patel

Ruby String to_time broken?

I would expect Time and to_time to be reflective.
UPDATE
This is the answer, if you add the parameter :local, it doesn't ignore the offset. I find it wierd that it ignores data (the offset) by default, though...
Time.zone.now.to_s.to_time(:local) returns 2012-03-20 14:39:03 +0100
/UPDATE
> Time.zone.now.to_s
=> "2012-03-20 12:50:05 +0100"
> Time.zone.now.to_s.to_time.to_s
=> "2012-03-20 12:50:05 UTC"
# PROBLEM:
# UTC is +0000, meaning that there is 1 hour diff in the above
> Time.zone.now
=> Tue, 20 Mar 2012 12:51:32 CET +01:00
> Time.zone.now.to_time
=> Tue, 20 Mar 2012 12:51:32 CET +01:00
> Time.zone.now.to_json
=> "\"2012-03-20T12:50:36+01:00\""
> Time.zone.now.to_json.to_time
=> 2012-03-20 12:50:36 UTC
I have this problem with JSON messages. Anyway, I wouldn't expect to_time to actually change the time. OK to translate to UTC, probably, but it should adjust time accordingly. Anybody know if this is a known issue or "by design"?
This method, String#to_time, defined in ActiveSupport takes one parameter, form, which can be either :utc (default) or :local. So by default, it always returns a Time in UTC.
To get a Time with timezone:
Time.zone.parse("2012-03-20T12:50:36+01:00")

Resources