Formatting Date according to user profile settings - ruby-on-rails

Each user of our application can have different format for Date and Time. I understand the date format is dependent on user's language and possibly time zone.
I guess I can try to run something like Date::DATE_FORMATS[:default] = "%m/%d/%Y" in ApplicationController. In this case Date.today.to_s would return the property formatted date. Will it be thread safe?
Another option I am looking at is to store profiles into config/locales/en.yml with different keys like en_US, en_GB, en_... and store locale name into the user's profile. In this case I will need to use I18n.localize to format the date. Is it possible to fall back to en if there is no key found in specific en_US?

In the case of Date::DATE_FORMATS or TIME::DATE_FORMATS, you will need to take extra care to make their usage threadsafe, unfortunately. Shouldn't be too difficult to do, however.
AS for your second question, I believe you are concerned with locale fallbacks, and the i18n gem has support for locale fallbacks. This feature is easily enabled with:
I18n::Backend::Simple.include(I18n::Backend::Fallbacks)

Related

Rails: How to manage a "static" time-value attribute?

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

How to present Rails form datetime select in different time zone?

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.

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 to return my current time zone in RoR?

When I use return the time that the record created, it show this :
2010-01-20 15:04:40 UTC
but I want the time in my specify time zone, for example, China. Is there any convenient method in RoR?
Configure your time zone in config/environment.rb to have Rails cast all timestamps to this time zone.
config.time_zone = 'Berlin'
As an alternative you can always use something like
Time.utc(2000).in_time_zone('Alaska')
See the documentation here.
Take a look at the TimeZone and TimeWithZone classes. They add time zone support. There's also been some additions to the Time and DateTime classes that also help deal with time zones. The documentation is given here: http://api.rubyonrails.org/classes/ActiveSupport/TimeWithZone.html.
There's also an excellent post here giving some extra details: http://ryandaigle.com/articles/2008/1/25/what-s-new-in-edge-rails-easier-timezones

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