The DateField can accept manual text input from users and convert them to a Date object. However DateField's default parse function accepts only numeric date values and there is a strict compliance to follow the assigned formatString. Like for example, you type in January 16 2010, the formatString assign to the DateField by default is MM/DD/YYYY, you expect it to return 01/16/2010 but it fails.
We create a custom parseFunction that will return a Date object. We will still follow the arrangement from the assigned format but let users type common formats of date. Also we are going to need a function that returns the index of a string representation of a month.
We are going to need two functions. First is the "customParseFunction" which will be use by our DateField instance. Second is a helper that will return an index of a given month name. Take note we are just interested in the first 3 letters of the months.
In our customParseFunction, we define regular expressions for string matching to get valid values of a string date. Typically, date values are group into 3. We have year, month and a date. In order to generate a Date object, we must get the month, year and date values of the string passed in the method. If matches return less than 3, we assume the string date is invalid else we proceed to reading of the date. In order to arrange the date values we would need the formatString. Gladly, the DateField's parseFunction has a second parameter which is the format(by default: MM/DD/YYYY). We will get the order of the date values base on the formatString and then properly place the date values to each date field. After that, we then do little checking to see if the values are valid, then we create a new Date Instance and passing the values. We then return this Date instance for the DateField to use.
public function customParseFunction(value:String, format:String):Date
{
//if there are no separators... e.g. mmddyy
var sepRegEx:RegExp = /\W/g;
var sepArray:Array = value.match(sepRegEx);
//we use the default...
if(!sepArray || sepArray.length < 1)
return DateField.stringToDate(value, format);
//we have separators!
/**
* This expression will validate a date We are only interested in first 3 letters of the month names.
* the expression \d{4} matches 4 succeeding digits while \d{1,2} will match 1 or 2 succeeding
* digits. When a user types in a regular date this expression will return 3 matches. One for date,
* one for month and another one for year. if it doesn't return 3 matches, we assume the date is
* incorrect. Also the matching will trim unecessary characters. Like for example Januaryblahblah,
* with regExp matching Jan, we will only be getting Jan.
**/
var dateRegEx:RegExp = /(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec|\d{4}|\d{1,2})/gi;
var values:Array = value.match(dateRegEx);
//owww.. user entered an invalid date, let's return nothing...
if(values.length != 3)
return null;
/**
* Now, we need to know what order the date has. For this we will be dependent on the
* format specified or assigned in the DateField. We will be making a reading task
* to know where we will assign the values.
**/
var formatString:String = format.toLowerCase();
var monthStartIndex:int = formatString.indexOf("m");
var dateStartIndex:int = formatString.indexOf("d");
var yearStartIndex:int = formatString.indexOf("y");
/**
* By getting the index of the first occurence of each character,
* we will know which value goes into month, date and year.
**/
//create reading tasks
var monthReadTask:Object = {type:"month", index: monthStartIndex};
var dateReadTask:Object = {type:"date", index: dateStartIndex};
var yearReadTask:Object = {type:"year", index: yearStartIndex};
var taskArray:Array = [monthReadTask, dateReadTask, yearReadTask];
//then sort base on the index!
taskArray.sortOn("index", Array.NUMERIC);
//containers for our String date values.
var monthStr:String = "";
var dateStr:String = "";
var yearStr:String = "";
for(var i:int = 0; i < taskArray.length; i++)
{
switch(taskArray[i].type)
{
case "month":
monthStr = values[i];
break;
case "date":
dateStr = values[i];
break;
case "year":
yearStr = values[i];
break;
}
}
var month:int;
var date:int;
var year:int;
//Then lets check if the values are valid...
if(isNaN(Number(monthStr)))
month = getMonthIndex(monthStr);
else
month = Number(monthStr)-1; //because month in actionscript is zero based.
if(month == -1)
return null;
if(!isNaN(Number(dateStr)))
date = Number(dateStr);
else
return null; //we can only read numbers...
//also try to see if date is in the range of allowable dates.
if(date > 31 || date < 1)
return null; //we can't have dates like 32 or -1
//now year would be pretty tricky...
if(!isNaN(Number(yearStr)))
year = Number(yearStr);
else
return null;
if(yearStr.length == 2 && year < 70) //anything below 70 will be..
year += 2000; //treated as a year of the new millenium.
//finally we create the date...
var newDate:Date = new Date(year, month, date);
return newDate;
}
/**
* This little helper will return us an integer representation of our month name.
* take note we already clean the value with regExp.
**/
public function getMonthIndex(value:String):int
{
var monthNames:Object = {};
monthNames["jan"] = 0;
monthNames["feb"] = 1;
monthNames["mar"] = 2;
monthNames["apr"] = 3;
monthNames["may"] = 4;
monthNames["jun"] = 5;
monthNames["jul"] = 6;
monthNames["aug"] = 7;
monthNames["sep"] = 8;
monthNames["oct"] = 9;
monthNames["nov"] = 10;
monthNames["dec"] = 11;
var valueMonth:String = value.toLowerCase();
if(monthNames[valueMonth] > -1)
return monthNames[valueMonth];
else
return -1;
}
Then on your DateField instance, we say
in mxml:
<mx:DateField parseFunction="customParseFunction"/>
in AS3:
dateFieldInstance.parseFunction = customParseFunction;
You can view the application here.
I've added a source code for your reference guys. Feel free to ask any questions.
+