Rails Form display datetime at local timezone - ruby-on-rails

I'm new to rails (just finished the Rails Tutorial) and I'm building my first solo app. The user can create 'events' at specific times. Currently the user form is in UTC. How do I set it to their local time?
I don't just want to change it after they submit, because it would be confusing for the user. When you open a new form the default time is the current time (currently UTC). I want that to be the time of their local timezone.
<%= f.label :Date_Time %><br />
<%= f.datetime_select :date %>
thanks,

Loads of ways to do this!
I'd recommend you look at the question Stepan recommended first
Server Side
If you're wanting to keep your times consistent, you may wish to use the Rails in-built timezone option:
#config/application.rb
config.time_zone = 'Eastern Time (US & Canada)'
You can see more about ActiveSupport::Timezone here
Client-Side
Client-side, you'd have to think about what you want to achieve. If you have set timezones, you may benefit from creating a TimeZone repository (either ENV variables or datatable), and allow user to select from them, like this:
#app/views/form.html.erb
<%= form_tag %>
<%= select_tag :date, options_from_collection_for_select(#timezones, "id", "name") %>
<% end %>
Alternatively, you could pass the timezone through javascript: Getting the client's timezone in JavaScript
UX
The bottom line is you need to think about what you're trying to achieve. If your users need localized times, you should use a country-select system, where they will give you their country, and you can dedicate a timezone
Speaking from experience, it's much better to store all times equally (using UTC), and then process them using user's specifications

Related

Rails Best In Place date initial date format

I'm really new to Rails and I'm using v6.1.4. In my application I'm using I18n localization. At other places for view-only label dates(not using BIP) I use
<%=l #client.active_date.to_date %>
and it works. However, in Best-In-Place there is a different syntax.
<span class="date-container"><%= best_in_place task, :due_date, :as => :date, class: 'hyperlink' %></span>
This causes the dates to be displayed in the yyyy-mm-dd format throughout. I want the dates to be formatted according to the locales currently in my application. I'm using the JQuery datepicker, but it is activated after I click on the date, and the format is changed to what I have set on the jquery setDefaults option.
How do I tackle this?
I think this post addresses your question.
You could add a display_as: :formatted_date to your method call and define that formatted_date method in your model. From there you should be able to format dates as needed.

Rails - Strange time_select buggy behavior

My rails repo example is here:
https://github.com/johndel/strange_timeselect
The replication of the strange behavior can be shown here:
https://captain24.herokuapp.com/availabilities/new
I have the following problem with time_select:
In a new rails app with postgresql, I have a model (availabilities) with a started_at column which is of type time in postgres. When I create a new record, it saves the started_at value, one hour before than the selected. On the update it works correctly.
I noticed the following:
I can replicate it only on heroku (locally I cannot replicate it). It works only on create and only after I set default_timezone on application.rb as you can see on this commit.
The application has also another model named tasks with a column named started_at and type datetime. This one works always correct but it saves the time on different timezone than the time field of availabilities.
I can fix it if I add ignore_date: true on the time_select field, but I am wondering why is this happening? Is it normal and I am missing something? Or is it some very strange ruby or rails bug? Or is it an issue / misconfiguration with postgresql on heroku?
Update:
Because #max asked me to explain the code further, here it goes:
Regarding the code, right now it is just two scaffold resources with the commands rails g scaffold tasks started_at:datetime and rails g scaffold availabilities started_at:time. So one is tasks and the other is availabilities with only one column each model. I have also added in the config/application.rb these lines of code:
config.time_zone = 'Athens'
config.active_record.default_timezone = :local
The code of availabilities form is this (plain scaffold):
<%= form_with(model: task, local: true) do |form| %>
<% if task.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(task.errors.count, "error") %> prohibited this task from being saved:</h2>
<ul>
<% task.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= form.label :started_at %>
<%= form.time_select :started_at %>
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
The code of migration for availability is this:
class CreateAvailabilities < ActiveRecord::Migration[6.0]
def change
create_table :availabilities do |t|
t.time :started_at
t.timestamps
end
end
end
If you add the above code and deploy on heroku, you will be able to reproduce the problem.
So regarding the problem with the above code is that when I am creating an availability record, it keeps one hour less than what it is selected. This happens only on create. It isn't the case with task record so it probably has to do with time postgres type, with the active_record timezone. this happens only on heroku as you can check on the link above.
This is occurring because you need to take your server's timezone into account.
You need to configure Heroku as well.The command to change Heroku Time settings is something like below
heroku config:add TZ="Europe/Paris"
And it not a good idea to save date in any other format than UTC
Details
EDIT
To answer the question why the update method is working differently, you have to inspect the form rendered by rails.Rails creates additional three hidden fields
availability[started_at(1i)],
availability[started_at(2i)] and
availability[started_at(3i)]
On the create form, those fields have default values of 2020,5,19
But on the edit form, they have the value of 2000,1,1
My educated guess is they are messing up with the daylight saving time, and thus creating the anomaly.
There are two different problems.
1) Different scenario in local vs Heroku.
- Your schema.rb file has "started_at" listed as datetime, while in the migration, it is "time"
- Heroku runs the migration, but bin/rails db:setup loads from schema.rb, hence you don't see the problem locally as it's saved as datetime.
2)
The problem with the time column is that with a lack of date, Ruby has to guess. When you are creating a new record, the form is prefilled with the current date, which at the time of the post, is in May (a summer month), and will be parsed in EEST.
Incoming attributes "2020", "05", "20", "11", "00" will be parsed as Wed, 20 May 2020 11:00:00 EEST +03:00
This is then saved in the database as time offset by -3 hours 8:00:00
When it is next loaded from the database, there's no date attached, so Rails parses it with the date assumption of "2000", "01", "01" , which results in Sat, 01 Jan 2000 10:00:00 EET +02:00. Note that only two hours is added in this case, since it's EET.
When you edit, the form is prefilled with the availability's current started_at Sat, 01 Jan 2000 10:00:00 EET +02:00. When you update, since date from the form is now January, it is parsed using EET time zone and saves now. Since the date used to display and edit is the same, it now looks 'correct'
How to fix? It depends on the application, and I'm no expert, but some thoughts:-
1) Some would say that time inherently has no meaning without date. Perhaps you could consider using datetime column instead. 12PM availability in Greece may mean differently to people in different time zones.
2) You could use something like the tod gem to handle time.
3) You could store it as an integer offset from say, 12AM.

Rails Hours:Minutes Dropdowns

I'm new to Rails and I want to create a simple time calculator:
User is presented with 2 dropdown menus that capture user inputs “hour” and “minutes”, so I can get time like 7:15. Then after some math on this time the user is given several “hour:minute” time options. What is the best way to build this logic and what helper to use? Should I create models for hours and minutes?
You can build up the hour and minutes drop down.
<%= f.select :hours, '1'..'24' %>
<%= f.select :minutes, '01'..'59' %>
or you can use gem
You can use time_select to get hours and minutes from user.

Having trouble getting Rails form builder to respect a given time zone for a time_select input

I cannot get my rails input helpers to respect a given time zone. I've looked at all the existing issues on this subject and code examples. It seems like this should be simple, but I'm getting nowhere. I'm hoping someone here can quickly see the error of my ways:
I have a Pro model in Rails with a time field called "work_start_time".
# schema.rb
create_table "pros", force: true do |t|
t.time "work_start_time"
I have a very basic form for my Pro that includes some additional lines to help me debug:
Work_start_time in UTC:<br>
<%= #pro.work_start_time %><br>
Work_start_time in PST:<br>
<% Time.use_zone("Pacific Time (US & Canada)") do %>
<%= #pro.work_start_time.in_time_zone %><br>
<% end %>
Form for work_start_time using PST:<br>
<%= form_for #pro do |f| %>
<% Time.use_zone("Pacific Time (US & Canada)") do %>
<%= f.time_select :work_start_time %><br>
<% end %>
<% end %>
Now I would expect the second and third times to be the same, since they're both supposed to be displayed in PST. But the time_select fails to respect it and shows the time in UTC instead:
What am I doing wrong?? I've tried using datetime_select. I've tried providing the work_start_time as a value: argument to the select method. No dice.
This is a simplified example - normally the time zone is actually a stored current_user.time_zone and we set Time.use_zone as an around_filter in the application controller. But those are disabled for this simplified experiment.
Ruby 2.2 (same issue with 2.0)
Rails 4.1.8 (same issue with 4.2.6 and Ruby 2.2)
I haven't tried using Time.use_zone do, but you might try adding to_s to the end of your #pro.work_start_time.in_time_zone call. Alternatively you could also use Moment.js and Moment time zone and render in javascript.
I found it easier to have the front end use moment to return utc times in forms, and to convert utc times to a default time zone.

Create multiple entries in model through nested form via simple_form

When a user creates a vacancy I want them to be able to save either 1 range of dates or multiple separate dates.
So I have 2 models, Vacancy and Vacancyschedule.
Vacancyschedule includes vacancy_id start_date end_date start_hour end_hour (to save a range) and when it is multiple seperate dates, I just want to leave the end_date empty and have multiple entries and combine them through my vacancy_id.
This is my code in my view:
... other code to ask for vacancy params, nothing special, nothing broken ...
#Code to create entry in vacancyschedule
<%= t.simple_fields_for :vacancyschedule do |p| %>
<%= p.input :start_date, :as => :date_picker, :label => false%>
<%= p.input :start_hour, as: :time, default: Time.parse('09:00')%>
<%= p.input :end_hour, as: :time, default: Time.parse('17:00')%>
<% end %>
And then I have some javascript that adds another exact copy one of those blocks when a user wants to add a second seperate date.
Now for my question:
The format in which they are passed is very strange. As you can see I ask for :start_date, :start_hour and :end hour and this is what I get:
{"name"=>"", "street"=>"", "description"=>"", "skill_id"=>"",
"jobtype_id"=>"", "wage"=>"", "vacancyschedule"=>
{"start_date"=>"27/10/15", "end_date"=>"", "start_hour"=>"",
"end_hour"=>"", "start_hour(1i)"=>"2015", "start_hour(2i)"=>"10",
"start_hour(3i)"=>"26", "start_hour(4i)"=>"21",
"start_hour(5i)"=>"00", "end_hour(1i)"=>"2015", "end_hour(2i)"=>"10",
"end_hour(3i)"=>"26", "end_hour(4i)"=>"17", "end_hour(5i)"=>"00"},
"vacancyscheduele"=>{"start_date"=>"", "start_hour(1i)"=>"2015",
"start_hour(2i)"=>"10", "start_hour(3i)"=>"26", "start_hour(4i)"=>"09",
"start_hour(5i)"=>"00", "end_hour(1i)"=>"2015", "end_hour(2i)"=>"10",
"end_hour(3i)"=>"26", "end_hour(4i)"=>"17", "end_hour(5i)"=>"00"},
"tag_list"=>""}, "radio"=>"on", "commit"=>"Create Vacancy",
"controller"=>"vacancies", "action"=>"create", "employer_id"=>"2"}
From what I can see is that they are passed in very different variables and incomplete (not all dates are passed).
Does anyone have any experience with this issue? How would I either grab these elements or how do I prevent them from being pushed in that format?
Eeeehhh, I hate nested forms. I always go back to the basics when I have to deal with them. Checkout these fine railscasts. The second one I think is better aimed at your problem but the first one helps understand it:
http://railscasts.com/episodes/196-nested-model-form-part-1
http://railscasts.com/episodes/197-nested-model-form-part-2
At the end of the day, you need to know what your html is generating and make sure it is what Rails is expecting in the backend. Also, you must account for the nested objects at load and save time. Your HTML as it stands, is not generating parseable objects as far as Rails is concerned. On the other hand, it is hard to deal with the crap date/time picker for Rails. I usually like to put in something that generates a better date picker. One that generates one string that is easily maps to a database field. If you are using bootstrap, I'd use the bootstrap datepicker gem.

Resources