Rails - Date time select in specified time zone - ruby-on-rails

I'm working with an app for a concert tour website, where all times (announcement times, on-sale start times, and event start times) are local to each particular venue's time zone. I take the user entered date/time where applicable and run a before_filter to set the appropriate time zone so that everything is stored in the database in UTC. For the 'new' form and for displaying the times in index and show actions, no problem at all. When the data is brought back out of the database and into a view, I use in_time_zone to adjust per the particular venue.
The only issue is on the edit form. The date/time select is showing the data in UTC. When I'm working on the site, I mentally adjust, but for others it's confusing. I'd like to do something along the lines of:
<%= f.datetime_select :start_datetime.in_time_zone(#event.time_zone) %>
Or, in the controller:
def edit
#performance = Performance.find(params[:id])
#event = #performance.event
#performance.start_datetime = #performance.start_datetime.in_time_zone(#event.time_zone)
end
Then simply, <%= f.datetime_select :start_datetime %>.
Unfortunately, I haven't found the right way to do this. Do you have any ideas worth giving a shot?
Thank you very much.

You may use default method of datetime_select, as following:
%br
= f.label :req_sess_start, "Session starts at"
= f.datetime_select(:req_sess_start, :start_year => 2010, :ampm => true, :default => 0.days.from_now.in_time_zone(#timezone))
Because of the default value shown, client will assume that times has to be entered in his/her local timezone, but...
this value will be actually in the timezone default to your application (as provided in application.rb, and the default is UTC).
So, you would require some server side coding to convert it to correct value.

I'm not sure if I understand what you want to do, but since you're storing the #event.time_zone, you could add
:default => start_time.in_time_zone(#event.time_zone)
to your datetime_select form field.

How about something like this:
# change PST to correct zone abbrev.
new_zone = ActiveSupport::TimeZone.new("PST")
#performance.start_datetime = #performance.start_datetime.in_time_zone(new_zone)

I just noticed this is an old post but:
If I were you, I would use a virtual attribute to represent the date time of the venue. For instance, you could add an attribute to performance called adjusted_start_time.

Related

Rails render a time form field in the user's time zone

I am re-writing this post for clarify and to show what I have so far and where exactly I am stuck.
So as you may know, the local_time gem only works when rendering the date and time attributes from the record itself, such as:
local_time(#events.start_time)
But I want to be able to render the datetime or time selects in the user's time zone, yet still save them to the database as UTC. I've been able to achieve this by hard coding it to Easter time by:
- Time.use_zone("Eastern Time (US & Canada)") do
%fieldset.border.border-dark.p-2
%legend Start Time
.input-group
= f.time_select :start_time, {ignore_date: true, minute_step: 15, prompt: true, ampm: true}, {class: "form-select form-select-lg border border-dark ms-2 me-2"}
.text-end.text-muted
= Time.zone
The above code will use Eastern Time (US & Canada) as the time zone the user sees, but at the same time is still saved to the database as UTC.
In order to make this more dynamic, I need to get the client's time zone, which according to this thread, you can obtain the client's IANA time zone by:
console.log(Intl.DateTimeFormat().resolvedOptions().timeZone)
However, I need a way to post this to the events/new controller so that I can use it as a Ruby instance variable to change one line above:
- Time.use_zone(#my_zone) do
In my Events Controller I currently have:
def new
#my_zone = params[:my_zone]
#my_zone = ActiveSupport::TimeZone[TZInfo::Timezone.get('America/Vancouver').period_for_utc(Time.now.utc).utc_offset]
#event = Event.new
The second time that #my_zone is called, we are using an ActiveSupport that would convert the iANA value directly into the TZInfo value that the Time.use_zone uses, as discussed here.
Then once the controller has that value, it can plug it into Time.use_zone() method.
Getting Javascript to post to the controller is where I'm struggling. I attempted to use a similar AJAX call to this thread, such that at the bottom of my new.html.haml:
:javascript
$.post('/events/new', {my_zone: Intl.DateTimeFormat().resolvedOptions().timeZone}))
However, when I try to run that, the browser console says:
Syntax Error: Bare private name can only be used on the left hand side of an 'in' expression.
I can't figure out what's going on with that.
On the other hand, I'm actually on Rails 7 now, which I do know doesn't have UJS by default but rather uses Stimulus, Hotwire and Turbo. So, is there a way I could do the post to the controller via Stimulus, Hotwire and turbo?

Rails timezone select doesn't show DST

In my user edit view I have the following select: <%= f.time_zone_select :timezone %>
So that users can save their own timezone to tailor the dates on our app.
However we've noticed that during DST which is the case at the time of this posting the select is still showing (GMT+00:00) London instead of UTC+00:01 or BST.
Is this an option in Rails to show the correct timezone? Or is what is currently being show actually correct instead?

How to correctly set time format in strftime method?

I have the following method
<%= message.created_at.strftime("%H:%M") %>
that currently returns 21:00
I need to display a Europe/Kiev time zone which is 3 hours ahead than usual Greenwich's time zone. How do I set it in the method input?
You can use a beautiful gem named "local_time". It takes care of local rendering of the time in your views. It has very nice documentation as well. Visit https://github.com/basecamp/local_time
If you stored time in your database in UTC than that time will always be in UTC when you fetch it. You need to display it according to user's local time zone. You have to do that using JavaScript, check this article.
Or if you need just Kiev time zone and nothing else you can do something like this:
<%= message.created_at.in_time_zone('Europe/Kiev').strftime("%H:%M") %>

How to localize date format in rails, form builders and error messages?

I've spent most of the day trying to localize dates in my application.
Reading this http://guides.rubyonrails.org/i18n.html it seems that I18n.l(date) is the way to do it.
This is all very well if all I want to do is render date objects directly in my view. However a lot of the time I want to render a date in a form field:
= form_for #object do |f|
.field
= f.label :date
= f.text_field :date
This seems to call to_s on the date object and use that, with no localization.
The first workaround I tried was to monkey patch the date class to use I18n.l:
class Date
def to_s
I18n.l(self)
end
end
This seemed nice as I wouldn't have to remember to call I18n.l each time I render a date. However doing it this way breaks all my database queries as locale specific formats do not make sense in a query string!
To fix this I added extra logic to the patch:
class Date
def to_s(type = nil)
if(type == :db)
self.strftime("%Y-%m-%d")
else
I18n.l(self)
end
end
end
However this STILL does not fit - because when using dates for validation errors active record seems to use the string value sent to the db.
Can anyone share with me how you would localize date formats consistently across your views, form builders and active record error messages?
Thanks for any help
Overloading Date's to_s method is a big no-no for me.
I can't think of a magical solution that formats dates differently depending on the context and manages to do that reliably ;)
I'd suggest continuing to specify the date formats only where you need them and possibly overwriting the date.formats.default translation in your locale file to make the format more palatable to your users (this should also fix the formatting in your text_field).

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