function jDate(){
//	example: var cal=new jDate();
//	example: var cal=new jDate(2007,11);
//	example: var cal=new jDate(2006,2,29);if(cal.exists)...

	this.Version='0.21';
	this.nightly='20080530';

	this.now = new Date();
	/* GENERIC FUNCTIONS */
	this.zeroFill=function(q){
		q=String(q);
		var zeri=arguments[1]||2;
		while(q.length<zeri)q="0"+q;
		return q;
	}
	// DATE FORMAT (PHP style: http://it2.php.net/manual/it/function.date.php )
	var args = [] ;
	var opts = [] ;
	for(var i=0;i<arguments.length;i++){
		if(isNaN(arguments[i]))opts[opts.length]=arguments[i];
		else args[args.length]=arguments[i];
	}
	var startingLang=(opts.length>0)?opts[0]:"it";
	
	this.j = (args.length>2)?args[2]*1:this.now.getDate();		// day
	this.n = (args.length>1)?args[1]*1:(this.now.getMonth()+1);	// month
	this.Y = (args.length>0)?args[0]*1:this.now.getFullYear();	// year
	this.G = (args.length>3)?args[3]*1:this.now.getHours();
	this.i = this.zeroFill((args.length>4)?args[4]*1:this.now.getMinutes());
	this.s = this.zeroFill((args.length>5)?args[5]*1:this.now.getSeconds());
	this.u = (args.length>6)?zeroFill(args[6]*1,3):this.now.getMilliseconds();
	
	// check for valid date (and declaration of "checkDate" method)
	this.checkDate=function(yyyy,mm,gg){
		function y2k(q){return(q<1000)?q+1900:q;}
		var today=new Date();
		yyyy = ((!yyyy) ? y2k(today.getFullYear()):yyyy)
		mm = ((!mm) ? today.getMonth():mm-1)
		if (!gg) return false
		var test = new Date(yyyy,mm,gg)
		if ((y2k(test.getFullYear()) == yyyy) && (mm == test.getMonth()) && (gg == test.getDate()) ){
			return true
		}else{
			return false
		}
	}

	this.exists=this.checkDate(this.Y,this.n,this.j);
	this.suffix=[
		"st","nd","rd","th","th","th","th","th","th","th",
		"th","th","th","th","th","th","th","th","th","th",
		"st","nd","rd","th","th","th","th","th","th","th",
		"st"
	];
		
	/* LANGUAGE */
	this.lngStr = [];

	this.lngStr.it = {
		"dayName":["Domenica","Luned&#236;","Marted&#236;","Mercoled&#236;","Gioved&#236;","Venerd&#236;","Sabato"],
		"monthName":["Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno","Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre"],
		"labels":["Mostra il mese precedente","Mostra il mese successivo","Calendario eventi di"]
	};
	this.lngStr.en = {
		"dayName":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],
		"monthName":["January","February","March","April","May","June","July","August","September","October","November","December"],
		"labels":["Show previous month","Show next month","Events calendar of"]
	};
	this.lngStr.es = {
		"dayName":["Domingo","Lunes","Martes","Mi&#233;rcoles","Jueves","Viernes","Sabado"],
		"monthName":["Enero","Febrero","Marzo","Abril","Mayo","Junio","Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"],
		"labels":["Show previous month","Show next month","Events calendar of"]
	};
	this.lngStr.fr = {
		"dayName":["Dimanche","Lundi","Mardi","Mercredi","Jeudi","Vendredi","Samedi"],
		"monthName":["Janvier","F&#233;vrier","Mars","Avril","Mai","Juin","Juillet","Ao&#251;t","Septembre","Octobre","Novembre","D&#233;cembre"],
		"labels":["Show previous month","Show next month","Events calendar of"]
	};
	this.lngStr.de = {
		"dayName":["Dienstag","Mittwoch","Donnerstag","Freitag","Samstag","Sonntag","Montag"],
		"monthName":["Januar","Februar","M&#228;rz","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],
		"labels":["Show previous month","Show next month","Events calendar of"]
	};
	
	this.firstDay=0;
	/* METHODS (checkDate already declared) */
	this.setFirstDay=function(q){
		var i,r=[],a=this.lngStr[this.lang].dayName;
		if(q>a.length||q<=0||isNaN(q)){
			this.lngStr.current.dayName=a;
		}else{
			for(i=q;i<a.length;i++)r[r.length]=a[i];
			for(i=0;i<q;i++)r[r.length]=a[i];
			this.lngStr.current.dayName=r;
		}
	}

	this.setLang = function(q){
		var i,found=false;

		if(q!=this.lang){
			switch(q){
				case"us":
					this.firstDay=0;found=true;
				break;
				case"en":case"it":case"es":case"fr":case"de":
					this.firstDay=1;found=true;
				break;
			}
			if(found){
				this.lang = q;
				this.lngStr.current={"dayName":[],"monthName":[],"labels":[]};
				for(i=0;i<this.lngStr[q].dayName.length;i++)this.lngStr.current.dayName[i]=this.lngStr[q].dayName[i];
				for(i=0;i<this.lngStr[q].monthName.length;i++)this.lngStr.current.monthName[i]=this.lngStr[q].dayName[i];
				for(i=0;i<this.lngStr[q].labels.length;i++)this.lngStr.current.labels[i]=this.lngStr[q].labels[i];
				this.setFirstDay(this.firstDay);
				this.F = this.lngStr[this.lang].monthName[this.now.getMonth()];
				this.M = this.F.substring(0,3);
				this.l = this.lngStr[this.lang].dayName[this.now.getDay()]
				this.D = this.l.substring(0,3);
			}
		}
	}

	// DATE FORMAT (PHP style: http://it2.php.net/manual/it/function.date.php )
	
	this.now.setFullYear(this.Y);
	this.now.setMonth(this.n-1);
	this.now.setDate((this.exists)?this.j:0);
	
	this.setLang(startingLang); // setting lang redefines F M l and D
	
	this.isBisestile=function(Y){
		return ((Y%4==0&&Y%100!=0)||Y%400==0)?1:0;
	}
	this.daysInMonth=function(n,Y){
		var isBisestile = ((Y%4==0&&Y%100!=0)||Y%400==0)?1:0;
		if(n==4||n==6||n==9||n==11)return 30;
		else if(n==1||n==3||n==5||n==7||n==8||n==10||n==12)return 31;
		else if(n==2)return (isBisestile)?29:28;
	}
	this.getWeek = function() {
		var onejan = new Date(this.Y,0,1);
		return Math.ceil((((this.now - onejan) / 86400000) + onejan.getDay())/7);
	}
	
	// Day
	this.d = this.zeroFill(this.j);
//	this.D : already set
//	this.j : already set
//	this.l : already set
	this.N = (this.now.getDay()>0)?this.now.getDay():7;
	this.S = this.suffix[this.j-1];
	this.w = this.now.getDay();
	this.z = 0;for(var i=0;i<this.n-1;i++)this.z+=this.daysInMonth(i+1);this.z += this.j - 1;
	
	// Week
	this.W = this.getWeek();
	
	// Month
//	this.F : already set
	this.m = this.zeroFill(this.n);
//	this.M : already set
//	this.n : already set
	this.t = this.daysInMonth(this.n,this.Y);
	
	// Year
	this.L = this.isBisestile(this.Y);
	this.o = this.Y
	if(this.W==1){
		var lastDayPrevYear=new Date(this.Y-1,11,31).getDay();
		if(lastDayPrevYear!=0)this.o = this.Y - 1;
	}else if(this.n==12&&this.j>31-7){
		var firstDayNextYear=new Date(this.Y+1,0,1).getDay();
		if(firstDayNextYear!=1)this.o = this.Y + 1;
	};
//	this.Y : already set
	this.y = String(this.Y).substr(2);
	
	// Time
	this.a = (this.G>0&&this.G<13)?"am":"pm";
	this.A = this.a.toUpperCase();
	this.B=Math.floor(((this.G-1) * 3600 + (this.i *60) + this.s * 1) * 1000 / 86400);
	if(this.B>=1000)this.B-=1000;
	else if(this.B<0)this.B+=1000;
	this.g = (this.G==0)?12:((this.G>12)?(this.G-12):this.G);
//	this.G : already set
	this.h = this.zeroFill(this.g);
	this.H = this.zeroFill(this.G);
//	this.i : already set
//	this.s : already set
//	this.u : already set
	
	// Timezone	
	// Ora legale
	var date1 = new Date(this.Y, 0, 1, 0, 0, 0, 0);
	var date2 = new Date(this.Y, 6, 1, 0, 0, 0, 0);
	var temp = date1.toGMTString();
	var date3 = new Date(temp.substring(0, temp.lastIndexOf(" ")-1));
	var temp = date2.toGMTString();
	var date4 = new Date(temp.substring(0, temp.lastIndexOf(" ")-1));
	var hoursDiffStdTime = (date1 - date3) / (1000 * 60 * 60);
	var hoursDiffDaylightTime = (date2 - date4) / (1000 * 60 * 60);
	var TimeZone = hoursDiffStdTime; // es: GMT+100 = 1 ; es ora legale: GMT+200 = 1;
	var isDstObserved = (hoursDiffDaylightTime != hoursDiffStdTime);
	
	this.e = "*UNSUPPORTED*";
	this.O = String(this.now);
	this.O = this.O.substr(this.O.indexOf("+"),5);
	this.I = ( hoursDiffStdTime != parseInt((this.O*1)/100) && isDstObserved ) ? 1 : 0;
	this.P = this.O.substr(0,3)+":"+this.O.substr(3,2);
	this.T = "*UNSUPPORTED*";
	this.Z = this.O/100*60*60;
	
	// Full Date/Time
	this.c = this.Y+"-"+this.m+"-"+this.d+"T"+this.H+":"+this.i+":"+this.s+this.P;
	this.r = this.lngStr.en.dayName[this.w].substr(0,3)+", "+this.d+" "+this.lngStr.en.monthName[this.n-1].substr(0,3)+" "+this.Y+" "+this.H+":"+this.i+":"+this.s+" "+this.O;
	this.U = Math.floor(this.now.getTime()/1000);
	
	this.format=function(formato){
		var s="";
		for(var i=0;i<formato.length;i++){
			var letter=formato.charAt(i);
			s+=(typeof this[letter]!="undefined")?this[letter]:letter;
		}
		return s;
	}	
}

Object.extend(jDate,{
	toDate:function(q){
		return this.now;
	},
	addDate:function(q){
		q=arguments[0]||q;
		var d=this.now.setDate(this.now.getDate()+q);
		return new jDate(d.getFullYear(),d.getMonth()+1,d.getDate(),d.getHours(),d.getMinutes(),d.getSeconds(),d.getMilliseconds());
	},
	addWeek:function(q){
		q=arguments[0]||q;
		var d=this.now.setDate(this.now.getDate()+7*q);
		return new jDate(d.getFullYear(),d.getMonth()+1,d.getDate(),d.getHours(),d.getMinutes(),d.getSeconds(),d.getMilliseconds());
	},
	addMonth:function(q){
		q=arguments[0]||q;
		var d=this.now.setMonth(this.now.getMonth()+q);
		return new jDate(d.getFullYear(),d.getMonth()+1,d.getDate(),d.getHours(),d.getMinutes(),d.getSeconds(),d.getMilliseconds());
	},
	addYear:function(q){
		q=arguments[0]||q;
		var d=this.now.setYear(this.now.getFullYear()+q);
		return new jDate(d.getFullYear(),d.getMonth()+1,d.getDate(),d.getHours(),d.getMinutes(),d.getSeconds(),d.getMilliseconds());
	}
});

Object.extend(Date.prototype,{
	tojDate:function(){
		return new jDate(this.getFullYear(),this.getMonth()+1,this.getDate());
	}
});

Object.extend(String.prototype, {
	timestamp2jDate:function(){
		var q=this.replace(/\D/g,"");
		return new jDate(q.substr(0,4),q.substr(4,2),q.substr(6,2),q.substr(8,2),q.substr(10,2),q.substr(12,2));
	}
});