Locale = function(){};
Locale.prototype.dayName = new Array(
	"Понедельник",
	"Вторник",
	"Среда",
	"Четверг",
	"Пятница",
	"Суббота",
	"Воскресенье");
Locale.prototype.shortDayName = new Array(
	"Вт",
	"Ср",
	"Чт",
	"Пт",
	"Сб",
	"Вс",
	"Пн");
Locale.prototype.monthNameList = new Array(
	"Январь",
	"Февраль",
	"Март",
	"Апрель",
	"Май",
	"Июнь",
	"Июль",
	"Август",
	"Сентябрь",
	"Октябрь",
	"Ноябрь",
	"Декабрь");
Locale.prototype.monthName = new Array(
	"Января",
	"Февраля",
	"Марта",
	"Апреля",
	"Мая",
	"Июня",
	"Июля",
	"Августа",
	"Сентября",
	"Октября",
	"Ноября",
	"Декабря");
Locale.prototype.weekend = new Array(4, 5);

Locale.prototype.getDay = function(weekDay)
{
	return weekDay % 7;
};

Locale.prototype.getShortDayName = function(weekDay)
{
	return this.shortDayName[this.getDay(weekDay)];
};

Locale.prototype.isWeekend = function(weekDay)
{
	if (this.weekend == null) return false;

	for (var i = 0; i < this.weekend.length; i++)
	{
		if (this.weekend[i] == weekDay) 
		{
			return true;
		}
	}
	return false;
};

Date.prototype.monthDayCount = new Array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
Date.prototype.leapMonthDayCount = new Array(31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
Date.chars = new Array('D', 'W', 'd', 'M', 'N', 'Y', 'H', 'h', 'm', 's', 'a', 'A');
Date.prototype.locale = new Locale();

Date.prototype.firstWeekDayOfMonth = function()
{
	var firstForMonth = new Date(this);
	firstForMonth.setDate(1);

	return firstForMonth.getDay();
};

Date.prototype.isWeekend = function()
{
	return this.locale.isWeekend(this.getDay());
};

Date.prototype.compareTo = function(dateTo)
{
	return (this > dateTo) ? 1 : (this < dateTo) ? -1 : 0;
};

Date.prototype.equalsDate = function(date)
{
	return (this.getFullYear() == date.getFullYear() &&
			this.getMonth() == date.getMonth() &&
			this.getDate() == date.getDate());
};

Date.prototype.getDayOfYear = function()
{
	var month = this.getMonth();
	var dayCount = 0;
	var monthDayCount = (this.isLeap()) ? this.leapMonthDayCount : this.monthDayCount;
	for (var i = 0; i < month; i++)
	{
		dayCount += monthDayCount[i];
	}

	return dayCount + this.getDate();
};

Date.prototype.getWeek = function()
{
	var days = this.getDayOfYear() - (this.locale.getDay(this.getDay()) + 1);
	if (days < 0) days = 0;
	var week = Math.floor(days / 7) + ((days % 7 > 0) ? 1 : 0) + 1;
	return week;
};

Date.prototype.isLeap = function()
{
	return (this.getFullYear() % 4 == 0);
};

Date.prototype.setOnlyDate = function(date)
{
	this.setFullYear(date.getFullYear());
	this.setMonth(date.getMonth());
	this.setDate(date.getDate());
};

Date.prototype.setOnlyTime = function(time)
{
	this.setHours(time.getHours());
	this.setMinutes(time.getMinutes());
};

// % - escape character
// D - week day
// W - week day in words
// d - day
// M - month
// N - month in words
// Y - year
// h - hour
// m - minute
// s - second
// a - am / pm
// A - AM / PM

// %DDD %dd.%MM.%yyyy %hh:%mm %AA
// Mon 01.02.2000 12:45 PM - correct

Date.dateToDBString = function(value)
{
	if (typeof(value) == "undefined" || value == null) return null;
	return value.dateToString("%YYYY%MM%dd");
}

Date.dateToDefaultString = function(value)
{
	if (typeof(value) == "undefined" || value == null) return "";
	return value.dateToDefaultString();
}

Date.prototype.dateToDefaultString = function()
{
	return this.dateToString("%dd.%MM.%YYYY");
}

Date.prototype.dateToString = function(format)
{
	var thisObject = this;
	if (!format && !this.locale) return this.toString();

	var isEscape = function(char)
	{
		for (var i = 0; i < Date.chars.length; i++)
		{
			if (Date.chars[i] == char) return true;
		}
		return false;
	};
	var parseToken = function(token, count)
	{
		var addZero = function(value)
		{
			if (count == 2)
			{
				return ((value.length < 2) ? "0" : "") + value;
			}
			else return value;
		};
		var truncate = function(value)
		{
			if (value.length >= count && count >= 1)
			{
				return value.substr(0, count);
			}
			else return value;
		};
		var truncateRight = function(value)
		{
			if (value.length >= count && count >= 1)
			{
				return value.substr(value.length - count, count);
			}
			else return value;
		};

		var value;
		switch (token)
		{
			case 'D':
				return "" + (thisObject.getDay() + 1);
			case 'W':
				return truncate("" + this.locale.dayName[thisObject.getDay()]);
			case 'd':
				return addZero("" + thisObject.getDate());
			case 'M':
				return addZero("" + (thisObject.getMonth() + 1));
			case 'N':
				return truncate("" + this.locale.monthName[thisObject.getMonth()]);
			case 'Y':
				return truncateRight("" + thisObject.getFullYear());
			case 'H':
				value = thisObject.getHours() % 12;
				return addZero("" + ((value == 0) ? 12 : value));
			case 'h':
				return addZero("" + thisObject.getHours());
			case 'm':
				return addZero("" + thisObject.getMinutes());
			case 's':
				return addZero("" + thisObject.getSeconds());
			case 'a':
				value = (thisObject.getHours() > 11) ? "pm" : "am";
				return (count == 2) ? value :
					   ((count == 1) ? value.charAt(0) : "");
			case 'A':
				value = (thisObject.getHours() > 11) ? "PM" : "AM";
				return (count == 2) ? value :
					   ((count == 1) ? value.charAt(0) : "");
		}
	};

	var str = "";
	var escape = false;
	var token = '';
	var count = 0;
	for (var i = 0; i < format.length; i++)
	{
		var currentChar = format.charAt(i);
		if (currentChar == '%')
		{
			if (escape)
			{
				if (token == '')
				{
					str += '%';
					escape = false;
					token = '';
					count = 0;
					continue;
				}
				else
				{
					str += parseToken(token, count);
				}
			}
			escape = true;
			token = '';
			count = 0;
		}
		else if (escape)
		{
			if (isEscape(currentChar))
			{
				if (token == '')
				{
					token = currentChar;
					count++;
				}
				else if (token != currentChar)
				{
					escape = false;
					str += parseToken(token, count) + currentChar;
					token = '';
					count = 0;
				}
				else count++;
			}
			else
			{
				if (token == '')
				{
					return null;
				}
				else
				{
					escape = false;
					str += parseToken(token, count) + currentChar;
					token = '';
					count = 0;
				}
			}
		}
		else
		{
			str += currentChar;
		}
	}

	if (escape && token != '')
	{
		str += parseToken(token, count);
	}

	return str;
};

Date.TryParseString = function(value, formatList)
{
	var result;
	if (formatList instanceof Array)
	{
		for (var i = 0; i < formatList.length; i++)
		{
			result = Date.ParseString(value, formatList[i], this.locale);
			if (result != null) return result;
		}
	}
	return null;
};

Date.ParseString = function(value, format)
{
	var result = new Date(2000, 0, 1, 0, 0, 0);
	if (!(value != null && format != null && result.locale != null &&
		typeof(value) != "undefined" && typeof(format) != "undefined" && typeof(result.locale) != "undefined"))
	{
		return null;
	}

	var fields = {
		date : -1,
		month : -1,
		year : -1,
		hourOfDay : -1,
		minute : -1,
		second : -1,
		hour : -1,
		am_pm : -1};

	var isEscape = function(char)
	{
		for (var i = 0; i < Date.chars.length; i++)
		{
			if (Date.chars[i] == char) return true;
		}
		return false;
	};
	var parseValue = function(token, count, value, pos)
	{
		if (pos >= value.length) return -1;

		var parseIntValue = function(value)
		{
			try
			{
				var valueToParse = "";
				var force = true;
				for (var i = 0; i < value.length; i++)
				{
					if (!(value.charAt(i) >= 0 && value.charAt(i) <= 9))
						return -1;
					if (force && value.charAt(i) == 0)
						continue;
					force = false;
					valueToParse += value.charAt(i);
				}
				var result = (valueToParse == "") ? 0 : parseInt(valueToParse);
				if (isNaN(result)) return -1;
				return result;
			}
			catch(ex)
			{
				return -1;
			}
		};

		var parseField = function(value, pos, count, fields, field, max, min)
		{
			if (value.length >= pos + count)
			{
				var date = parseIntValue(value.substr(pos, count));
				if (date != -1 && date <= max && date >= min)
				{
					switch (field)
					{
						case 1: fields.date = date; break;
						case 2: fields.month = date; break;
						case 3: 
							if (date < 100) fields.year = 2000 + date;
							else fields.year = date;
							break;
						case 4: fields.hourOfDay = date; break;
						case 5: fields.minute = date; break;
						case 6: fields.second = date; break;
						case 7: fields.hour = date; break;
						case 8: fields.am_pm = date; break;
					}
					return count;
				}
			}
			return -1;
		};

		var startsWith = function(first, second)
		{
			if (!(first && second && first.length >= second.length)) return false;

			for (var i = 0; i < first.length && i < second.length; i++)
			{
				if (first.charAt(i) != second.charAt(i)) return false;
			}

			return true;
		};

		var result;
		switch (token)
		{
			case 'D':
				return 1;
			case 'W':
				var dayName = value.substr(pos);
				var next;
				for (var i = 0; i < result.locale.dayName.length; i++)
				{
					next = result.locale.dayName[i];
					if ((count != 1) && (count > next.length)) next = next.substr(0, count);
					if (startsWith(dayName, next))
						return next.length;
				}
				break;
			case 'd':
				result = parseField(value, pos, 2, fields, 1, 31, 1);
				if (result == -1 && count == 1) result = parseField(value, pos, 1, fields, 1, 31, 1);
				return result;
			case 'M':
				result = parseField(value, pos, 2, fields, 2, 12, 1);
				if (result == -1 && count == 1) result = parseField(value, pos, 1, fields, 2, 12, 1);
				return result;
			case 'N':
				var monthName = value.substr(pos);
				var nextMonth;
				for (var i = 0; i < result.locale.monthName.length; i++)
				{
					nextMonth = result.locale.monthName[i];
					if ((count != 1) && (count > nextMonth.length)) nextMonth = nextMonth.substr(0, count);
					if (startsWith(monthName, nextMonth))
					{
						fields.month = i;
						return nextMonth.length;
					}
				}
				break;
			case 'Y':
				result = parseField(value, pos, count, fields, 3, 3000, 0);
				return result;
			case 'H':
				result = parseField(value, pos, 2, fields, 7, 12, 1);
				if (result == -1 && count == 1) parseField(value, pos, 1, fields, 7, 12, 1);
				return result;
			case 'h':
				result = parseField(value, pos, 2, fields, 4, 23, 0);
				if (result == -1 && count == 1) parseField(value, pos, 1, fields, 4, 23, 0);
				return result;
			case 'm':
				result = parseField(value, pos, 2, fields, 5, 59, 0);
				if (result == -1 && count == 1) parseField(value, pos, 1, fields, 5, 59, 0);
				return result;
			case 's':
				result = parseField(value, pos, 2, fields, 6, 59, 0);
				if (result == -1 && count == 1) parseField(value, pos, 1, fields, 6, 59, 0);
				return result;
			case 'a':
			case 'A':
				if (fields.hourOfDay == -1 && value.length >= pos + count)
				{
					var ampm = value.substr(pos, count);
					if (startsWith(ampm.toLowerCase(), "a"))
					{
						fields.am_pm = 1;
						return count;
					}
					else if (startsWith(ampm.toLowerCase(), "p"))
					{
						fields.am_pm = 2;
						return count;
					}
				}
				break;
		}

		return -1;
	};

	var pos = 0;
	var escape = false;
	var token = '';
	var count = 0;
	for (var i = 0; i < format.length; i++)
	{
		var currentChar = format.charAt(i);
		if (currentChar == '%')
		{
			if (escape)
			{
				if (token == '')
				{
					pos++;
					escape = false;
					token = '';
					count = 0;
					continue;
				}
				else
				{
					var p = parseValue(token, count, value, pos);
					if (p == -1)
					{
						return null;
					}
					pos += p;
				}
			}
			escape = true;
			token = '';
			count = 0;
		}
		else if (escape)
		{
			if (isEscape(currentChar))
			{
				if (token == '')
				{
					token = currentChar;
					count++;
				}
				else if (token != currentChar)
				{
					escape = false;
					var p = parseValue(token, count, value, pos);
					if (p == -1)
					{
						return null;
					}
					pos += p + 1;
					token = '';
					count = 0;
				}
				else count++;
			}
			else
			{
				if (token == '')
				{
					return null;
				}
				else
				{
					escape = false;
					var p = parseValue(token, count, value, pos);
					if (p == -1)
					{
						return null;
					}
					pos += p + 1;
					token = '';
					count = 0;
				}
			}
		}
		else
		{
			pos++;
		}
	}

	if (escape && token != '')
	{
		var p = parseValue(token, count, value, pos);
		if (p == -1)
		{
			return null;
		}
	}
	
	if (fields.year != -1)
		result.setFullYear(fields.year);
	if (fields.month != -1)
		result.setMonth(fields.month - 1);
	if (fields.date != -1)
		result.setDate(fields.date);
	if (fields.hour != -1)
	{
		if (fields.hour == 12)
			result.setHours(0);
		else
			result.setHours(fields.hour);
	}
	if (fields.hourOfDay != -1)
		result.setHours(fields.hourOfDay);
	if (fields.am_pm != -1)
	{
		if (fields.am_pm == 1 && result.getHours() > 11)
			result.setHours(result.getHours() - 12);
		else if (fields.am_pm == 2 && result.getHours() < 12)
		{
			result.setHours(result.getHours() + 12);
		}
	}
	if (fields.minute != -1)
		result.setMinutes(fields.minute);
	if (fields.second != -1)
		result.setSeconds(fields.second);

	return result;
};

Date.prototype.compare = function(toCompare, withTime)
{
	if (this.getFullYear() > toCompare.getFullYear()) return 1;
	else if (this.getFullYear() < toCompare.getFullYear()) return -1;

	if (this.getMonth() > toCompare.getMonth()) return 1;
	else if (this.getMonth() < toCompare.getMonth()) return -1;
	
	if (this.getDate() > toCompare.getDate()) return 1;
	else if (this.getDate() < toCompare.getDate()) return -1;

	if (withTime)
	{
		if (this.getHours() > toCompare.getHours()) return 1;
		else if (this.getHours() < toCompare.getHours()) return -1;

		if (this.getMinutes() > toCompare.getMinutes()) return 1;
		else if (this.getMinutes() < toCompare.getMinutes()) return -1;

		if (this.getSeconds() > toCompare.getSeconds()) return 1;
		else if (this.getSeconds() < toCompare.getSeconds()) return -1;

		if (this.getMilliseconds() > toCompare.getMilliseconds()) return 1;
		else if (this.getMilliseconds() < toCompare.getMilliseconds()) return -1;
	}
	
	return 0;
};
