Better time input
Some history
Some time ago, Simon Willison published a "Better Date Input" javascript that accepts all kinds of user inputs and tries to figure out a date out of it. For example "2006-02-08", "today", "next Friday" and so on. It's really neat, the original blog posting is here and a demo is available. A rehash of the idea was made by Nathaniel Brown, adding a popup calendar and some other tweaks/features (talking about it and a demo).
Thanks to Derek of Subtitle Designs, who pointed out this "neato" script to me (for the second time!), I got curious as to what is involved in hacking the script to work with time instead of dates.
Result
- A demo - just type something time-like, like "9" or "1pm" or "12:34:56" or "1300"
- The javascript
How it works (in 10 easy steps (or less)
)
- User-entered value is passed to
magicTime(). magicTime()asksparseTimeString()to parse that string and to return aDateobject.parseTimeString()loops through an array of objects, each having a regular expression, trying to match the entered text.- Once a regexp match is found, a corresponding function is called to "translate" the match to a valid
Date - The result
Dateis passed back tomagicTime(), which calls the callback functiongetReadable()for any tweaking and formatting of the result before displaying it (default output is hh:mm:ss)
Improvements of the original idea
Apart from just processing time instead of dates, there are a few improvements to the original script.
- Regexps examples and (unit?) testing. In the array of objects where the regexp rules are defines, I've added another property - an array of examples illustrating sample matches. The purpose is two fold - to quickly provide a help with a list of supported formats and also to have the ability to test the regexps semi-automatically. So then the function
getExamples()is called, it produces a list of acceptable values. If the same function is called passing a TRUE parameter, these example values are actually parsed and the result of the parsing is returned. This allows to quickly test all the regexps. Click "help" and/or "run tests!" in the demo for an illustration. getReadable()- a function that formats the output is introduced, allowing the users of this script to customize it quickly without interfering with the main logic. In this function you can opt for dropping the seconds, or rounding to the next minute, or five minutes, or an hour, or anything that has to do with displaying the result of the successful parse.- Optional message area. The message area, placed under the text box is optional. There is now a
try..catchto make sure no JS error occurs if it's missing.
How to use the script
It shouldn't be a problem to integrate this into your application. You need to:
- Get the javascript
- (optionally) Tweak
getReadable()to match your needs. Remember that at this time all parsing work is done and you have a valid Date object which you only need to display to the user - (optionally) Place a div to display messages, related to the text box (see the demo). Name it with id
id-of-the-input-messages - Call
magicTime()when you think appropriate (likeonblur), passing the input element (e.g.document.getElementById('id-of-the-input'), or justthis, depending from where you call it)
Feedback
Any comments, bug reports or requests are welcome! Thanks!
This entry was posted on Wednesday, February 8th, 2006 and is filed under JavaScript. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.
Get notification for future posts: follow me on Twitter or subscribe to my RSS feed

March 4th, 2006 at 12:12 am
Hey, this is a pretty cool script but I had to make several mods to make it usable. Try some input strings such as “12:30 am” or “01:00 PM” and it doesn’t do so well. Let me know if you want the modified script. Thanks for the good start!
-Atul
March 4th, 2006 at 12:23 am
Thanks Atul!
I’d love to see how you’ve modified it.
May 11th, 2006 at 5:31 pm
what is the fix for those problems?
thanks alot!
Dave
September 26th, 2006 at 6:53 pm
I like what you have done here. I noticed an inconsistency though with how it handles ’12am’ (in its various forms). When reformatting any other time it converts to a 24-hour clock, but 12am in a 24-hour clock is 0:00. I actually modified it myself to do this and you can see it at this site
http://marriottschool.byu.edu/timeparse/time-parser.htm
I also added a clock input feature.
April 5th, 2007 at 4:38 am
When I input “830″ i get “11:00:00″.
Why??
April 5th, 2007 at 6:36 pm
Twan, that’s gotta be because the parser considers 83 as hour and 0 as minutes. If you do 0830, then it’s fine. But, yeah, it’ll be better if it figures out 8:30 and not 11.
Thanks for the input!
May 3rd, 2007 at 1:56 pm
If your initial text box has a style class applied and after submit the style is cleared, I found you can add that style name to the javascript on line 195:
input.className = ‘YOUR-CLASS-HERE’;
Thanks for the great script!
March 30th, 2008 at 3:02 pm
I wish someone would combine that with a calendar picker next to it, but where the icon is actually draggable. x-axis modifies the day, y-axis the months / year.
That way people could just click and drag to the right while the input field would indicate “next wednesday” “next thursday” until the day is so far off that it would just switch to numeric formats again.
May 27th, 2008 at 2:09 pm
Awesome script, it was _just_ what I was looking for!! Thank you!
@Mendrik, there is one like that, I saw it just the other day. Google JQUERY + Calendar popup
October 30th, 2008 at 12:43 am
Hey, great work on the script. I was looking for something like this but also implement the jQuery library (www.jquery.com) for other UI elements. I’ve updated your code to work as a jQuery plug in. If you want a copy of that drop me an email.
NB: The conversion is just a basic wrapper at this stage. I’d like to see it with output formatting options etc, but for now it just defaults to 24hour clock.
June 29th, 2010 at 2:26 pm
Hello,
great work, this is going to be super useful, but…
i think i found a possible bug!
it can’t seem to tell the difference between am and pm when the hour is 12.
for example:
unexpected results:
when i pass: 12:19am
i get: Tue Jun 29 2010 12:19:00 GMT-0400 (Eastern Daylight Time)
and when i pass:12:19pm
i get: Tue Jun 29 2010 12:19:00 GMT-0400 (Eastern Daylight Time)
expected results:
but when i pass:01:19pm
i get:Tue Jun 29 2010 13:19:00 GMT-0400 (Eastern Daylight Time)
and when i pass:01:19am
i get:Tue Jun 29 2010 01:19:00 GMT-0400 (Eastern Daylight Time)
the behaviour can be duplicated here:
http://www.phpied.com/files/time-parser/time-parser.htm
December 7th, 2010 at 7:57 pm
Here is a fixed script – I’ve adjusted getReadable to be a user-friendly representation for my purpose (i.e. “1:30 PM”)
And more importantly, the following inputs have been fixed by updating the timeParsePatterns, and adding a ‘get_hours” function:
1230am => 12:30 AM
130 => 1:30 AM
I’ve also added a “pm_default” option in case you want the default to be pm rather than am – here it is: http://pastie.org/1357481
I should mention that I made a couple modifications to the “magicTime” function itself that use jQuery and made it have a return value. If these don’t work for you, you can copy the original magicTime function, and it should work fine.
And if you do that, but you still want the default_pm option, it’s as simple as adding it to the parameter list of the function (pastie line 175), and tacking it on to the end of the call to parseTimeString (pastie line 178). It just has to be passed through.
June 22nd, 2011 at 3:20 pm
Added two more parsing patterns:
// Noon
{ re: /^noon/i,
example: new Array(‘noon’),
handler: function() {
var d = new Date();
d.setHours(12);
d.setMinutes(0);
d.setSeconds(0);
return d;
}
},
// Midnight
{ re: /^midnight/i,
example: new Array(‘midnight’),
handler: function() {
var d = new Date();
d.setHours(0);
d.setMinutes(0);
d.setSeconds(0);
return d;
}
},
July 1st, 2011 at 3:32 pm
Probably not that important, but the fact that 1PM turns to 1:00 AM is really annoying. So I added ‘i’ after all the ‘re:’s to ignore case. You may want to include that in the original.
For example
Before:
re: /(\d{1,2})(?:p| p)/,
After:
re: /(\d{1,2})(?:p| p)/i,
Much better, especially if you accidentally hit CAPS LOCK
November 4th, 2011 at 9:52 am
What is the license on this? Can I use it for commercial projects?