jsbeans

jsbeans  1.0.0

jsbeans > jsbeans > SimpleDateFormat.js (source view)
Search:
 
Filters
/*!
 * Copyright (c) 2009 Francesco Mele jsbeans@francescomele.com
 *
 * This Software is licenced under the LGPL Licence (GNU Lesser General 
 * Public License).
 * In addition to the LGPL Licence the Software is subject to the 
 * following conditions:
 * 
 * 	i	every modification must be public and comunicated to the Author
 * 	ii	every "jsbean" added to this library must be self consistent 
 * 		except for the dependence from jsbeans-x.x.x.js
 * 	iii	copyright notice and this permission notice shall be included 
 * 		in all copies or substantial portions of the Software
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

/**
 * Pure javascript implementaion of <code>java.text.SimpleDateFormat</code>.
 * <pre>
 * Letter | Date or Time Component | Presentation | Examples
 * G | Era designator | Text | AD
 * y | Year | Year | 1996; 96
 * M | Month in year | Month | July; Jul; 07
 * w | Week in year | Number | 27
 * W | Week in month | Number | 2
 * D | Day in year | Number | 189
 * d | Day in month | Number | 10
 * F | Day of week in month | Number | 2
 * E | Day in week | Text | Tuesday; Tue
 * a | Am/pm marker | Text | PM
 * H | Hour in day (0-23) | Number | 0
 * k | Hour in day (1-24) | Number | 24
 * K | Hour in am/pm (0-11) | Number | 0
 * h | Hour in am/pm (1-12) | Number | 12
 * m | Minute in hour | Number | 30
 * s | Second in minute | Number | 55
 * S | Millisecond | Number | 978
 * z | Time zone | General time zone | Pacific Standard Time; PST; GMT-08:00
 * Z | Time zone | RFC 822 time zone | -0800
 * </pre>
 * @namespace jsbeans
 * @class SimpleDateFormat
 * @param [pattern] {String} default "MM/dd/yyyy hh:mm"
 * @constructor 
 * */
jsbeans.SimpleDateFormat = function() {
	this.pattern = arguments[0] || "MM/dd/yyyy hh:mm";
	//default Locale
	this.locale = "en";
};

/**
 * Array of default locales: english ('en') and italian ('it')
 * @config jsbeans.SimpleDateFormat.LOCALES
 * @static
 * */
jsbeans.SimpleDateFormat.LOCALES = ["en", "it"];

/**
 * Returns the available locales as set by <code class="prop">jsbeans.SimpleDateFormat.LOCALES</code> and, eventually, other than defaults.
 * @method getAvailableLocales
 * @static
 * */
jsbeans.SimpleDateFormat.getAvailableLocales = function() {
	return jsbeans.SimpleDateFormat.LOCALES;
};
/**
 * A new Locale must be in the format
 * <pre>
 * {
 * name: 'xx'// two chars, e.g. 'fr'
 * months: ['first month', 'second month',  ...]// month's names
 * days: ['first day starting from sunday', 'day after sunday', ...]//day's names starting from sunday
 * }
 * </pre>
 * @method addLocale
 * @param locale {JSON}
 * */
jsbeans.SimpleDateFormat.addLocale = function(locale) {
	// a new Locale must be well formed ...
	if (!locale || !locale.name || !locale.months || !locale.days) {
		return;
	}
	// ... and months and days must be arrays of right length
	if (locale.months.constructor.toLowerCase() != "array" || locale.days.constructor.toLowerCase() != "array" || locale.months.length != 12 || locale.days.length != 7) {
		return;
	}
	// now we can go on
	jsbeans.SimpleDateFormat.LOCALES.push(locale.name);
	jsbeans.SimpleDateFormat.MONTH_NAMES[locale.name] = locale.months;
	jsbeans.SimpleDateFormat.DAY_NAMES[locale.name] = locale.days;
};

/**
 * Loads a script tag: <code>&lt;script type="text/javascript" src="{@param}scriptPrefixSlashEnded/SimpleDateFormatLocale_{@param}localeName.js"> &lt;/script></code>.<br/>
 * As a choice of design we do not throws exceptions so no feedback will be return to user in case of error
 * @method loadLocale
 * @param localeName {String}
 * @param [scriptPrefixSlashEnded] {String} optional url prefix. Default ""
 * @static
 * */
jsbeans.SimpleDateFormat.loadLocale = function(localeName /*, scriptPrefixSlashEnded*/) {
	var head = document.getElementsByTagName("head")[0] || document.documentElement;
	var script = document.createElement("script");
	script.type = "text/javascript";
	script.src = (arguments[1] || "") + "SimpleDateFormatLocale_" + localeName + ".js";
	head.appendChild(script);
};

/**
 * Array of month names for default locales.
 * @config jsbeans.SimpleDateFormat.MONTH_NAMES
 * @static
 * */
jsbeans.SimpleDateFormat.MONTH_NAMES = {
	en: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
	,it: ["Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "giugno", "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", "Dicembre"]
};

/**
 * Array of day names for default locales.
 * @config jsbeans.SimpleDateFormat.DAY_NAMES
 * @static
 * */
jsbeans.SimpleDateFormat.DAY_NAMES = {
	en: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
	,it: ["Domenica", "Luned\u00ec", "Marted\u00ec", "Mercoled\u00ec", "Gioved\u00ec", "Venerd\u00ec", "Sabato"]
};

/**
 * Applies the given {@param} <code class="param">pattern</code> string to <code class="this">this</code> date format.
 * @method applyPattern
 * @param pattern {String}
 * */
jsbeans.SimpleDateFormat.prototype.applyPattern = function(pattern) {
	this.pattern = pattern;
};

/**
 * Sets {@param} <code class="param">locale</code> to <code class="this">this</code> date format.<br/>
 * The {@param} <code class="param">locale</code> must be loaded first, otherwise "en" will be used.
 * @method setLocale
 * @param locale {String}
 * */
jsbeans.SimpleDateFormat.prototype.setLocale = function(locale) {
	if (typeof locale == "undefined" || locale == null) {
		locale = "en";
	}
	locale = locale.toLowerCase();;
	var locales = jsbeans.SimpleDateFormat.getAvailableLocales();
	for (var i = 0, l; l = locales[i]; i++) {
		if (l == locale) {
			this.locale = locale;
			return;
		}
	}
	this.locale = "en";
};

/**
 * Returns the current locale
 * @method getLocale
 * @return {String}
 * */
jsbeans.SimpleDateFormat.prototype.getLocale = function() {
	return this.locale;
};

/**
 * Returns the localized month's name from its index.
 * @method getMonthName
 * @param monthZeroBased {Integer} the index of month
 * @return {String} the localized month's name
 * */
jsbeans.SimpleDateFormat.prototype.getMonthName = function(monthZeroBased) {
	return jsbeans.SimpleDateFormat.MONTH_NAMES[this.getLocale()][monthZeroBased];
};

/**
 * Returns the localized day's name from its index.
 * @method getDayName
 * @param dayZeroBased {Integer} the index of day
 * @return {String} the localized day's name
 * */
jsbeans.SimpleDateFormat.prototype.getDayName = function(dayZeroBased) {
	return jsbeans.SimpleDateFormat.DAY_NAMES[this.getLocale()][dayZeroBased];
};

/**
 * Returns a formatted string starting from {@param} <code class="param">date</code> based on current pattern.
 * @method format
 * @param date {Date} the date to format
 * @return {String} the formatted date
 * */
jsbeans.SimpleDateFormat.prototype.format = function(date) {
	var res = "";
	var pad = function(str, len) {
		while (str.length < len) {
			str = "0" + str;
		}
		return str;
	};
	var formatText = function(data, letterOccurs, minLen) {
		return (letterOccurs >= 4) ? data : data.substr(0, Math.max(minLen, letterOccurs));
	};
	var formatNumber = function(data, letterOccurs) {
		return pad("" + data, letterOccurs);
	};
	var matches;
	var searchString = this.pattern;
	var matchedString, quoted, patternLetters, otherLetters, otherChars;
	while ((matches = jsbeans.SimpleDateFormat._Constants.regex.exec(searchString))) {
		matchedString = matches[0];
		quoted = matches[1];
		patternLetters = matches[2];
		otherLetters = matches[3];
		otherChars = matches[4];

		if (quoted) {
			if (quoted == "''") {
				res += "'";
			} 
			else {
				res += quoted.substring(1, quoted.length - 1);
			}
		} 
		else if (otherLetters) {
			// do nothing
		} 
		else if (otherChars) {
			res += otherChars;
		} 
		else if (patternLetters) {
			var patternLetter = patternLetters.charAt(0);
			var letterOccurs = patternLetters.length;
			var rawData = "";
			switch (patternLetter) {
				case "a":
					rawData = (date.getHours() >= 12) ? "PM" : "AM";
					break;
				case "d":
					rawData = date.getDate();
					break;
				case "D":
					rawData = jsbeans.SimpleDateFormat._getDayInYear(date);
					break;
				case "E":
					rawData = this.getDayName(date.getDay());
					break;
				case "F":
					rawData = 1 + Math.floor((date.getDate() - 1) / 7);
					break;
				case "G":
					rawData = "AD";
					break;
				case "h":
					rawData = (date.getHours() % 12) || 12;
					break;
				case "H":
					rawData = date.getHours();
					break;
				case "k":
					rawData = date.getHours() || 24;
					break;
				case "K":
					rawData = date.getHours() % 12;
					break;
				case "m":
					rawData = date.getMinutes();
					break;
				case "M":
					rawData = date.getMonth();
					break;
				case "s":
					rawData = date.getSeconds();
					break;
				case "S":
					rawData = date.getMilliseconds();
					break;
				case "w":
					rawData = jsbeans.SimpleDateFormat._getWeekInYear(this._getMinimalDaysInFirstWeek(), date);
					break;
				case "W":
					rawData = jsbeans.SimpleDateFormat._getWeekInMonth(this._getMinimalDaysInFirstWeek(), date);
					break;
				case "y":
					rawData = date.getFullYear();
					break;
				case "Z":
					rawData = date.getTimezoneOffset();
					break;
			}
			switch (jsbeans.SimpleDateFormat._Constants.types[patternLetter]) {
				case jsbeans.SimpleDateFormat._Constants.TEXT2:
					res += formatText(rawData, letterOccurs, 2);
					break;
				case jsbeans.SimpleDateFormat._Constants.TEXT3:
					res += formatText(rawData, letterOccurs, 3);
					break;
				case jsbeans.SimpleDateFormat._Constants.NUMBER:
					res += formatNumber(rawData, letterOccurs);
					break;
				case jsbeans.SimpleDateFormat._Constants.YEAR:
					if (letterOccurs <= 3) {
						res += ("" + rawData).substr(2, 2);
					} 
					else {
						res += formatNumber(rawData, letterOccurs);
					}
					break;
				case jsbeans.SimpleDateFormat._Constants.MONTH:
					if (letterOccurs >= 3) {
						res += formatText(this.getMonthName(rawData), letterOccurs, letterOccurs);
					} 
					else {
						res += formatNumber(rawData + 1, letterOccurs);
					}
					break;
				case jsbeans.SimpleDateFormat._Constants.TIMEZONE:
					var prefix = rawData > 0 ? "-" : "+";
					var absData = Math.abs(rawData);

					var hours = "" + Math.floor(absData / 60);
					hours = pad(hours, 2);

					var minutes = "" + (absData % 60);
					minutes = pad(minutes, 2);

					res += prefix + hours + minutes;
					break;
			}
		}
		searchString = searchString.substr(matches.index + matches[0].length);
	}
	return res;
};
/**
 * @method _setMinimalDaysInFirstWeek
 * @param days {Integer}
 * @private
 * */
jsbeans.SimpleDateFormat.prototype._setMinimalDaysInFirstWeek = function(days) {
	this.minimalDaysInFirstWeek = days;
};
jsbeans.SimpleDateFormat.prototype._getMinimalDaysInFirstWeek = function(days) {
	return typeof(this.minimalDaysInFirstWeek) == "undefined" ? 1 : this.minimalDaysInFirstWeek;
};

/**
 * @config constants
 * @type JSON
 * @private
 * @static
 * @final
 * */
jsbeans.SimpleDateFormat._Constants = {
	regex: /('[^']*')|(G+|y+|M+|w+|W+|D+|d+|F+|E+|a+|H+|k+|K+|h+|m+|s+|S+|Z+)|([a-zA-Z]+)|([^a-zA-Z']+)/,
	TEXT2: 0,
	TEXT3: 1,
	NUMBER: 2,
	YEAR: 3,
	MONTH: 4,
	TIMEZONE: 5,
	types: {
		G: 0/*this.TEXT2*/,
		y: 3/*this.YEAR*/,
		M: 4/*this.MONTH*/,
		w: 2/*this.NUMBER*/,
		W: 2/*this.NUMBER*/,
		D: 2/*this.NUMBER*/,
		d: 2/*this.NUMBER*/,
		F: 2/*this.NUMBER*/,
		E: 1/*this.TEXT3*/,
		a: 0/*this.TEXT2*/,
		H: 2/*this.NUMBER*/,
		k: 2/*this.NUMBER*/,
		K: 2/*this.NUMBER*/,
		h: 2/*this.NUMBER*/,
		m: 2/*this.NUMBER*/,
		s: 2/*this.NUMBER*/,
		S: 2/*this.NUMBER*/,
		Z: 5/*this.TIMEZONE*/
	},
	ONE_DAY: 86400000/*24 * 60 * 60 * 1000*/,
	ONE_WEEK: 604800000 /*7 * this.ONE_DAY*/
};

/**
 * Utility method
 * @method _floorToMidnight
 * @private
 * @static
 * */
jsbeans.SimpleDateFormat._floorToMidnight = function(year, month, day) {
	var res = new Date(year, month, day, 0, 0, 0);
	res.setMilliseconds(0);
	return res;
};

/**
 * Utility method
 * @method _before
 * @private
 * @static
 * */
jsbeans.SimpleDateFormat._before = function(d1, d2) {
	return d1.getTime() < d2.getTime();
};

/**
 * Utility method
 * @method _getUTCTime
 * @private
 * @static
 * */
jsbeans.SimpleDateFormat._getUTCTime = function(d) {
	return Date.UTC(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds());
};

/**
 * Utility method
 * @method _getTimeDifference
 * @private
 * @static
 * */
jsbeans.SimpleDateFormat._getTimeDifference = function(d1, d2) {
	return jsbeans.SimpleDateFormat._getUTCTime(d1) - jsbeans.SimpleDateFormat._getUTCTime(d2);
};

/**
 * Utility method
 * @method _getPreviousSunday
 * @private
 * @static
 * */
jsbeans.SimpleDateFormat._getPreviousSunday = function(d) {
	var midday = new Date(d.getFullYear(), d.getMonth(), d.getDate(), 12, 0, 0);
	var prevSunday = new Date(midday.getTime() - d.getDay() * jsbeans.SimpleDateFormat._Constants.ONE_DAY);
	return jsbeans.SimpleDateFormat._floorToMidnight(prevSunday.getFullYear(), prevSunday.getMonth(), prevSunday.getDate());
};

/**
 * Utility method
 * @method _getWeekInYear
 * @private
 * @static
 * */
jsbeans.SimpleDateFormat._getWeekInYear = function(minimalDaysInFirstWeek, d) {
	if (typeof(this.minimalDaysInFirstWeek) == "undefined") {
		minimalDaysInFirstWeek = 1;
	}
	var prevSunday = jsbeans.SimpleDateFormat._getPreviousSunday(d);
	var startOfYear = jsbeans.SimpleDateFormat._floorToMidnight(d.getFullYear(), 0, 1);
	var sundays = jsbeans.SimpleDateFormat._before(prevSunday, startOfYear) ? 0 : 1 + Math.floor(jsbeans.SimpleDateFormat._getTimeDifference(prevSunday, startOfYear) / jsbeans.SimpleDateFormat._Constants.ONE_WEEK);
	var daysInFirstWeek =  7 - startOfYear.getDay();
	var res = sundays;
	if (daysInFirstWeek < minimalDaysInFirstWeek) {
		res--;
	}
	return res;
};

/**
 * Utility method
 * @method _getWeekInMonth
 * @private
 * @static
 * */
jsbeans.SimpleDateFormat._getWeekInMonth = function(minimalDaysInFirstWeek, d) {
	if (typeof(this.minimalDaysInFirstWeek) == "undefined") {
		minimalDaysInFirstWeek = 1;
	}
	var prevSunday = jsbeans.SimpleDateFormat._getPreviousSunday(d);
	var startOfMonth = jsbeans.SimpleDateFormat._floorToMidnight(d.getFullYear(), d.getMonth(), 1);
	var sundays = jsbeans.SimpleDateFormat._before(prevSunday, startOfMonth) ? 0 : 1 + Math.floor((jsbeans.SimpleDateFormat._getTimeDifference(prevSunday, startOfMonth)) / jsbeans.SimpleDateFormat._Constants.ONE_WEEK);
	var daysInFirstWeek =  7 - startOfMonth.getDay();
	var res = sundays;
	if (daysInFirstWeek >= minimalDaysInFirstWeek) {
		res++;
	}
	return res;
};

/**
 * Utility method
 * @method _getDayInYear
 * @private
 * @static
 * */
jsbeans.SimpleDateFormat._getDayInYear = function(d) {
	var startOfYear = jsbeans.SimpleDateFormat._floorToMidnight(d.getFullYear(), 0, 1);
	return 1 + Math.floor(jsbeans.SimpleDateFormat._getTimeDifference(d, startOfYear) / jsbeans.SimpleDateFormat._Constants.ONE_DAY);
};

Copyright © 2016 Francesco Mele. All rights reserved.