An Interesting “Feature” In Ruby’s DateTime.Parse
It’s not all rainbows and unicorns in the Ruby world… everything now and then you run into something that really makes you go “WHAT THE?!” … today’s misadventure comes curtesy of Ruby’s DateTime.parse method. Generally speaking, this method is quite useful and quite forgiving in what it can parse. It will take a large number of formats and do what it can, even with very little.
For example, did you know that this string will correctly parse: “SHOW ME THE MONEY!!!” … it results in a very “interesting” date… like this:
=> #<DateTime: 2010-12-13T00:00:00+00:00 (4911087/2,0/1,2299161)>
Don’t believe me? Open up IRB in ruby 1.9.2 and run this:</p>
Crazy, eh?! I thought so. Here’s the real fun, though… the ability for DateTime.parse to work with what should be a completely invalid date string caused a false-positive in a unit test for me earlier today. I wasn’t using “SHOW ME THE MONEY!!!” as the text, but I was passing in a string as “six_months_to_one_year”. The code that was parsing this string had some logic in it that was supposed to produce a value of “1” for any date that fell within the range of six months to one year. Oddly enough, this code and logic produced that result because DateTime.parse(“six_months_to_one_year”) parsed as a date that fell within the range I was looking for.
Needless to say, I was expecting an exception to be thrown when this string was parsed and was shocked when the test passed. My incredulity only increased as I tried out various random strings, date ranges in the form of strings, and nonsensical garbage characters – some of which parsed, too my surprise, and some of which didn’t, again to my surprise. For example “how many days are there?” throws an exception, but “how many days are there before the wedding?” does parse.
It’s crazy I tell you.
After some time, I began to notice a pattern in what parsed, though. At first, I noticed that any string with the word “month” in it parsed correctly. I started narrowing down the text until i only had the world “month”, then “mont”, then “mon” – so far, so good. But then “mo” failed. Aha! “mon” is the abbreviation for Monday! and what did the original “six_months_to_one_year” parse as? “Monday, December 13th.”
See the pattern with the other strings, yet? “show me the MONey” and “how many days are the until the WEDding?”, or “I sat on a thumbtack today! Ouch!” … they all have a day’s abbreviation in them.
No matter how badly munged the rest of the string is, the DateTime.parse will find that abbreviation and parse it as a date because of it. … at least in Ruby 1.9.2 – I haven’t tried on any other versions, yet.
Crazy, eh? Threw me for a serious loop, for quite some time.