﻿// formatDate :
// a PHP date like function, for formatting date strings
// authored by Svend Tofte <www.svendtofte.com>
// the code is in the public domain
//
// see http://www.svendtofte.com/javascript/javascript-date-string-formatting/
// and http://www.php.net/date
//
// thanks to 
//  - Daniel Berlin <mail@daniel-berlin.de>,
//    major overhaul and improvements
//  - Matt Bannon,
//    correcting some stupid bugs in my days-in-the-months list!
//  - levon ghazaryan. pointing out an error in z switch.
//  - Andy Pemberton. pointing out error in c switch 
//
// input : format string
// time : epoch time (seconds, and optional)
//
// if time is not passed, formatting is based on 
// the current "this" date object's set time.
//
// supported switches are
// a, A, B, c, d, D, F, g, G, h, H, i, I (uppercase i), j, l (lowecase L), 
// L, m, M, n, N, O, P, r, s, S, t, U, w, W, y, Y, z, Z
// 
// unsupported (as compared to date in PHP 5.1.3)
// T, e, o

Date.prototype.formatDate = function (input,time) {
	
	var daysLong =    ["Sunday", "Monday", "Tuesday", "Wednesday", 
					   "Thursday", "Friday", "Saturday"];
	var daysShort =   ["Sun", "Mon", "Tue", "Wed", 
					   "Thu", "Fri", "Sat"];
	var monthsShort = ["Jan", "Feb", "Mar", "Apr",
					   "May", "Jun", "Jul", "Aug", "Sep",
					   "Oct", "Nov", "Dec"];
	var monthsLong =  ["January", "February", "March", "April",
					   "May", "June", "July", "August", "September",
					   "October", "November", "December"];

	var switches = { // switches object
		
		a : function () {
			// Lowercase Ante meridiem and Post meridiem
			return date.getHours() > 11? "pm" : "am";
		},
		
		A : function () {
			// Uppercase Ante meridiem and Post meridiem
			return (this.a().toUpperCase ());
		},
	
		B : function (){
			// Swatch internet time. code simply grabbed from ppk,
			// since I was feeling lazy:
			// http://www.xs4all.nl/~ppk/js/beat.html
			var off = (date.getTimezoneOffset() + 60)*60;
			var theSeconds = (date.getHours() * 3600) + 
							 (date.getMinutes() * 60) + 
							  date.getSeconds() + off;
			var beat = Math.floor(theSeconds/86.4);
			if (beat > 1000) beat -= 1000;
			if (beat < 0) beat += 1000;
			if ((String(beat)).length == 1) beat = "00"+beat;
			if ((String(beat)).length == 2) beat = "0"+beat;
			return beat;
		},
		
		c : function () {
			// ISO 8601 date (e.g.: "2004-02-12T15:19:21+00:00"), as per
			// http://www.cl.cam.ac.uk/~mgk25/iso-time.html
			return (this.Y() + "-" + this.m() + "-" + this.d() + "T" + 
					this.H() + ":" + this.i() + ":" + this.s() + this.P());
		},
		
		d : function () {
			// Day of the month, 2 digits with leading zeros
			var j = String(this.j());
			return (j.length == 1 ? "0"+j : j);
		},
		
		D : function () {
			// A textual representation of a day, three letters
			return daysShort[date.getDay()];
		},
		
		F : function () {
			// A full textual representation of a month
			return monthsLong[date.getMonth()];
		},
		
		g : function () {
		   // 12-hour format of an hour without leading zeros, 1 through 12!
		   if (date.getHours() == 0) {
			   return 12;
		   } else {
			   return date.getHours()>12 ? date.getHours()-12 : date.getHours();
		   }
	   },
		
		G : function () {
			// 24-hour format of an hour without leading zeros
			return date.getHours();
		},
		
		h : function () {
			// 12-hour format of an hour with leading zeros
			var g = String(this.g());
			return (g.length == 1 ? "0"+g : g);
		},
		
		H : function () {
			// 24-hour format of an hour with leading zeros
			var G = String(this.G());
			return (G.length == 1 ? "0"+G : G);
		},
		
		i : function () {
			// Minutes with leading zeros
			var min = String (date.getMinutes ());
			return (min.length == 1 ? "0" + min : min);
		},
		
		I : function () {
			// Whether or not the date is in daylight saving time (DST)
			// note that this has no bearing in actual DST mechanics,
			// and is just a pure guess. buyer beware.
			var noDST = new Date ("January 1 " + this.Y() + " 00:00:00");
			return (noDST.getTimezoneOffset () == 
					date.getTimezoneOffset () ? 0 : 1);
		},
		
		j : function () {
			// Day of the month without leading zeros
			return date.getDate();
		},
		
		l : function () {
			// A full textual representation of the day of the week
			return daysLong[date.getDay()];
		},
		
		L : function () {
			// leap year or not. 1 if leap year, 0 if not.
			// the logic should match iso's 8601 standard.
			// http://www.uic.edu/depts/accc/software/isodates/leapyear.html
			var Y = this.Y();
			if (         
				(Y % 4 == 0 && Y % 100 != 0) ||
				(Y % 4 == 0 && Y % 100 == 0 && Y % 400 == 0)
				) {
				return 1;
			} else {
				return 0;
			}
		},
		
		m : function () {
			// Numeric representation of a month, with leading zeros
			var n = String(this.n());
			return (n.length == 1 ? "0"+n : n);
		},
		
		M : function () {
			// A short textual representation of a month, three letters
			return monthsShort[date.getMonth()];
		},
		
		n : function () {
			// Numeric representation of a month, without leading zeros
			return date.getMonth()+1;
		},
		
		N : function () {
			// ISO-8601 numeric representation of the day of the week
			var w = this.w();
			return (w == 0 ? 7 : w);
		},
		
		O : function () {
			// Difference to Greenwich time (GMT) in hours
			var os = Math.abs(date.getTimezoneOffset());
			var h = String(Math.floor(os/60));
			var m = String(os%60);
			h.length == 1? h = "0"+h:1;
			m.length == 1? m = "0"+m:1;
			return date.getTimezoneOffset() < 0 ? "+"+h+m : "-"+h+m;
		},
		
		P : function () {
			// Difference to GMT, with colon between hours and minutes
			var O = this.O();
			return (O.substr(0, 3) + ":" + O.substr(3, 2));
		},      
		
		r : function () {
			// RFC 822 formatted date
			var r; // result
			//  Thu         ,     21               Dec              2000
			r = this.D() + ", " + this.d() + " " + this.M() + " " + this.Y() +
			//    16          :    01          :    07               0200
			" " + this.H() + ":" + this.i() + ":" + this.s() + " " + this.O();
			return r;
		},

		s : function () {
			// Seconds, with leading zeros
			var sec = String (date.getSeconds ());
			return (sec.length == 1 ? "0" + sec : sec);
		},        
		
		S : function () {
			// English ordinal suffix for the day of the month, 2 characters
			switch (date.getDate ()) {
				case  1: return ("st"); 
				case  2: return ("nd"); 
				case  3: return ("rd");
				case 21: return ("st"); 
				case 22: return ("nd"); 
				case 23: return ("rd");
				case 31: return ("st");
				default: return ("th");
			}
		},
		
		t : function () {
			// thanks to Matt Bannon for some much needed code-fixes here!
			var daysinmonths = [null,31,28,31,30,31,30,31,31,30,31,30,31];
			if (this.L()==1 && this.n()==2) return 29; // ~leap day
			return daysinmonths[this.n()];
		},
		
		U : function () {
			// Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)
			return Math.round(date.getTime()/1000);
		},

		w : function () {
			// Numeric representation of the day of the week
			return date.getDay();
		},
		
		W : function () {
			// Weeknumber, as per ISO specification:
			// http://www.cl.cam.ac.uk/~mgk25/iso-time.html
		
			var DoW = this.N ();
			var DoY = this.z ();

			// If the day is 3 days before New Year's Eve and is Thursday or earlier,
			// it's week 1 of next year.
			var daysToNY = 364 + this.L () - DoY;
			if (daysToNY <= 2 && DoW <= (3 - daysToNY)) {
				return 1;
			}

			// If the day is within 3 days after New Year's Eve and is Friday or later,
			// it belongs to the old year.
			if (DoY <= 2 && DoW >= 5) {
				return new Date (this.Y () - 1, 11, 31).formatDate ("W");
			}
			
			var nyDoW = new Date (this.Y (), 0, 1).getDay ();
			nyDoW = nyDoW != 0 ? nyDoW - 1 : 6;

			if (nyDoW <= 3) { // First day of the year is a Thursday or earlier
				return (1 + Math.floor ((DoY + nyDoW) / 7));
			} else {  // First day of the year is a Friday or later
				return (1 + Math.floor ((DoY - (7 - nyDoW)) / 7));
			}
		},
		
		y : function () {
			// A two-digit representation of a year
			var y = String(this.Y());
			return y.substring(y.length-2,y.length);
		},        
		
		Y : function () {
			// A full numeric representation of a year, 4 digits
	
			// we first check, if getFullYear is supported. if it
			// is, we just use that. ppks code is nice, but wont
			// work with dates outside 1900-2038, or something like that
			if (date.getFullYear) {
				var newDate = new Date("January 1 2001 00:00:00 +0000");
				var x = newDate .getFullYear();
				if (x == 2001) {              
					// i trust the method now
					return date.getFullYear();
				}
			}
			// else, do this:
			// codes thanks to ppk:
			// http://www.xs4all.nl/~ppk/js/introdate.html
			var x = date.getYear();
			var y = x % 100;
			y += (y < 38) ? 2000 : 1900;
			return y;
		},

		
		z : function () {
			// The day of the year, zero indexed! 0 through 366
			var s = "January 1 " + this.Y() + " 00:00:00 GMT" + this.O();
			var t = new Date(s);
			var diff = date.getTime() - t.getTime();
			return Math.floor(diff/1000/60/60/24);
		},

		Z : function () {
			// Timezone offset in seconds
			return (date.getTimezoneOffset () * -60);
		}        
	
	}

	function getSwitch(str) {
		if (switches[str] != undefined) {
			return switches[str]();
		} else {
			return str;
		}
	}

	var date;
	if (time) {
		var date = new Date (time);
	} else {
		var date = this;
	}

	var formatString = input.split("");
	var i = 0;
	while (i < formatString.length) {
		if (formatString[i] == "%") {
			// this is our way of allowing users to escape stuff
			formatString.splice(i,1);
		} else {
			formatString[i] = getSwitch(formatString[i]);
		}
		i++;
	}
	
	return formatString.join("");
}


// Some (not all) predefined format strings from PHP 5.1.1, which 
// offer standard date representations.
// See: http://www.php.net/manual/en/ref.datetime.php#datetime.constants
//

// Atom      "2005-08-15T15:52:01+00:00"
Date.DATE_ATOM    = "Y-m-d%TH:i:sP";
// ISO-8601  "2005-08-15T15:52:01+0000"
Date.DATE_ISO8601 = "Y-m-d%TH:i:sO";
// RFC 2822  "Mon, 15 Aug 2005 15:52:01 +0000"
Date.DATE_RFC2822 = "D, d M Y H:i:s O";
// W3C       "2005-08-15 15:52:01+00:00"
Date.DATE_W3C     = "Y-m-d%TH:i:sP";
