1 /**
  2  * @fileOverview Extensions/helper methods for Date objects
  3  */
  4 
  5 /**
  6  * Get the week number for this date
  7  * @param {Number} [dowOffset=0] the day of week the week "starts" on for your locale - it can be from 0 to 6. If dowOffset is 1 (Monday), the week returned is the ISO 8601 week number.
  8  * @returns {Number} the week number for this date.
  9  * @author <a href="http://www.meanfreepath.com">Nick Baicoianu at MeanFreePath</a>
 10  */
 11 Date.prototype.getWeekOfYear = function getWeekOfYear(dowOffset) {
 12 	dowOffset = typeof(dowOffset) == 'int' ? dowOffset : 0; //default dowOffset to zero
 13 	var newYear = new Date(this.getFullYear(),0,1);
 14 	var day = newYear.getDay() - dowOffset; //the day of week the year begins on
 15 	day = (day >= 0 ? day : day + 7);
 16 	var daynum = Math.floor((this.getTime() - newYear.getTime() - 
 17 	(this.getTimezoneOffset()-newYear.getTimezoneOffset())*60000)/86400000) + 1;
 18 	var weeknum;
 19 	//if the year starts before the middle of a week
 20 	if (day < 4) {
 21 		weeknum = Math.floor((daynum+day-1)/7) + 1;
 22 		if (weeknum > 52) {
 23 			nYear = new Date(this.getFullYear() + 1,0,1);
 24 			nday = nYear.getDay() - dowOffset;
 25 			nday = nday >= 0 ? nday : nday + 7;
 26 			/*if the next year starts before the middle of
 27  			  the week, it is week #1 of that year*/
 28 			weeknum = nday < 4 ? 1 : 53;
 29 		}
 30 	}
 31 	else {
 32 		weeknum = Math.floor((daynum+day-1)/7);
 33 	}
 34 	return weeknum;
 35 };
 36 
 37 /**
 38  * A Hash of Arrays, containing localized Days of the Week as strings, using either language or language-country letter location codes (e.g. 'en' or 'en-us')
 39  * @type {Object}
 40  * @example
 41  * //Adding French Candaian days of the week - this will only apply to users with French-Canadian language settings
 42  * //Use the language code only to apply to all variations of the language. Country specific variations have precedence.
 43  * Date.prototype.daysOfWeek['fr-ca'] = [
 44  * 	'dimanche',
 45  * 	'lundi',
 46  * 	'mardi',
 47  * 	'mercredi',
 48  * 	'jeudi',
 49  * 	'vendredi',
 50  * 	'samedi'
 51  * ];
 52  */
 53 Date.prototype.daysOfWeek = {
 54 	'en' : [
 55 		'Sunday',
 56 		'Monday',
 57 		'Tuesday',
 58 		'Wednesday',
 59 		'Thursday',
 60 		'Friday',
 61 		'Saturday'
 62 	]
 63 };
 64 
 65 /**
 66  * Hash of Arrays, containing localized Months of the Year as strings, using either language or language-country letter location codes (e.g. 'en' or 'en-us')
 67  * @type {Object}
 68  * @example
 69  * //Adding Spanish months of the year - this will apply to users with Spanish language settings in any country
 70  * //Use the language-country code only to apply to specific variations of the language. Country specific variations have precedence.
 71  * Date.prototype.daysOfWeek['es'] = [
 72  *	'enero',
 73  *	'febrero',
 74  *	'marzo',
 75  *	'abril',
 76  *	'mayo',
 77  *	'junio',
 78  *	'julio',
 79  *	'agosto',
 80  *	'septiembre',
 81  *	'octubre',
 82  *	'noviembre',
 83  *	'diciembre'
 84  * ];
 85  */
 86 Date.prototype.monthsOfYear = {
 87 	'en' : [
 88 		'January',
 89 		'February',
 90 		'March',
 91 		'April',
 92 		'May',
 93 		'June',
 94 		'July',
 95 		'August',
 96 		'September',
 97 		'October',
 98 		'November',
 99 		'December'
100 	]
101 };
102 
103 /**
104  * Get the days of the week in the best match for the current browser language settings. If the current language/country isn't available, falls back to language only, then English as last resort.
105  * @param {String} [lang] try to force the language to use. Can be a language-country code just a language code. If the specified language/country isn't available, falls back to language only, then English
106  * @returns {String[]} days of the week in an Array (index starts at 0)
107  * @see Date#daysOfWeek
108  * @example
109  * var t = new Date();
110  * var days = t.getDaysOfWeek('fr-ca'); //Attempt to get the days of the week as an Array, in this order: French-Canadian, French, English
111  */
112 Date.prototype.getDaysOfWeek = function getDaysOfWeek(lang) {
113 	if (!Object.isUndefined(lang) && !Object.isString(lang)) {
114 		throw new TypeError();
115 	}
116 
117 	lang = (lang || window.navigator.language || window.navigator.userLanguage).toLowerCase();
118 	if (!(lang in this.daysOfWeek)) {
119 		lang = lang.split('-')[0];
120 	}
121 	if (!(lang in this.daysOfWeek)) {
122 		lang = 'en';
123 	}
124 
125 	return this.daysOfWeek[lang];
126 };
127 
128 /**
129  * Get the months of the year in the best match for the current browser language settings. If the current language/country isn't available, falls back to language only, then English as last resort.
130  * @param {String} [lang] try to force the language to use. Can be a language-country code just a language code. If the specified language/country isn't available, falls back to language only, then English
131  * @returns {String[]} months of the year in an Array (index starts at 0)
132  * @see Date#monthsOfYear
133  * @example
134  * var t = new Date();
135  * var months = t.getMonthsOfYear('es-bo'); //Attempt to get the months of the year as an Array, in this order: Bolivian-Spanish, Spanish, English
136  */
137 Date.prototype.getMonthsOfYear = function getMonthsOfYear(lang) {
138 	lang = (lang || window.navigator.language || window.navigator.userLanguage).toLowerCase();
139 	if (!(lang in this.monthsOfYear)) {
140 		lang = lang.split('-')[0];
141 	}
142 	if (!(lang in this.monthsOfYear)) {
143 		lang = 'en';
144 	}
145 	
146 	return this.monthsOfYear[lang];
147 };
148 
149 
150 /**
151  * Get the number of Days in the current Month, including calculations for February in Leap Years
152  * @returns {Number} the number of Days in the current Month
153  */
154 Date.prototype.getDaysInMonth = function getDaysInMonth() {
155 	var lengths = [
156 		31,
157 		28,
158 		31,
159 		30,
160 		31,
161 		30,
162 		31,
163 		31,
164 		30,
165 		31,
166 		30,
167 		31
168 	];
169 	
170 	if (this.getMonth() === 1 && this.isLeapYear()) {
171 		return 29;
172 	}
173 	else {
174 		return lengths[this.getMonth()];
175 	}	
176 };
177 
178 /**
179  * Determine if current Year is a leap Year
180  * @returns {Boolean} true if current Year is a leap year, false if not
181  */
182 Date.prototype.isLeapYear = function isLeapYear() {
183 	return (((this.getFullYear() % 4 === 0) && (this.getFullYear() % 100 !== 0)) || this.getFullYear() % 400 === 0);
184 };
185 
186 /**
187  * Get day of the year
188  * @returns {Integer} the day of the year
189  */
190 Date.prototype.getDayOfYear = function getDayOfYear() {
191 	var days = 0;
192 	for (var i = 0; i < this.getMonth(); i++) {
193 		var tmpDate = new Date(this.getFullYear(), i, 1);
194 		days += tmpDate.getDaysInMonth();
195 	}
196 	
197 	days += this.getDate();
198 	
199 	return days;
200 };
201 
202 /**
203  * Generate a formatted string from a Date object according to the specified template. 
204  * Supports most of the same characters for date strings as the PHP date function, except date characters to be replaced must have '%' characters wrapping them.
205  * The following characters are supported, see {@link http://www.php.net/date} for more info<br />
206  * Days: d, D, j, l, N, S, w, z<br />
207  * Week: W<br />
208  * Month: F, m, M, n, t<br />
209  * Year: L, o, Y, y<br />
210  * Time: a, A, g, G, h, H, i, s<br />
211  * @param {String} format the template for the new String.
212  * @returns {String} the template with valid characters replaced with respective values
213  * @example
214  * var d = new Date();
215  * var message = d.formatDate('%l%, %F% %d% %Y%'); // "Thursday, January 22 2009" 
216  */
217 Date.prototype.formatDate = function formatDate(format) {
218 	if(!Object.isString(format)) {
219 		throw new TypeError();
220 	}
221 	var values = {
222 		// Day
223 		'd' : this.getDate().toPaddedString(),
224 		'D' : this.getDaysOfWeek()[this.getDay()].substr(0, 3),
225 		'j' : this.getDate(),
226 		'l' : this.getDaysOfWeek()[this.getDay()],
227 		'N' : this.getDay() + 1,
228 		'S' : '',
229 		'w' : this.getDay(),
230 		'z' : this.getDayOfYear(),
231 		// Week
232 		'W' : this.getWeekOfYear(1),
233 		// Month
234 		'F' : this.getMonthsOfYear()[this.getMonth()],
235 		'm' : (this.getMonth() + 1).toPaddedString(),
236 		'M' : this.getMonthsOfYear()[this.getMonth()].substr(0, 3),
237 		'n' : this.getMonth() + 1,
238 		't' : this.getDaysInMonth(),
239 		// Year
240 		'L' : this.isLeapYear() ? 1 : 0,
241 		'o' : this.getWeekOfYear(1) === 0 ? this.getFullYear() - 1 : this.getFullYear(),
242 		'Y' : this.getFullYear(),
243 		'y' : this.getYear(),
244 		// Time
245 		'a' : this.getHours() < 12 ? 'am' : 'pm',
246 		'A' : this.getHours() < 12 ? 'AM' : 'PM',
247 		'g' : this.getHours() < 12 ? this.getHours() + 1 : this.getHours() - 11,
248 		'G' : this.getHours(),
249 		'h' : (this.getHours() < 12 ? this.getHours() + 1 : this.getHours() - 11).toPaddedString(),
250 		'H' : this.getHours().toPaddedString(),
251 		'i' : this.getMinutes().toPaddedString(),
252 		's' : this.getSeconds().toPaddedString()
253 	};
254 	
255 	var keys = Object.getKeys(values).map(function (char) {return '%' + char + '%';});
256 	
257 	var str = format.replaceMulti(keys, Object.getValues(values));
258 			
259 	return str;
260 };