"Argh! I need a date/time widget"
I recently came across the need for a date/time parser that used natural language processing. Maleega’s date picker is extremely barebones and needed replacing. I left it barebones because I wanted time to figure out how to build a good date/time picker. Every one that I’ve used has felt clunky. This is probably because of the inherent challenges with date and time.
There’s a lot of complexity in time that we don’t think about because we’ve learned to talk about time from a very young age. It feels simple because talking about it comes naturally to us. We interchange the context we talk about time as if it was nothing. Sometimes we talk about dates, like “March 23 at 5:45 pm”. Sometimes we use relative terms such as “last week” or “next thursday”.
This is extremely difficult to convey in a GUI. Most just show a calendar and a time picker, which handles only one way of thinking about time. Some have been trying natural language shortcuts like “tomorrow”, but there are so many permutations of these. The dropdowns either get very large or a user is unlikely to see the option they want.
This reminded me of a book I read that referenced Lotus Agenda. The software was built in the 80s, so it was mostly text based. I haven’t tried it since it only runs in DOS (and I’m not motivated enough to try), but it seemed to have handled date/time inputs using user entered text.
While I know I have a programmer’s bias towards text interfaces, this seemed like the right approach to a date/time widget. Allowing the user to enter in any text is the most versatile way of handling the various time contexts we are used to thinking in. Granted, natural language processing is challenging to say the least, but a GUI widget can still be provided to show the user what time the system thinks they are trying to convey.
Now comes the hard part: figuring out how to process arbitrary user text into a date and time.
My first inclination with all things is “I can build it!”
Thankfully, I actually thought this through before starting. Building something like this could easily take weeks or months, especially since I’ve never done NLP programming before. While I want this feature in the product, it is not exactly a core piece of the product. It’s not like I’m selling a date/time service. This also sounded like a common enough problem that there must be existing solutions out there.
I found four that looked promising at first glance:
- Natty - although it hasn’t been updated in years, it heavily uses the ANTLR project so I imagine it has a ton of development time spent in building its NLP engine.
- Maya - written by the same person who built the requests library. I love the requests library so this deserved a chance.
- Sugar.js - Sugar has been around for a while and they have an impressive looking list of date strings they can parse. Despite knowing that they’ve probably cherry picked examples that work well, it was still an impressive display of functionality.
With my list, I needed to test these for accuracy. NLP is not exactly a solved problem. Other types of programming libraries like HTTP clients either work or they don’t. Parsing date/time from arbitrary user input is going to be a lot less well defined.
I made a spreadsheet of various test cases that I’d like to see the results of and then ran each library against these test cases. The results were pretty interesting because of the wide range of results the different libraries would provide on the same text.
Context makes NLP especially difficult. For example, what does “April 25th” mean? Is it for the previous April 25th or the next April 25th? Without specifying a year, most of the libraries assumed you always mean the current year. Chrono tries to be a bit more clever by going to the closer date (e.g. I ran these in November so the August date was interpreted as 2018, but the April date was interpreted as 2019). For a reminder feature in Maleega, I’m going to always want the future date.
Then there’s the issue of what to do with text that the library can’t parse. For “last day of the week” and “last day of the month”, both Chrono and Natty only understood “last day” as if you wanted yesterday.
Crono interpreted “two weeks from now” as just “now”.
“Second month of next year” looks like it was just interpreted as “next year” for 3 of the libraries.
This behavior probably makes sense if the library expects to be handed whole sentences or paragraphs. It becomes problematic if you’re passing in text that’s solely meant to be for the date because it results in a lot of incorrect dates.
Then there’s handling ambiguous time statements like “tomorrow at 9”. Without a qualifier, Natty and Chrono both assumed 9 am, which is fine. Sugar seemed to fail spectacularly by providing dates in 2001. It needs an am/pm qualifier for time.
Natty also had some really strange behavior where it understood “8:35am january 9th”, but couldn’t understand “aug 21th at 5:45am”. It could also understand “apr 25th at 5:45 pm” so in some formats it needs a space between the time and the am/pm, but in other formats it doesn’t.
Overall, Maya had the fewest incorrect answers, but it also had the fewest answers in general. It also took several seconds to process text where it couldn’t generate an answer. This made Maya unuseable for Maleega because this is too long of a delay for user feedback.
Chrono had the most inaccuracies, but it did catch a bunch of the ones that Sugar missed. Natty was better than Chrono in every way, but Natty is going to have to run on the server. Natty and Sugar also complement each other nicely by catching the other’s incorrect answers.
While each of these libraries have pretty big gaps individually, they provide pretty good results when combined. That leads to considering a way to leverage all 3 libraries.
Sugar and Chrono are both speedy and can be run in the front end. That makes running both of them for every text input an easy decision to make. In cases where they agree on a time, it’s usually correct. I can just display that to the user.
The disagreements are where things get interesting. I can make a request to an endpoint that will return a result from Natty, but what do I do with it? There are cases where 2 of the 3 libraries are wrong (and have the same wrong answer), so a simple voting algorithm won’t work. I can’t really use NLP to determine accuracy either because the whole point of this is to avoid building an NLP library myself.
I could try inserting some hard coded checks such as if “last day” is in the text, trust Sugar. This has the risk of creating some really messy code because I may need to create a lot of these.
For the cases I have, if Sugar is wrong, it is FANTASTICALLY wrong (e.g. the 2001 dates). That could be useful. I could rely on Sugar in most cases, but use Natty when Sugar provides a date that is more than 2 years away or when it can not provide a date. This would remove the need to use Chrono.
I’m still figuring a lot of this out. There are obviously more date/time formats I can test out. That could help me find more patterns in the results between the libraries that I can use in determining which one is correct. I could also try and find more libraries to test out. I picked these because they seemed popular enough and they took less than 5 minutes to install (I appreciate easy installs). If you have any feedback or advice, I’m all ears!