Validate date strings in Ruby with multiple accepted formats? - ruby-on-rails

I have some strings that might or might not be dates like:
"Hello World", "Sept 12, 2013", "Hello World Sept 12"
In this case, I'd like only the second one to be considered a proper date.
So far, I have been using Date.parse and the Chronic gem but they very lenient and convert strings like "a a" or "12-UNKN/34/OWN1" into acceptable dates.
For example:
Date.parse '12-UNKN/34/OWN1'
would return:
Tue, 12 Nov 2013
So, I am trying to restrict the accepted formats to a set of formats I can control:
09/12/2013
9/12/2013
9/12/13
09-12-2013
9-12-2013
9-12-13
and some formats with text inside like:
Sept 9, 2013 - with or without the coma, accepting Sep, Sept or September and with or without a dot after the month name, covering things like:
Sept. 9, 2013
Sept 09, 2013
Sept. 09, 2013
September 9, 2013
September 09, 2013
Any suggestion on a good way to do this in Ruby, either pure Ruby or with Rails?

I'll take a stab at this and elaborate on my comment. Separating the date from the other string allows you to validate the date in multiple formats before handing it off to Chronic:
validates_format_of :date, with: /\d{2,4}[-/]\d{1,2}[-/]\d{1,4}/, on: :create
That should handle most of the date formats you described.
However, a big issue is you won't know if someone is submitting a date in the US format or non-US format.
The bigger issue is know what part of the title string is in fact a date, and that would either be a more complicated regular expression, or make it easy on yourself and make it a separate field. You're getting somewhat close to trying to parse natural language which isn't cut and dry by any means.

Try something like this
validate :validate_some_date
private
def validate_some_date
errors.add("Created at date", "is invalid.") unless [
date_case_one,
date_case_two,
date_case_three
].all?
end
def date_case_one
# some regex
end
def date_case_two
# some regex
end
def date_case_three
# some regex
end

Related

Ruby string to Date object

I have a Mongoid field that is of type Date. I'm having all sorts of trouble searching for documents against this specific field. I receive dates as a string in this format: 10/20/2013. I thought something like Date.parse("10/20/2013") or "10/20/2013".to_date would be good enough to let me do something like MyModel.find_by(datefield: date_result) but this is giving me a ton of ArgumentError out of range type issues.
What's the easiest way to turn "10/20/2013" into a simple Date object that I can use to query against databases?
You get this:
Date.parse("10/20/2013")
ArgumentError: invalid date
The problem is 10/20. Ruby is an international language, and the values 10 and 20 are somewhat ambiguous. In the U.S. the "standard" date format is "MMDDYYYY", or %m%d%Y in date parsing terms. The majority of the world uses a different standard though, "DDMMYYYY" or %d%m%Y. Ruby uses the second format, with day first.
Looking at the difference, it's easy to see why Date.parse would be confused and complain. 10 is a sensible day, but 20 is nonsense as far as a month, so Ruby rejects it.
You can fix this by forcing the pattern used for parsing:
Date.strptime('10/20/2013', '%m/%d/%Y')
# => #<Date: 2013-10-20 ((2456586j,0s,0n),+0s,2299161j)>
You can use strptime:
Date.strptime('10/20/2013', '%m/%d/%Y')
=> <Date: 2013-10-20 ((2456586j,0s,0n),+0s,2299161j)>
Read this a list of possible formats
Date.parse("10/20/2013")
=> ArgumentError: invalid date
to
Date.parse("20/10/2013")
=> Sun, 20 Oct 2013

Validate Date String

We have a new field being added to our app where the client wants to be able to put in Sept 22. The input will be part of an import with 100 or so records. I know there are many libraries for parsing it but we want to be able to validate it. In case someone were to make a typo. Any thoughts or libraries to do this?
DateTime.parse will parse "Sept 22" with current year.
you can just make a dateTime with specified year as
date = DateTime.parse("Sept 22")
date_time_with_year = DateTime.new(year, date.month, date.day)
Check out Chronic
You can do things like
Chronic.parse('may 27th', :now => Time.local(2000, 1, 1))
#=> Sat May 27 12:00:00 PDT 2000
It will attempt to guess what the string was trying to convey, by default (ie: "Sept 27" will actually parse to something like 2013-09-27 12:00:00 -0500

Date format bug when date is in Portuguese?

I'm seeing some odd behavior in our application coming from a user working in what I believe to be Portuguese.
I realized that Rails seems to be mis-interpreting the dates she is submitting. Here's some examples:
Date.parse("ter, 30 abr 2013 07:00:00 GMT-07:00")
=> Thu, 30 May 2013
Date.parse("dom, 5 mai 2013 07:00:00 GMT-07:00 -07:00")
ArgumentError: invalid date
Is there something extra I need to do correctly identify dates being submitted in other languages?
Date are parsed with Ruby Date class with the following documentation
http://www.ruby-doc.org/stdlib-1.9.3/libdoc/date/rdoc/Date.html#method-c-parse
I'm not sure it's a great idea to allow your users to type in the full date by themselves - they can mistype or assume different format. I suggest to use datepicker that with localized presentation based on the users location? One of the the many date pickers that allow this is http://docs.jquery.com/UI/Datepicker/Localization

Rails convert date format

I have a form input in my application that accepts dates in the format DD/MM/YYYY eg 02/05/2012 is 2nd May 2012.
What is the best way for me to convert this into a suitable format to be added to the database through ActiveRecord?
Is there a simply way of converting 02/05/2012 to 05/02/2012 before adding to the database?
This is similar to Getting rails to accept European date format (dd/mm/yyyy)
In your model, create a setter method, where "my_date" is your database field.
def my_date=(val)
Date.strptime(val, "%d/%m/%Y") if val.present?
end
For this specific format you can call DateTime.parse:
DateTime.parse("02/05/2012") # => Thu, 02 May 2012 00:00:00 +0000
2 Years later but it's something I came across too, here's my fix.
In your view, use:
<%= l article.created_at, format: :euro %> **make sure to add the l
And then in /config/locales/en.yml add:
en:
time:
formats:
euro: "%d/%m/%Y"
** you can also just add, euro: "%D" to display in the us date format.
Thanks.

Did I just find a bug in rails Date format?

In trying to parse a date, I have been racking my brain for hours:
Date.today.to_s
=> "06/07/2011"
Date.today
=> Tue, 07 Jun 2011
Date.parse Date.today.to_s
=> Wed, 06 Jul 2011
Date::DATE_FORMATS[:default]
=> "%m/%d/%Y"
The default format for to_s is different than the default format for parsing? Why would they do this to me?
Using Rails 3.0.5 with Ruby 1.9.2-p180
UPDATE
So thanks to your answers, I realize that the DATE_FORMATS is a rails thing while Date.format is using the ruby library (correct?). Is there a way then to parse dates/times with the default DATE_FORMAT without using strptime?
Normally, Date.today.to_s would return "2011-06-07", but since you set a default date format, it's using "06/07/2011" instead.
Date.parse easily recognizes the YYYY-MM-DD format, but when it sees 06/07/2011 it thinks that's really DD/MM/YYYY (not MM/DD/YYYY as you're expecting -- keep in mind that Date.parse knows nothing about Rails' default date format you set. The default date format is only for Rails' outputting of Date.to_s).
You can force it to parse a MM/DD/YYYY date like this:
Date.strptime(Date.today.to_s, "%m/%d/%Y")
# => Tue, 07 Jun 2011
I would guess that the to_s method uses a locale option to determine how to write the date.
I dont see how this is an issue though. Date.parse uses heuristics to parse the date so sometimes it will get it wrong.
The Chronic gem (link)solves pretty much all date parsing issues (and yes, they can be quite annoying).
Chronic.parse("06/07/2011")
#=> 2011-06-07 12:00:00 +0000

Resources