michaelp0730
9/9/2014 - 4:26 AM

RequireJS module that provides the basic date formatting functionality that is shockingly missing from the JavaScript spec. (~1k minified, ~

RequireJS module that provides the basic date formatting functionality that is shockingly missing from the JavaScript spec. (~1k minified, ~500 bytes gzipped)

define(
	'dateFormat',
	function() {
		var DAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Firday', 'Saturday'],
			MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
			MONTHS_ABBREV = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'June', 'July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'],
			datePrototype = Date.prototype,
			formatCharacters = {
				// DAY
				// Numeric representation (with leading zero)
				d: function() {
					return padWithZero(applyFormat(this, 'j'));
				},
				// Numeric representation
				j: datePrototype.getDate,
				// Textual representation of day of the week (abbreviated)
				D: function() {
					return MONTHS_ABBREV[applyFormat(this, 'j')];
				},
				// Full textual representation of day of the week
				l: function() {
					return DAYS[applyFormat(this, 'j')];
				},
 
 
				// MONTH
				// Full textual representation
				F: function() {
					return MONTHS[applyFormat(this, 'M')];
				},
				// Numberic representation (with leading zero)
				m: function() {
					return padWithZero(applyFormat(this, 'M'));
				},
				// Numberic reprsentation
				M: datePrototype.getMonth,
 
 
				// YEAR
				// Full numeric representation
				Y: datePrototype.getFullYear,
				// Two digit representation
				y: function() {
					return applyFormat(this, 'Y').substr(-2);
				},
 
 
				// TIME
				// Lowercase Ante meridiem and Post meridiem
				a: function() {
					return applyFormat(this, 'G') > 11 ? 'am' : 'pm';
				},
				// Uppercase Ante meridiem and Post meridiem
				A: function() {
					return applyFormat(this, 'a').toUpperCase();
				},
 
				g: function() {
					var hours = applyFormat(this, 'G');
					return hours - (hours > 12 && 12);
				},
				G: datePrototype.getHours,
				h: function() {
					return padWithZero(applyFormat(this, 'g'));
				},
				H: function() {
					return padWithZero(applyFormat(this, 'G'));
				},
 
				i: datePrototype.getMinutes,
				s: datePrototype.getSeconds
			},
			REPLACEMENT_REGEX = new RegExp(Object.keys(formatCharacters).join('|'), 'g');
 
		/**
		 * Applys a given format given the date context
		 * @param {object} context - The date object to format
		 * @param {string} name - Name of the formatter to used
		 * @returns {string} The formated date property
		 */
		function applyFormat(context, name) {
			return '' + formatCharacters[name].call(context);
		}
 
		/**
		 * If number is less than 10 prefixes number with a zero
		 * @param {string} str - The number to pad
		 * @returns {string} The resulting string
		 */
		function padWithZero(str) {
			return (str < 10 && '0') + +str;
		}
 
 
		/**
		 * Formats a date using the given format string
		 * @param {object} date - Date object to format
		 * @param {string} format - Format string
		 * @returns {string} Formated date string
		 */
		return function(date, format) {
			if (!(date instanceof Date)) {
				throw new TypeError('Expected Date object.');
			}
 
			return format.replace(REPLACEMENT_REGEX, function(fn) {
				return applyFormat(date, fn);
			});
		};
	}
);