Cody asks:
I have a date field that consists of three drop down menus. (1) Month; (2) Day; (3) Year. I assemble these by using CreateDate(yyyy,mm,dd), which works great. My question involves checking to make sure the values the user selects from the dropdowns are valid before the CreateDate() function is called. For example, if a user selects 2/31/2008 (which is wrong, only 28 days in Feb), I haven't been able to figure out how to validate the date with IsDate() or IsValid("date",value) before it tries to assemble it which inevitably fails with the error: MONTH. Do you have any ideas, suggestions, or a maybe giant Internet finger pointing me in the right direction?
Ah yes, the giant Internet finger. I do have that. Thanks for asking. Seriously though, how can we solve this? While the best option would be client side, let me demonstrate a server side solution first.
Let's begin with a real simple form, set up as you desribed:
<form action="test.cfm" method="post">
Month: <select name="month">
<cfloop index="x" from="1" to="12">
<cfoutput><option value="#x#">#monthAsString(x)#</option></cfoutput>
</cfloop>
</select><br />
Day: <input type="text" name="day" maxlength="2" size="2"><br />
Year: <input type="text" name="year" maxlength="4" size="4"><br />
<input type="submit">
</form>
Nothing too fancy here. I didn't even bother doing the code to remember the values in case you have an error. Months are a drop down and the day and year values are both free form. (Don't think that months are safe though. A user could easily use a Firefox plugin to manipulate the DOM, or just even save the form to the desktop and edit it.)
Cody mentioned he had issues with isDate, but it really isn't that hard to use. Just treat the form values like a string. Consider:
<cfif not structIsEmpty(form)>
<cfif isDate("#form.month#/#form.day#/#form.year#")>
<cfset dVal = createDate(form.year,form.month,form.day)>
<cfoutput><p>Your date: #dateFormat(dVal, "long")#</p></cfoutput>
</cfif>
</cfif>
All I do here is create a date string in the form month/day/year. This will handle everything. It will handle my putting "Ray" for a year and it will handle February 31 not being a valid date. Note that isDate will only check for American dates, because America rocks and the rest of the world doesn't. If you dare to live in one of those other countries (where you probably have that stupid sensible metric system) you can use lsIsDate.
What about client side? I did a bit of searching at the jQuery side. I found a plugin to do validation, but from what I can see, it just worked on one text field (Ie, check field X to see if it is a valid date). I also saw a date picker UI thingy. I did not see a function that would let me pass in the values for M, D, and Y and see if together they created a valid date.
The last time I did this in JavaScript was close to 5 years ago, and I remember it being a royal pain in the butt. If I remember right, you could make a date with February 31, but the date object would simply change it to March 2.
So if anyone has any suggestions, speak up. Of course, you still want the server side code anyway, since we all back up our client side validation with server side validation. Right?
Archived Comments
I don't like IsDate because it is not locale specific for instance IsDate('31/01/2008') and IsDate('01/31/2008') both return true however CreateDate(2008, 31, 01) will throw an error.
I tend to do:
<cftry>
<cfset mydate = CreateDate(form.y, form.m, form.d) />
<cfcatch>
<cfset mydate = "" />
</cfcatch>
</cftry>
<cfif IsDate(myDate)>
<!--- invalid date --->
<cfelse>
<!--- valid date --->
</cfif>
Sorry forgot to add that LSIsDate() has an issue as mentioned on livedocs which is why I don't use it:
"Changed behavior: when using the SUN JRE 1.3.1 on an English(UK) locale, this function returns False for a date that has a one-digit month or day (for example, 1/1/01). To work around this, insert a zero in a one-digit month or day (for example, 01/01/01). "
Well I did mention very clearly that isDate is US specific. As for lsIsDate and the issue you brought up - it would be trivial to get around:
if len(form.day) is 1
form.day = "0" & form.day
I see your point now about 31/1/08. If the user changed their form field on the client side, they could change the value to 31.
Hmpth.
I prefer not to use try/catch in general unless it is absolutely necessary.
You could do validation on the month value first. Ie, numeric and 1-12. I'd probably do that that instead of try/catch.
One note on the JRE 1.3/UK thing - since CF ships with 1.5, and 1.6 is the most recent, this bug would probably not affect too many people.
Ahah, interestingly enough, isValid does work right and blocks 31/1, if you specify usdate:
#isValid("usdate", "31/01/2008")#
will return no. So if you change my code above then you would be fine. (Assuming you want American dates.)
Yeah - I didn't explain myself very well :)
That's a good point about the JVM version I just like to know that it'll work regardless of environment as sometimes clients install on their own servers *eek*.
I've noticed that some frameworks use try and catch when creating directories instead of using DirectoryExists() for performance reasons, so I've always considered that if used sparingly it is a valid technique, so I find you comment very interesting as you're a bit of a guru. Do you only use it to trap unexpected exceptions? (I'm not sure if the performance reasoning still applies with CF8.)
> I prefer not to use try/catch in general unless it is absolutely necessary
Hmm, interesting. Any particular reason(s)?
It seems to me that try/catch should be used only when you have no other way to check for some condition. It's a: "This is out of my hands situation, so I'll try like hell...". If I can check for some condition using regular methods, then I will.
Makes sense. Thanks!
Check out DateJS. It's a great JS library for parsing dates on the client side.
http://www.datejs.com/
I normally handle the client side implementation to create the date transform it back to a string and compare the two. If they are the same strings then they are the same date.
Why not just avoid the problem all together and use a date selector? Plenty around and easier for users as well.
What about CFAJAXPROXY ?
It should be very easy to make a "onsubmit" dateValidation cfc-function. And that function would then be used on both clientside and serverside. - Right ?
-Mikkel
Sure, that could be a nice way of doing it as well.
Hi, if you can't use a datepicker, the best way i think, in your form, a solution is to completely avoid the date validation: if you select year and month first, an ajax call cal load the day's select with the appropriate number of days; also, using the isLeapYear() function on the selected year, you can solve february too.
regards
salvatore
I have had issues with the date validation as well, but the following code has worked well for me. Plus I use a date picker. Hope this helps someone.
If request.form("yourfiled") <> "" Then
If Not IsDate(request.form("yourfiled")) or Len(request.form("yourfiled")) > 10 or Len(request.form("yourfiled")) < 8 or Not ISNumeric(Right(request.form("yourfiled"),4)) Then
myerror = myerror & "(yourfiled must be a valid date) "
End If
End IF