1 /**	@namespace Functions for working with dates <br />
  2  *	See {@link http://php.net/strftime} for formatting options.
  3  */
  4 puredom.date = /** @lends puredom.date */ {
  5 	
  6 	/** Returns the current timestamp, in milliseconds.
  7 	 *	@function
  8 	 *	@returns {Number} timestamp
  9 	 */
 10 	now : (
 11 		Date.now ? function() {
 12 			return Date.now();
 13 		} : function() {
 14 			return +new Date();
 15 		}
 16 	),
 17 	
 18 
 19 	/** Create a date, optionally from a string.<br />
 20 	 *	This is a wrapper on new Date(str), adding support for more date formats and smoothing out differences between browsers.
 21 	 *	@param {String} [str=now]	A date string, parsed and used to set the initial date.
 22 	 *	@returns {Date} a new date object.
 23 	 */
 24 	create : function(str) {
 25 		var date;
 26 		if (str) {
 27 			str = (str+'').replace(/^([0-9]{4})\-([0-9]{2})\-([0-9]{2})T([0-9]{2})\:([0-9]{2})\:([0-9]{2})\.[0-9]{3}Z$/, '$1/$2/$3 $4:$5:$6');
 28 			date = new Date(str);
 29 		}
 30 		else {
 31 			date = new Date();
 32 		}
 33 		return date;
 34 	},
 35 	
 36 	
 37 	/**	Parse a string with the given format into a Date object.
 38 	 *	@param {String} str						A date string to parse
 39 	 *	@param {String} [format="%d/%m/%Y"]		A date format string. See {@link http://php.net/strftime} for available fields.
 40 	 *	@returns {Date|Boolean}	the date, or false on failure.
 41 	 */
 42 	parse : function(str, format) {
 43 		format = format || "%d/%m/%Y";
 44 		function setHours(hours, pm) {
 45 			if (pm===false || pm===true) {
 46 				temp.pm = pm===true;
 47 			}
 48 			if (hours || hours===0) {
 49 				temp.hours = hours;
 50 			}
 51 			hours = temp.hours;
 52 			if (temp.hours<12 && temp.pm) {
 53 				hours -= 12;
 54 			}
 55 			var i = rdate.getDate();
 56 			if (temp.hours===12 && temp.pm===false) {
 57 				if (rdate.getHours()!==0 || typeof pm==='boolean') {
 58 					rdate.setHours(0);
 59 				}
 60 			}
 61 			else {
 62 				rdate.setHours(hours);
 63 				rdate.setDate(i);
 64 			}
 65 		}
 66 		var origStr = str,
 67 			rdate = new Date(0),
 68 			temp = {},
 69 			weekdays = ['mo','tu','we','th','fr','sa','su'],
 70 			replacers = {
 71 				H : [/^[0-9]{1,2}/g, function(e){e=Math.round(e);setHours(e);}],
 72 				I : [/^[0-9]{1,2}/g, function(e){e=Math.round(e);setHours(e);}],
 73 				p : [/^[AP]M/gi, function(e){setHours(null, e.toLowerCase()==="pm");}],
 74 				M : [/^[0-9]{1,2}/g, function(e){rdate.setMinutes(Math.round(e));}],
 75 				a : [/^(Mon|Tue(s?)|Wed|Thu|Fri|Sat|Sun)/i, function(){}],									// dummy
 76 				A : [/^(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)/i, function(){}],			// dummy
 77 
 78 
 79 				d : [/^[0-9]{1,2}/g, function(e){temp.date=Math.round(e);rdate.setDate(temp.date);}],
 80 				m : [/^[0-9]{1,2}/g, function(e){temp.month=Math.round(e)-1;rdate.setMonth(temp.month);}],
 81 				B : [new RegExp('^('+this.months.join("|")+')','gi'), function(e){temp.month=date._getMonthIndex(e);rdate.setMonth(temp.month);}],
 82 				b : [/^(Jan|Feb|Mar|Apr|May|Jun(e?)|Jul(y?)|Aug|Sep(t?)|Oct|Nov|Dec)/gi, function(e){temp.month=date._getMonthIndex(e);rdate.setMonth(temp.month);}],
 83 				y : [/^[0-9]{2}/g, function(e){e=Math.round(e)+1900;if(e<1950){e+=100;}rdate.setFullYear(e);}],		// wrap 2-digit dates at 1950/2050
 84 				Y : [/^[0-9]{4}/g, function(e){temp.year=Math.round(e);rdate.setFullYear(temp.year);}]
 85 			},
 86 			index, rep, r;
 87 		replacers.l = replacers.I;
 88 		replacers.e = replacers.d;
 89 		replacers.P = replacers.p;
 90 		replacers.h = replacers.b;
 91 		
 92 		/**	@ignore */
 93 		function rp(e) {
 94 			rep[1](e);
 95 			return '';
 96 		}
 97 		
 98 		for (index=0; index<format.length; index++) {
 99 			if (format.charAt(index)==="%") {
100 				rep = null;
101 				if (str.charAt(0)===' ' && format.charAt(index)==='%') {
102 					str = str.substring(1);
103 				}
104 				for (r in replacers) {
105 					if (replacers.hasOwnProperty(r) && format.substring(index+1, index+1+r.length)===r) {
106 						rep = replacers[r];
107 						str = str.replace(rep[0], rp);
108 						index += rep.length-1;		// advance past the used symbol in format str
109 						break;
110 					}
111 				}
112 			}
113 			else {
114 				if (str.charAt(0)===format.charAt(index)) {
115 					str = str.substring(1);
116 				}
117 			}
118 		}
119 		
120 		if (temp.month || temp.month===0) {
121 			rdate.setMonth(temp.month);
122 		}
123 		if (temp.year || temp.year===0) {
124 			rdate.setFullYear(temp.year);
125 		}
126 		
127 		return rdate;
128 	},
129 
130 
131 	/** Alias of {@link puredom.date.parse}
132 	 *	@see puredom.date.parse
133 	 *	@deprecated
134 	 *	@private
135 	 */
136 	unformat : function(){return this.parse.apply(this,arguments);},
137 	
138 	
139 	/**	Get a formatted string representation of a Date object.
140 	 *	@param {String} date					A date object to convert
141 	 *	@param {String} [format="%d/%m/%Y"]		A date format string. See {@link http://php.net/strftime} for available fields.
142 	 *	@returns {String|Boolean}	the formatted date string, or false on failure.
143 	 */
144 	format : function(date, format) {
145 		format = format || "%d/%m/%Y";
146 		
147 		if (!date || date.constructor!==Date || !date.toDateString) {
148 			return false;
149 		}
150 		
151 		var dateStr = date.toDateString();
152 		if (!dateStr || dateStr.toLowerCase()==="invalid date") {
153 			return false;
154 		}
155 		
156 		if (dateStr==='NaN') {	// only trips in IE ("3 is not an object")
157 			return false;
158 		}
159 		
160 		var dateParts = dateStr.split(" "),
161 			hours = date.getHours(),
162 			hv = ((hours+11)%12)+1,
163 			m = date.getMonth()+1,
164 			replacers = {
165 				H : hours,						// 24 hour time
166 				I : (hv<10?"0":"") + hv,		// 12 hour time, leading 0
167 				l : hv,							// 12 hour time
168 				p : hours>11?"PM":"AM",
169 				P : hours>11?"pm":"am",
170 				M : (date.getMinutes()<10?"0":"") + date.getMinutes(),
171 				S : (date.getSeconds()<10?"0":"") + date.getSeconds(),		// seconds
172 				a : dateParts[0],
173 				A : this.weekdays[date.getDay()],
174 				d : dateParts[2],
175 				e : Math.round(dateParts[2]),
176 				m : (m<10?"0":"") + m,
177 				B : this.months[Math.round(dateParts[1])],
178 				b : dateParts[1],
179 				h : dateParts[1],
180 				y : dateParts[3].substring(2),
181 				Y : dateParts[3]
182 			};
183 		
184 		return format.replace(/%[HIlpPMSaAdemBbhyY]/gm, function(s) {
185 			var v = replacers[s.charAt(1)+''];
186 			return (v || v===0 || v===false) ? v : s;
187 		});
188 	},
189 	
190 
191 	/** @private */
192 	_getMonthIndex : function(m){
193 		m = m.substring(0,3).toLowerCase();
194 		for (var x=0; x<this.months.length; x++) {
195 			if (this.months[x].substring(0,3).toLowerCase()===m) {
196 				return x;
197 			}
198 		}
199 		return -1;
200 	},
201 	
202 
203 	/** Weekday names
204 	 *	@type Array(String)
205 	 */
206 	weekdays : ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],
207 
208 
209 	/** Month names
210 	 *	@type Array(String)
211 	 */
212 	months : ["January","February","March","April","May","June","July","August","September","October","November","December"]
213 	
214 };