I would like to present a datetime select to the user in their preferred time zone but store the datetime as UTC. Currently, the default behavior is to display and store the datetime field using UTC. How can I change the behavior of this field without affecting the entire application (i.e. not changing the application default time zone)?
Update: This is not a per-user timezone. I don't need to adjust how times are displayed. Only these specific fields deal with a different time zone, so I would like the user to be able to specify the time in this time zone.
Here's how you can allow the user to set a date using a specific time zone:
To convert the multi-parameter attributes that are submitted in the form to a specific time zone, add a method in your controller to manually convert the params into a datetime object. I chose to add this to the controller because I did not want to affect the model behavior. You should still be able to set a date on the model and assume your date was set correctly.
def create
convert_datetimes_to_pdt("start_date")
convert_datetimes_to_pdt("end_date")
#model = MyModel.new(params[:my_model])
# ...
end
def update
convert_datetimes_to_pdt("start_date")
convert_datetimes_to_pdt("end_date")
# ...
end
def convert_datetimes_to_pdt(field)
datetime = (1..5).collect {|num| params['my_model'].delete "#{field}(#{num}i)" }
if datetime[0] and datetime[1] and datetime[2] # only if a date has been set
params['my_model'][field] = Time.find_zone!("Pacific Time (US & Canada)").local(*datetime.map(&:to_i))
end
end
Now the datetime will be adjusted to the correct time zone. However, when the user goes to edit the time, the form fields will still display the time in UTC. To fix this, we can wrap the fields in a call to Time.use_zone:
Time.use_zone("Pacific Time (US & Canada)") do
f.datetime_select :start_date
end
There are a couple of options:
Utilize the user's local timezone when displaying data to them. This is really easy with something like the browser-timezone-rails gem. See https://github.com/kbaum/browser-timezone-rails. It is essentially overriding the application timezone for each request based on the timezone detected from the browser. NOTE: it only uses the OS timezone, so it's not as accurate as an IP/geo based solution.
Setup your application timezone so that it is consistent with the majority of your user base. For example: config.time_zone = 'Mountain Time (US & Canada)'. This is a very standard thing to do in rails. Rails will always store the data in the DB as UTC, but will present / load it using the application timezone.
Create a timezone for your user model. Allow users to set this value in their account settings. And, then use a similar approach to that of the above gem does in the application_controller.
Related
What is the best way with Rails to have a “time” attribute (selected by the user) which is supposed to always be displayed as the same “static” time value?
(Meaning: It should always show the same time, for example “14:00”, completely independently of any user’s time zone and/or DST value.)
Until now, I have tried the following setup:
In the MySQL database, I use a field of the type time (i.e. with the format: 14:00:00)
In the Rails view, I use the helper time_select (because it’s really handy)
However, it seams that with this approach, Rails’ ActiveRecord will treat this time value as a full-blown Ruby Time object, and therefor convert the value (14:00:00) to the default time zone (usually set to ‘UTC’) for storage and then convert it back to the user’s time zone, for the view. And if I’m not mistaken, this also means that the fluctuating DST value will make the displayed time value fluctuate throughout the year (and the same happens if the user moves to another time zone).
So what is the best way to manage a simple “static” time attribute with Rails?
If you don't want any time related functionality, why not save it as an string field. Since from your question description its evident that functionalities such as timezone doesn't effect your use case, so just make it a normal VARCHAR(8) and save the value as a string and parse it such as Time.now.strftime("%H:%M:%S") before saving it to the database, you can also write this logic inside your ActiveRecrd model class
def static_time=(value)
super(value.strftime("%H:%M:%S"))
end
you can somewhere in the code say model_object.static_time=Time.now and this will automatically parse it, if you want to get the time as a ruby object retaining the format you can simply do it defining a custom getter.
def static_time
current_time = Time.now
time_keys = [:hour, :min, :sec]
current_time.change(**Hash[time_keys.zip(super.split(":"))])
end
I have a User model, which allow user to provide their own time zone. User can choose their own time zone using the time_zone_select. So we will store something like Pacific Time (US & Canada) in database.
My Rails 3 application default setting is using Pacific Time (US & Canada). So all the time display is in this time zone.
May I change the time time display based on user time zone? For example, User A will see all the time displayed in his time zone Central Time (US & Canada), and User B will see all the time in London.
Thanks all.
In 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
From railscast
First you'll need to add a column to your users table, to keep the selected timezone for this user (user.timezone should be fine).
in the form, you'll need to use ActiveSupport's #time_zone_select to allow the user to select their desired timezone.
finally, you'll need a before filter in ApplicationController to set the current sessions's timezone to the user's specific timezone.
Here is a gist with the migration, partial view and application controller filter:
https://gist.github.com/eladmeidar/6121183
I have a rails view where the user is presented with a dropdown displaying the hours '00:00', '01:00' etc.
Once the user has selected this hour, then it is stored in the model with some other fields.
My application is working in timezone UTC+2 (I have set config.time_zone = 'Jerusalem').
At what stage to I related to timezones when handling this time on its path from the view to the model? If I use e.g. DateTime.strptime("10:00", "%H:%M").in_time_zone(Time.zone), then I get a time at '12:00' - while what I really would like is for the database to store in UTF (which should be 08:00 while the rest of the site works with local time.
You can use TZInfo ruby library http://tzinfo.rubyforge.org and convert your time with following method
def local_to_utc(date, time_zone)
begin
unless time_zone.nil?
tz = TZInfo::Timezone.get(time_zone)
return tz.local_to_utc(date)
end
rescue
return nil
end
end
I have Rails3 application with model user and field expires_at created like this:
t.column :expires_at, :timestamp
In my database (postgresql) it has type:
timestamp without timezone
The problem is when I call:
#user.expires_at = Time.now
#user.save
it is saved into database with UTC timezone (my local time is UTC + 1:00, Warsaw) but I don't want that. I just want to have time with my local timezone saved into the database (2011-03-30 01:29:01.766709, not 2011-03-29 23:29:01.766709)
Can I achieve this using rails3?
For saving time in local timezone to database this has to be set in application.rb
config.active_record.default_timezone = :local
If you only want to use local times on certain columns, rather than as a global setting, then the Rails documentation tells us this:
# If your attributes are time zone aware and you desire to skip time zone conversion to the current Time#zone when reading certain attributes then you can do following:
class Topic < ActiveRecord::Base
self.skip_time_zone_conversion_for_attributes = [:written_on]
end
(This also skips time zone conversion on writing, not just reading). And you can pass in an array of symbols for multiple attributes.
I am not sure which versions of Rails this was introduced in, though.
I have the need to capture a time and time zone from users of a rails 2.3.8 app, but have been unable to think of a clean solution to create and parse the selections.
Ideally I would have a drop-down menus for the following:
hour (1-12)
minute (0-59)
AM/PM
Time Zone
Is there a gem/plugin that accomplishes what I am looking for? Will I need to store both the time and time zone in the database? What is the best strategy for storage?
I'll eventually need to spit these values out in UTC, but a user should be able to go back and review the time in the correct time zone.
You can get time zone selects with the appropriate methods:
time_zone_options_for_select
time_zone_select
Similarly, there's date_select for dates.
Storage:
If the timezone is specific to the user and doesn't change, then store their time zone in their user record and set it when you load the current_user. Rails will convert times to/from UTC and always store UTC in the database and do the automatic convert to that default timezone for you (including daylight savings!). Easiest way to do it.
use_zone(zone) lets you override the default zone for a block, so you can accept a form value and set it with that function and set your value in that block.
UPDATE: I wrote up some Rails timezone examples as a blog entry.
I personally used jQuery to change the display only:
ampm = ["12 AM","01 AM","02 AM","03 AM","04 AM","05 AM","06 AM","07 AM","08 AM","09 AM","10 AM","11 AM","12 PM","01 PM","12 PM","01 PM","02 PM","01 PM","02 PM","03 PM","04 PM","05 PM","06 PM","07 PM","08 PM","09 PM","10 PM","11 PM"]
j("#game_start_time_4i option").each(function(index,value){
j(value).html(ampm[index]);
});
Rails:
<%= datetime_select('game', 'start_time') %>