Convert Time.now into ActiveSupport::TimeZone - ruby-on-rails

My website has a functionality of using User system time unless they are logged in.
The problem comes when trying to test this functionality, as Time.now.zone gives me a string representation of the zone instead of an ActiveSupport::TimeZone object.
The one I get from Time.now.zone also can't be used to look up the timezone:
Time.now.zone
=> 'BST'
while I need:
=> #<ActiveSupport::TimeZone:0x00007ffe1afd9470 #name="Europe/London", #utc_offset=nil, #tzinfo=#<TZInfo::DataTimezone: Europe/London>>

Use Time.zone instead of Time.now.zone

Thanks to #Stefan, my misunderstanding of Time.now has been cleared, it reporting server machine time, not user's, which of course makes all the sense. Thankfully, when it comes to tests, I can assume the client and server are the on the same machine, so its not a big problem.
I managed to also find a solution to my original problem. The method I was looking for was getlocal, which converts the DateTime object into system time(as mentioned above, that would be the server time, which is ok in tests). That made tests pass on both CircleCI and locally.

Related

TimeWithZone & Time.zone.now integration test fails

In a controller method I set a user's variable activation_sent_at equal to Time.zone.now when an activation email is sent to that user. On the development server this seems to work (although time expressions in my application are 2 hours behind on the local time of my computer).
I want to include an integration test that tests whether activation_sent_at indeed gets set properly. So I included the line:
assert_equal #user.activation_sent_at, Time.zone.now
However, this produces the error:
No visible difference in the ActiveSupport::TimeWithZone#inspect output.
You should look at the implementation of #== on ActiveSupport::TimeWithZone or its members.
I think it's suggesting to use another expression for Time.zone.now in my test. I've looked at different sources, including http://api.rubyonrails.org/classes/ActiveSupport/TimeWithZone.html, but am not sure what to do here. Any suggestions what could be causing this error?
Additional info: Adding puts Time.zone.now and puts #stakeholder.activation_sent_at confirms the two are equal. Not sure what generates the failure/error.
The issue is that the 2 dates are very close to each other but not the same. You can use assert_in_delta
assert_in_delta #user.activation_sent_at, Time.zone.now, 1.second
For RSpec, a similar approach would be to use be_within:
expect(#user.activation_sent_at).to be_within(1.second).of Time.zone.now
The problem is that your times are very close but not quite equal. They are probably off by a few fractions of a second.
One solution to issues like this is a testing gem called timecop. It gives you the ability to mock Time.now so that it will temporarily return a specific value that you can use for comparisons.
The reason is because Time.now or Time.zone.now include milliseconds (when you do a simple put to print the time it doesn't show milliseconds). However, when you persist the timestamp in the database these milliseconds likely get lost unless the db field is configured to store milliseconds. So when you read the value from the db it will not include milliseconds, hence the times are slightly different.
One solution is to remove milliseconds from Time.now. You can do this like so Time.now.change(usec: 0). This should fix the error in the tests.

Trouble with the speed of using in_time_zone

I am using Ruby 1.8.7 and Rails 3.0.3. Even though currently all of my users are in the same time zone as my server, I thought that in preparation for world domination I'd get rid of all of my RubyTimeObjIGotOutOfMyDb.getlocal calls and replace them with RubyTimeObjIGotOutOfMyDb.in_time_zone(user_timezone) where the user's timezone is a column in my user's table. What happened is now my page takes maybe 5 or 6 times as long to load. Is this the wrong strategy? Is there a better way I should be preparing for users in different timezones from my server?
watching this railscast
I think you just need to do
controllers/application.rb
before_filter :set_user_time_zone
private
def set_user_time_zone
Time.zone = current_user.time_zone if logged_in?
end
And all the times will be converted when fetching from db
If times are not in the current timezone, try this in the views (or in console for testing)
Time.now.in_time_zone
UPDATE:
If you are doing that call in_time_zone(zone) many times, I think it's fetching the corresponding time difference many times, the way the screencast tells to do, it will fetch only one time and use it in every conversion.

Working with and testing dates in rails

My application uses dates a lot. A lot of ajax calls and urls involve datetimes and I find the typical format '1920-10-10 18:30:00' to be unfriendly for these purposes. I work around this by creating a helper method that basically strips the unnecessary characters out of the date (192010101830) and another method for converting the string back in to a date object.
When I build a url it goes something like this:
=link_to "Send Date", thing_my_date_path(date_to_string(DateTime.now))
Then when the thing_date action receives, it converts the parameter back in to a datetime object
def my_date
#date = string_to_date(params[:mydate])
....
end
This works fine in development. However I am completely open to other suggestions.
The problem is when I go to test my application. Tests fail because the helper methods for date_to_string and string_to_date are not present. I could include them in the tests but I feel like they should be kept separate.
So I'm looking for
a) a better way to pass dates around, and more importantly
b) a method of testing an action that is dependent on helper methods.
There are built-in methods for that methinks.
> DateTime.now.to_s(:number)
=> "20110429162748"
> DateTime.parse("20110429162748")
=> Fri, 29 Apr 2011 16:27:48 +0000
Hope that helps..

why is the datetime in database different when I output it?

In my database on heroku, it shows contactemail.created_at = "2010-08-08 17:16:19"
However, when I use puts.contactemail.created_at I get something different. I get:
2010-08-08 10:11:13 -0700
I need to input that value through an API to another application, and I am pretty sure that the first format is what it wants. If it doesn't take that, it wants 08/08/10 17:16:19 -- in either case, I don't know how to format it properly.
This is in Ruby on Rails.
The display of date is based on your servers locale settings. If you are looking for the first format you could try
puts.contactemail.created_at.to_s(:db)
Have a look at strftime doc here http://ruby-doc.org/core/classes/Time.html#M000298 to get the second format
HTH
Ruby on rails can manage different time zones.
You can:
Make a direct sql consult (rails have some helpers)
Make that the two dates be equals, configuring config.active_record.default_timezone

How do I work with Time in Rails?

I've been pulling my hair out trying to work with Time in Rails. Basically I need to set all time output (core as well as ActiveSupport) to the server's local time -- no GMT, no UTC, etc. I've seen various posts relating to Time, but they usually involve someone's need to set it for each user. Mine isn't nearly as complex, I simply want consistency when I use any Time object. (I'd also appreciate not receiving errors every 3 seconds telling me that I can't convert a Fixnum (or some other type) to string -- it's Ruby, just do it!)
I also seem to be getting drastically different times for Time.new vs the ActiveSupport 1.second.ago. Anyway, does anyone have any quality suggestions as regards working with Time in Rails?
If you just want Time objects to be consistent, then why not stick with UTC? I just tried Time.new and 1.second.ago using script/console and I get the same output (give or take a second for typing the command). How are you doing it?
Somewhere in your initializers, define the format(s) that you want to use.
ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.merge!(:default => '%m/%d/%Y %H:%M')
ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.merge!(:my_special_format => '%H:%M %p')
Then when you want to print a Time object, it works like the following example. Notice that the Time object in my console is already aware of my time zone. I'm not performing any magical transformations here.
>> t = Time.now
=> Wed Jul 15 18:47:33 -0500 2009
>> t.to_s
=> "07/15/2009 18:47"
>> t.to_s(:my_special_format)
=> "18:47 PM"
Calling Time#to_s uses the :default format, or you can pass in the name of the format you'd rather use like I did with :my_special_format.
You can see the various options for formatting a Time object here.
If u don't want to store each user time setting, the only solution is to use javascript time system because it work on user client time. For example i have an application that each time user try it, the app will create some example data with each data have a initial date value "today". At first time, it confuse me a lot because my host server is in australia and lot of user is on western part, so sometime the initial date value is not "today", it said "yesterday" because of different time region.
After a couple day of headache i finally take decision to JUST use javascript time system and include it in the link, so when user click the "try now" link it will also include today date value.
<% javascript_tag do -%>
var today = new Date();
$("trynow").href = "<%= new_invitation_path %>?today=" + today.toLocaleString();
<% end -%>
Add the following to config/environment.rb to handle time correctly and consistently all the time within the context of Rails. It's important to know that it will store your times to the database in UTC -- but this is what you want -- all the conversion is done automatically.
config.time_zone = 'Pacific Time (US & Canada)'
You can run rake time:zones:local from your Rails root directory to get a list of valid time zone strings in your area.
A quick addition to the DATE_FORMAT solution posted above. Your format can be a string, in which case it works as noted above by calling strftime, but you can also define the format as a lambda:
CoreExtensions::Time::Conversions::DATE_FORMATS.merge! :my_complex_format => lambda {|time|
# your code goes here
}

Resources