package com.falkensweb.as3.utils {
	public class DateUtil {	
		
		import mx.utils.StringUtil;
		import mx.collections.ArrayCollection;
		
		public static const millisecondsPerDay:int = 1000 * 60 * 60 * 24;
		
		private static const kmDays:Array = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
		
		public static const monthNames:Array=["January" , "February" ,"March" ,
					"April" , "May" , "June" , "July" , "August" , "September" ,
					"October" , "November" , "December"]; 
		
		/**
		 *  Determine if a year is a leap year			 */
		public static function isLeapYear(year:Number):Boolean	{	
		     return (   year % 4 == 0 &&	
		             (year % 100 != 0 || year % 400 == 0) );
		}
		
		/**
		 *  Determine number of days in month	
		 *  (including leap years)	
		 */	
		public static function daysInMonth(year:Number,mNbr:Number):Number // month is from 0 - 11	
		{	
		  //http://board.flashkit.com/board/showthread.php?threadid=561381	
		  return kmDays[mNbr] + (mNbr == 1 && isLeapYear(year));	
		}
		
		/**
		 * Return (float) number of days betwen two dates		 */
		public static function daysBetween(a:Date,b:Date):Number{
			if (b.getTime() < a.getTime()){
				var t:Number=a.getTime();
				a=new Date(b.getTime());
				b=new Date(t);	
			}
			return (b.getTime()-a.getTime())/millisecondsPerDay;
		} 
		
		/**
		 * Add the indicated number of days to a date		 */ 
		public static function addDaysTo(i:Number,d:Date):Date{
			return new Date(d.getTime()+(i*millisecondsPerDay));
		} 

		/**
		 * Return the date with (non-UTC) time set to 23:59:59		 */
		public static function endOfDay(h:Date):Date{
			h.seconds=23;	
			h.hours=23;
			h.minutes=23;
			h.milliseconds=59;
			return h;
		}
		
		/**
		 * Return the date with (non-UTC) time set to 0:0:0
		 */
		public static function startOfDay(d:Date):Date{
			d.seconds=0;	
			d.hours=0;
			d.minutes=0;
			d.milliseconds=0;			
			return d;
		}
		
		/**
		 * Parse a string into a Date (will not work with strings that are datetimes)
		 * Will parse as if you are in the UK locale
		 * Bloody Flex Date(string) constructor doesn't work with commonly formated dates
		 *	 22/3/06 => 3 Oct 1907 (nb not even right in the US)!
		 *	 10 dec 06 => fails
		 * This method understands: (all will parse to 10th Decemeber 2006 00:00:00)
		 * 10 Dec 2006
		 * 10 dec 2006
		 * Dec 10 2006
		 * dec 10 2006
		 * 10 December 2006
		 * 10 december 2006
		 * December 10 2006
		 * december 10 2006
		 * 10.12.2006
		 * 10.12.06
		 * 10/12/06
		 * 10/12/2006
		 * etc.
		 * See testDateUtil.mxml in root of as3 utils project for full list
		 * - dates containing month names work with 'st' or 'th' after day of month		 */
		public static function parseDate(s:String):Date{
			var a:Array;
			var m:String;
			var y:String;
			s=StringUtil.trim(s);
			if (s.length == 0){	//move along...
				return new Date(s);
			}
			if (s.indexOf('.')>0){ //assume d.m.y
				a=s.split('.');
				if (a[2].length == 2){
					return new Date(20+a[2],a[1]-1,a[0]);	//TODO fix Y3K problem
				}else{
					return new Date(a[2],a[1]-1,a[0]);
				}
			}
			if (s.indexOf('/')>0){ //assume d/m/y
				a=s.split('/');
				if (a[2].length == 2){
					return new Date(20+a[2],a[1]-1,a[0]);	//TODO fix Y3K problem
				}else{
					return new Date(a[2],a[1]-1,a[0]);
				}
			}
			
			//'n[n][st] str nnnn => needs uppercase inital letter
			var p:RegExp = /^(\d{1,2})(?:|th|st|rd|nd) ([a-zA-Z]{3,}) (\d{4})$/;   	
			var res:Object=p.exec(s);
			if (res != null){
				m=initCap(res[2])
				if (isValidMonthName(m)){					
					return new Date(res[1]+" "+m.substr(0,3)+" "+res[3]);
				}
			}	
			//as above but two digit dates
			p = /^(\d{1,2})(?:|th|st|rd|nd) ([a-zA-Z]{3,}) (\d{2})$/;   
			res=p.exec(s);
			if (res != null){
				m=initCap(res[2]);
				y=20+res[3];						//TODO fix Y3K problem
				if (isValidMonthName(m)){					
					return new Date(res[1]+" "+m.substr(0,3)+" "+y);
				}
			}		
			
			//'str n[n][st] nn[nn] as above two cases but transposed month name and day
			p = /^([a-zA-Z]{3,}) (\d{1,2})(?:|th|st|rd|nd) (\d{4})$/;   	
			res=p.exec(s);
			if (res != null){
				m=initCap(res[1])
				if (isValidMonthName(m)){					
					return new Date(res[2]+" "+m.substr(0,3)+" "+res[3]);
				}
			}	
			p = /^([a-zA-Z]{3,}) (\d{1,2})(?:|th|st|rd|nd) (\d{2})$/;   
			res=p.exec(s);
			if (res != null){
				m=initCap(res[1]);
				y=20+res[3];						//TODO fix Y3K problem
				if (isValidMonthName(m)){					
					return new Date(res[2]+" "+m.substr(0,3)+" "+y);
				}
			}
			
			return new Date(s);	//pass over to the Date constructor for anything else
		}
		
		/**
		 * Determine if the string is a valid Gregorian English month name
		 * case must be correct too.		 */
		public static function isValidMonthName(s:String):Boolean{
			var a:ArrayCollection=new ArrayCollection(monthNames);
			s=StringUtil.trim(s);
			if (s.length<3){
				return false;
			}
			if (s.length>3){
				if (a.contains(s)){
					return true;
				}
				return false;
			}
			
			//try to find a match with the inital 3 letters
			for each (var i:String in monthNames){
				var t:String=i.substr(0,3);
				if (t == s){
					return true;	
				}
			}	
			
			return false;
		}
		
		private static function initCap(s:String):String{			
			return s.substr(0,1).toUpperCase()+s.substr(1,s.length-1);				
		}
		
	}	
}
