HomeRamblings  ⁄  Programming

Handling Date Entries

Published: May 10, 2008 (over 9 years ago)
Updated: over 2 years ago

Separate inputs for a single Date Entry? pop-up Calendars? Enforce one way (i.e. enter in “mm/dd/yyyy” format)? What happened to the end-user perspective? User input parsing and validation is classic computer science and usability fodder, yet we seem to get ever more clever at making it more cumbersome to enter a date than to get a date! (I’ll leave the pun implications to the reader…)

The Rails Way

Separating inputs into month, day, year drop down lists makes the user click three times with mouse (or worse, type, tab, type, tab, type. It looks cool and minimizes programming effort to get a guaranteed valid date entry, but before Rails came along, I haven’t seen this entry style in practice since the days of IBM 3270 terminal applications! At least in those old applications, there was a predictive key parser narrowing your choices as you typed so it was a seemless type and auto-tab to next field. Thus, a user could enter an entire date by typing just numerals (01012008 to get January 1st, 2008). Not only the default Rails input a cumbersome interface, you gotta merge those fields together come controller time since Rails doesn’t handle this out of box (or does it and I just can’t find the right helpers?)

The JavaScript way

Pop up calendars enabled via JavaScript seems to be the usual answer to the multiple entry boxes. They’re great and look pretty. They also seem to work best only when the date being entered happens to be quite near the current date. In this scenario, the user is usually two clicks away from a valid date. But this breaks down when you need specific dates from years ago…need to go back to January 2000? That’s either eight clicks of the back_one_year button, or drop down list, scan up and down and choose year, then repeat for the month. Today is May 10th, 2008, I need one click to open, 8 clicks to year 2000, then 4 clicks back to January, then final click on the desired day of the month. Tell me how that’s fun for the end-user?

Back to Basics

So, that leaves one field for data entry (as I don’t want to get into debugging javascript to auto-tab). One could argue that I could still make popups available as an end-user choice, but their use is seriously diminished in a reporting situation where there’s no getting around all those mouse clicks). So for my implementation, I’m leaving those javascript heavy pop-ups off. But now I got the age-old problem of collecting valid date inputs while letting the user enter the dates in the most familiar way, whether it be yyyy-mm-dd, mm/dd/yyyy, or MMM d yy or any other combination the user happens to like.

Chronic Dates

At first, I thought this was going to be a seriously challenging problem to solve. I had already seen the issues blogged about dealing with all those “default date formats” where I’d set up such only to break how Rails talks to the database and so on. Lots of interesting approaches there! But I digress. Isn’t there a simple approach to collecting and parsing that date entry without Javascript and without monkey patching Rails? Enter Chronic. It is a date parsing library that can intelligently parse and even guess at the dates entered from partial date specification (i.e. “May 5th” => “05/05/2009”). Twenty minutes after uncovering this library, I now have intelligent parsing added to my application. Now the trick’s to figure out how to incorporate parsing as part of standard validation and perhaps even get those smarts into a plugin so that I don’t have to futz with controllers with code like this:

1
2
3
start_date = Chronic.parse(data[:start_date], :guess => true, :context => :past)
end_date = Chronic.parse(data[:end_date], :guess => true, :context => :past)
raise "Invalid date format" if start_date.nil? || end_date.nil?

It would definitely be better to parse and validate through definition of a validation rule declaration. As I learn more Ruby, I will definitely revisit this with a cleaner approach, but at least I have a solid foundation for flexibly entering dates without slowing down the user or encumbering them with toys they won’t use.

Changing the Rails default format

Along the way to getting to know Rails, I wasn’t quite sure how to implement date formatting for all my date fields. The default rendering gave me “YYYY-MM-DD HH:MM,” which is a date plus a time! My first reaction was to shelter things behind a property (attribute getter/setter) with the following:

1
2
3
4
5
6
7
def formatted_start_date
  begin
    start_date.to_date.strftime('%m/%d/%Y')
  rescue 
    ''
  end
end

But I realized this was quickly going to get out of hand and started looking into how I could at least put the constant “%m/%d/%Y” into a globally accessible file. There were a lot of discussions about how setting the default formatting would mess up the database persistence mechanism, at least up to around Rails 1.2. Nowadays, the database libraries appear to use the “:db” format, thus leaving us free to set a :default we want for presenting to the users. I did this by creating a new file, “my_rails_project/config/initializers/format_strings.rb” and added the following:

1
2
3
# Sets default display of dates to mm/dd/yyyy format
ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS.merge! \
  :default => "%m/%d/%Y"

This takes care of the Date class, and that’s all I really need, but you can also override the Time default by merging to ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS. Once doing this, my property getters are reduced essentially to guaranteeing I’m type coercing to a Date class (and not a Time class).

1
2
3
4
5
6
7
def start_date
  begin
    start_date.to_date.to_s
  rescue 
    ''
  end
end
comments powered by Disqus