Pretty regularly while working on projects at Flatiron School I would look at my friend Justin Nazari and, with extreme prejudice and exasperation, I would sigh "Ugh... Dates...".
We found early and often that dealing with user DateTime input can be a bear. We ran into seemingly countless issues. First we needed to normalize the values being stored from user input, which for us are primarily in the Eastern Daylight Time Zone, against those in the database, which were stored in UTC so we could compare them against one another to display only events that have yet to occur. Then we hacked together a lot of methods to present the date in a pretty fashion, only to find after a morning's work that Rails Action View Date Helpers already gave us almost everything we needed!
One more big hill to climb was replacing the (awful!) HTML date time picker fields. Emphasis on the S in fields because this archaic input type uses FIVE separate dropdown boxes(!) for Month, Day, Year, Hour and Minute. This is an eye sore and a terrible burden on users, so I finally decided to get rid of it.
To do so, I utilized the Bootstrap3 DateTime Picker for Rails gem, which also requires Moment.js Rails as a dependency.
gem 'momentjs-rails', '>= 2.9.0'
gem 'bootstrap3-datetimepicker-rails', '~> 4.17.37'
And add the following to the
application.scss file as well (we were already using Bootstrap so I only had to add line three):
Previously we were using a form-for
datetime_select input field, which is replaced by the following:
<div class='input-group date' id='datetimepicker'>
<input type='text' class="form-control" name="event[time]" id="event_time" <% if @event.start_time %>value="<%= @event.start_time.strftime("%m/%d/%Y %I:%M %p") %><% else %> value="<%= DateTime.now.strftime("%m/%d/%Y %I:%M %p") %>"<% end %>">
<span class="glyphicon glyphicon-calendar"></span>
datetime_picker.js file. Here we use the HTML id selector on line 1 above
id='datetimepicker' to select the div and give it the properties of the DateTime Picker.
$('.date#datetimepicker').datetimepicker().data("DateTimePicker").format('ddd, MMM D YYYY, h:mm A')
Also note that this is where we give
.data("DateTimePicker").format() a string as defined by Moment.js. This defines the format our date and time will be not only displayed in to the user in the input, but also how it will be submitted to us. Since we're just submitting a string of text in this format, but we want to build a DateTime object with that string, this Moment.js format has ramifications on how we associate this time with an event later.
Even before this change we had an instance method
parse_time in our Event model, so I wanted to continue using that. It actually required no change, to the method, just the parameters that I was passing in from the previous five-dropdown-menu variant of datetime picker, which had to be parsed themselves.
In order to keep things consistent, here we assign an event's
start_time property by taking our string of text and using Rails'
Time.zone.parse method, into which we pass our sting of date and time information.
self.start_time = Time.zone.parse(time_as_string)
The takeaway here was while originally using numerals for Month, Rails would assume I was giving it the date in the order the rest of the world likes (DD/MM/YYYY, smallest to biggest. Makes sense! Just like Metric!) But I wasn't. So when I entered 7/8/2016 I meant July 8th, but Rails built me a lovely DateTime object for August 7th. Easy solution: use month names in the date picker! Rails can parse them just as well and there will be no mistakes, this time.
What we end up with is a simple, beautiful user experience, with one text entry field and an intuitive selector that pops up at the click of a button.