问题
I've built a rails app that helps parents keep track of their infants’ sleep. For it to work properly I've had to support different time zones. To avoid annoying the user with time zones, I've created a little javascript that adds a hidden field to the login form, including the timezone offset. Here's the code
var timeZoneField = $("input[name='user_tz_offset']");
if (timeZoneField.length) {
var browserDate = new Date();
var timeZoneOffsetSeconds = (browserDate.getTimezoneOffset() * 60) * -1;
$(timeZoneField).val(timeZoneOffsetSeconds);
}
With the data from that field I set the Time.zone to whatever city corresponds with that offset. Something like this generates the timezone
user_time_zone = ActiveSupport::TimeZone.[](params[:user_tz_offset].to_i)
session[:user_time_zone] = user_time_zone
Finally, I set the time zone in the ApplicationController.
def set_user_time_zone
if (session[:user_id])
Time.zone = session[:user_time_zone]
else
Time.zone = config.time_zone
end
end
All this relies on the login functionality, which I wrote myself. However, I knew that I would need to use a better user management system later, as my own code is neither well done or particularly secure (I was focusing on other functionality first).
Now, I've installed devise and it works well to log in and log out, most other functions of the site work as well. But I don't know how to approach the time zone support with devise as my user management system.
One idea is to override the SessionsController in Devise, add a check for that hidden time zone field and add its value to the user_session. But I feel apprehensive about doing so, it feels like a bad idea.
Is there a better way to add this functionality, without forcing the user to add time zone information during registration?
Thank you!
回答1:
I manage a scheduling software and we also check the browser's timezone but then just save it in the database and call it with a before_filter in the application controller.
#Application Controller
before_filter :set_time_zone
private
def set_time_zone
if current_user
Time.zone = current_user.time_zone if current_user.time_zone
end
end
回答2:
After about eight hours of trial and error I have come up with a solution that works for now. Perhaps this may be of interest to someone with a similar setup.
I started by adding a column to the users table and corresponding attribute in the model – session_tz_offset
.
Then I started hacking around with Warden callbacks. What worked for me was to put a helper method in the ApplicationController, and call that with a before filter like this:
before_filter :authenticate_user!, :set_session_tz_offset_for_user
helper_method :set_user_time_zone, :set_session_tz_offset_for_user
def set_session_tz_offset_for_user
Warden::Manager.after_authentication do |user, auth, opts|
if (params[:user])
user.session_tz_offset = params[:user][:session_tz_offset]
user.save
end
end
end
The after_authentication callback fires several times during login, why is unknown to me. Not all of these calls have a params[:user] field, and if I didn't check for it, my application crashed with a undefined method [] for nil:NilClass
error.
When the session_tz_offset
is set, my other controllers use another helper method, also defined in ApplicationController to set Time.zone
for the current request:
def set_user_time_zone
if (user_signed_in?)
if(user_session[:time_zone])
Time.zone = user_session[:time_zone]
else
user_session[:time_zone] =
ActiveSupport::TimeZone.[](current_user.session_tz_offset)
Time.zone = user_session[:time_zone]
end
else
Time.zone = config.time_zone
end
end
来源:https://stackoverflow.com/questions/13360875/set-user-time-zone-dynamically-with-devise