Setting system time in rspec tests without changing system clock - ruby-on-rails

I'm working on getting a rails 3 application ready to use time zones. My development machine is in EDT and the servers I'm hosting on are in UTC. Is there a way in my rspec tests to change the system time zone ruby is using so that I'm running tests using the same system time zone without having to change the system clock on my computer? I've looked into Delorean and Timecop and they're not what I'm looking for. I'm looking for something like
Time.system_time_zone = "UTC"
...and then Time.now would return the UTC time instead of whatever my system time zone is set to.

before {Time.stub(:now) {Time.now.utc}}
With this, anywhere Time.now is called in your tests, it will return your system's time in UTC.
Example:
describe Time do
before {Time.stub(:now) {Time.now.utc}}
it "returns utc as my system's time" do
Time.now.utc?.should be_true
end
end

Rails gives you precisely what you are looking for:
Time.zone = "UTC"
Look at http://api.rubyonrails.org/classes/Time.html#method-c-zone-3D for more information.

Related

How do you run a rake task at midnight of every single time zone?

I want to perform some actions on a user when it's midnight in his or her region. Since I want to support time zones instead of basing everything on server time, I want to cover all the possible midnights of every time zone.
The idea I had so far is to create a rake task that is triggered every hour with cron, and the rake task tries to figure out what time zone just ticked over to midnight. This is a pretty unreliable approach, especially when a time zone enters DST.
Are there any other more reliable ways to accomplish this?
You can use the tzinfo gem to navigate through different timezones and it supports DST. The Whenever gem makes it easy to write your cron jobs in Ruby. I assume your User model has a timezone column which follows The Time Zone Database naming.
So, your schedule.rb would be something like this:
require "tzinfo"
def midnight_in_timezone(timezone)
# assumes your server runs in utc
TZInfo::Timezone.get(timezone.name).local_to_utc(Time.parse('00:00am')).strftime('%H:%M%p')
end
set :output, "/var/log/cron_log.log"
TZInfo::Timezone.all.each do |timezone|
every :day, :at => midnight_in_timezone(timezone) do
# pass in timezone so you can perform your task
# for users who match => User.where(timezone: timezone)
runner "SomeModel.run_your_midnight_task('#{timezone}')"
end
end

How does Rails know my timezone?

How does Rails know my time zone? I don't have it set in application.rb:
# config.time_zone = 'Central Time (US & Canada)'
I searched the whole app for time_zone and that is the only instance of it. I don't see any environment variables for time zone set either. I'm on Windows.
C:\Users\Chloe\workspace\MyBestBody>rails runner "p Time.now"
DL is deprecated, please use Fiddle
2015-06-12 23:38:33 -0400
It prints UTC time when deployed to Heroku.
C:\Users\Chloe\workspace\MyBestBody>heroku run rails console
Running `rails console` attached to terminal... up, run.1949
Loading production environment (Rails 4.2.1)
irb(main):001:0> Time.new
=> 2015-06-13 03:28:34 +0000
Rails 4.2.1
From gettimeofday(), or so the ruby MRI source would have me believe:
int __cdecl
gettimeofday(struct timeval *tv, struct timezone *tz)
{
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
filetime_to_timeval(&ft, tv);
return 0;
}
Per comment, this is a C function, called by the ruby date routines under the hood. You do not call the method yourself, rather you call the ruby methods you are calling.
Your question involves two separate things.
The first is what ruby thinks the time zone is. This is the zone used when you use things likeTime.now or Time.mktime. Ruby uses the C level APIs provided by the operating system to get this information (the TZ environment variable can override this on unixy operating systems).
The second is the time zone your users will see when they use your app. This is frequently not the same thing. Even if your users were all in the same timezone it's a good idea to have your servers use UTC because it is free from things like daylight savings time.
For this reason rails has its own timezone system and the setting you found controls the default value of that timezone. Sometimes you might overwrite this on a per user basis. Rails always records information in UTC, but it will be displayed in views using the value of Time.zone (And input from forms is interpreted in that zone). You can see what the current time in this zone is by doing
Time.zone.now

Define "current time" when starting Rails?

I have an app that changes behavior based on the time of the day, and I'd like to be able to just spin up the server and test how it works at different times.
Ideally, I'd like to be able to just pass in the time as an argument when I run rails s, like rails s TIME=2014-02-26 22:06:11 -0500.
In config/locales/application.rb you can set the timezone before running rails s.
config.time_zone = 'Alaska'
If you're running OS X, you can also set the system time in the terminal before running rails s.
date 022611002013
The format is [month][day][hh][mm][year], so the above is 11:00 February 26, 2013. You can change your system time back to the current time in the terminal or Date and Time preferences panel.
You can actually read environment variables from within rails (actually Ruby). This is how to do it:
require 'date'
$startup_time = DateTime.parse(ENV['STARTUP_TIME']) rescue DateTime.now
puts "Server started at: #{$startup_time}"
You'd call this code placed in pull_time.rb like this:
STARTUP_TIME="2014-02-26 22:06:11 -0500" ruby pull_time.rb
In order to have this global variable work in Rails, you want to add it to an initializer.
So put the code above in config/initializers/set_startup_time.rb
You can now access $startup_time anywhere in your code. Just pass STARTUP_TIME as an environment variable to rails.
STARTUP_TIME=2014-02-26 22:06:11 -0500 rails s
Rails server uses time of the hosting machine. So if you are running on local machine then changing the time of the local operating system will also have effect on your rails server
If it's hosted on another server then you have to change that server's time.
Don't try changing the time of your server. That can play real havoc with cron jobs or other time-based services.
Instead, have you tried defining the starting time as a specific value in your code then running the server? Then set it to the next time you need, and run the server again?
Time.new(2013, 12, 31, 12, 59, 59) # => 2013-12-31 12:59:59 -0700
You could also use the time as an integer, using the number of seconds that represent the time you want the server to think it is. Pass that in as an ENV hash element, then check to see if it's defined. If it is, define the time based on that value, otherwise don't define the ENV value and the server will run as normal:
ENV['SERVER_TIME'] = '1388519999'
server_time = ENV['SERVER_TIME'] ? Time.at(ENV['SERVER_TIME'].to_i) : Time.now
server_time # => 2013-12-31 12:59:59 -0700
Then, when you need to test the server, you could write a little shell script that sets that value and runs rails s, test the server then quit it, and rerun the shell script with a different value.
You can change Rails' internal timezone by adding config.time_zone = 'Pacific' to application.rb.
If you're deploying to Heroku, you can do the same slightly differently: heroku config:add TZ="Europe/Athens".
However, the downside is that neither of these commands allows you to define a specific time for the app (just the timezone, so you get the current time in another timezone), nor can you run these from command-line when starting your local Rails server.
Rails server determine time from the hosting machine
So changing the system's time would do the trick
EDIT:
As 'sevenseacat' said in comment
you can also use gem timecop to test your application without changing system's time.

Ruby on Rails, delayed_job serializing incorrect datetime as GMT

I have some code that seems to execute improperly when it is serialized and run with delayed_job. To get the necessaries out of the way, I am running Ubuntu 11.04, Ruby 1.8.7 with Rails 3.0.4.
I have a datetime field in one of my tables that obviously stores the date and time of a particular event. In my RoR application, I use this field through the application calling strftime on it to format it in different ways. The output of this formatting is correct on the web page.
I am also using delayed_job to put this same field into an email that is sent out (triggered via some action). When the email arrives, it appears that it has somehow been formatted as GMT. For example, if the date time was supposed to be 11-12-2011 15:30:00 (3:30 PM) in the database, the email would read 11-12-2011 22:30:00 (10:30 PM).
I looked in the database and found somethings that are interesting:
The datetime in the database is 2011-11-12 22:30:00
The app, when displayed via the web, formats the data properly as 11-12-2011, 3:30 PM
The email, formats the data properly as 11-12-2011, 10:30 PM
When I run a small ruby file that simply prints the date and time
p Time.now
I get this the correct output (local, non GMT time)
Mon Aug 15 10:15:27 -0600 2011
When I look at what is in the serialized yaml for the delayed_jobs table, I can see that the date field is formatted as
2011-11-12 22:30:00 Z
In my application.rb, I have
config.time_zone = 'Mountain Time (US & Canada)'
and in my environment.rb, I have
timezone = "Mountain Time (US & Canada)"
Can any one help me figure out what is going on here? I am relatively new to RoR, so this may be a super obvious, easy question. If you need any more debug information, please let me know and I can post it up. Similarly, if my question is unclear, I can try to clar
In my case, I solved explicitly specifying the method in_time_zone in the view
example: #my_obj.created_at.in_time_zone.
I know it is not elegant, but so far I have not found better solutions.
Try changing the date object to a string before serializing.
You may want to set the timezone in the database. If it's MySQL, see: http://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html

Getting local time

I've got this time stored as a string:
2010-07-25 04:16:25
This is the GMT time for some action I took.
Since I live in the Jerusalem time zone, I would like to show it at Jerusalem time, i.e. 07:16 in the morning, not the GMT time of 04:16:25 which is 3 hours before.
How do I properly convert it programmatically with Ruby on Rails? I seem to get lost with the multitude of timezone functions and considerations I need to take when serving users from different locations.
I tried:
Time.parse("2010-07-25 04:16:25")
and it gave me:
"Sun Jul 25 04:16:25 +0300 2010".
I suppose the "+0300" is the difference to where I'm at?
Some light on this, or even a link to a good article that doesn't assume you know much, would help.
You can define your timezone in environment.rb file (if you are using Rails 2.3.*) or application.rb (if you're using Rails 3).
Just look for section about time zones and everything is explained in comment. It will say something like this (this is from Rails 3):
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
# config.time_zone = 'Central Time (US & Canada)'
Just uncomment that last line and you should be fine.
To configure it easily related to your system local zone you can add this in your application.rb
config.time_zone = Time.now.zone
and you can use something like this to get the localtime
Post.created_at.localtime

Resources