Question

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!

Was it helpful?

Solution 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.zonefor 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

OTHER TIPS

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
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top