/*
# File: /javascriptlibrary/date.js
#
# Purpose: defines window.datejso with methods:
# date(dateString, aDate) # example: date('d.m.Y - H:i:s');
# dateFromPack('YYYYMMDD') # returns a date object using format YYYYMMDD
# dateFromMySqlPack('YYYY-MM-DD HH:MM:SS') # returns a date object using format YYYY-MM-DD HH:MM:SS
# twelveHour(aDate)
# getMeridiem(hour)
# leapYear(year)
# getNumberOfDays(date)
# getDaysInMonth(date)
# getDateSuffix(dayOfMonth)
# daylightSavingsTime(date)
# getTimezoneOffset(date)
# getTimezone(date)
# getGTMDifference(date)
# getWeekNumber(date)
# getRFCDate(aDate)
# getSwatchInternetTime(aDate)
# insertLeadingZero(number)
# addSlashes(string)
# getHours(aDate)
#
# Defined Date prototypes:
#  getFullYear()      :== fixes bug in netscape, getFullYear
#  get0Month()        :== returns the numeric month, in 2 digits.
#  get0Date()         :== returns the date, in 2 digits.
#  get0Hours()        :== returns the hours, in 2 digits.
#  get0Minutes()      :== returns the minutes, in 2 digits.
#  getShortMonthName():== returns Jan, or Feb, or ...
#
# History:
# 15-Aug-06 fhk; Init
# 22-Dec-06 fhk; updated getDaysInMonth to consider leap year. :-)
# 14-Feb-07 fhk; added getShortMonthName function
# 18-Feb-07 fhk; added dateFromPack and dateFromMySqlPack
#--------------------------------------------------
*/

Date.prototype.getFullYear = GetFullYear;
Date.prototype.get0Month = Get0Month;
Date.prototype.get0Date = Get0Date;
Date.prototype.get0Hours = Get0Hours;
Date.prototype.get0Minutes = Get0Minutes;
Date.prototype.getShortMonthName = getShortMonthName;

//----------------------------------------------------------------------------
// GetFullYear(): Returns full year value fixes a Netscape 2 and 3 bug
//----------------------------------------------------------------------------
function GetFullYear() {
        var yr;
        yr = this.getYear();
        if (yr < 1000) yr +=1900;
        return yr;
}

//----------------------------------------------------------------------------
// Get0Month(): Returns numeric month, in 2 digits.
//----------------------------------------------------------------------------
function Get0Month() {
   return window.datejso.insertLeadingZero(this.getMonth()+1);
}

//----------------------------------------------------------------------------
// Get0Date(): Returns numeric date, in 2 digits.
//----------------------------------------------------------------------------
function Get0Date() {
   return window.datejso.insertLeadingZero(this.getDate());
}

function Get0Hours() {
 return window.datejso.insertLeadingZero(this.getHours());
}

function Get0Minutes() {
 return window.datejso.insertLeadingZero(this.getMinutes());
}

function getShortMonthName() {
 var m_names = new Array("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
 return m_names[this.getMonth()];
}

window.datejso = {

  /**
   * The month- and weekdayname is to be stored
   * in a dtd language dependent file.
   */
  month: new Array("January",
    "February",
    "March",
    "April",
    "May",
    "June",
    "July",
    "August",
    "September",
    "October",
    "November",
    "December"
  /*
	
  */
  
  ),

  weekday: new Array("Sunday",
    "Monday",
    "Tuesday",
    "Wedensday",
    "Thursday",
    "Friday",
    "Saturday"
   /*
   
   */
   
  ),


  /**
   * Returns a date object using packed date of YYYYMMDD.
   */
  dateFromPack: function(str) {
    var d = new Date();
    d.setHours(0);
    d.setMinutes(0);
    d.setSeconds(0);
    d.setMilliseconds(0);
    var a = str.match(/([0-9][0-9][0-9][0-9])([0-1][0-9])([0-3][0-9])/);
    d.setFullYear(a[1]);
    d.setMonth(a[2]-1);
    d.setDate(a[3]);
    return d;
  },

  /**
   * Returns a date object using packed date of YYYY-MM-DD HH:MM:SS
   */
  dateFromMySqlPack: function(str) {
    var d = new Date();
    d.setMilliseconds(0);
    var a = str.match(/([0-9][0-9][0-9][0-9])-([0-1][0-9])-([0-3][0-9]) ([0-9][0-9]):([0-9][0-9]):([0-9][0-9])/);
    d.setFullYear(a[1]);
    d.setMonth(a[2]-1);
    d.setDate(a[3]);
    d.setHours(a[4]);
    d.setMinutes(a[5]);
    d.setSeconds(a[6]);
    return d;
  },

  /**
   * Returns a string formatted according to the given
   * format string using the given date object
   */
  date: function(dateString, aDate) {
    if (arguments.length<1) dateString = "d.m.Y - H:i:s";

    var date = aDate; if (arguments.length<2) date = (new Date());

    var datePatterns = new Array(
      // Lowercase Ante meridiem and Post meridiem
      new Array(new RegExp(/([^\\])a|^a()/g), this.addSlashes(this.getMeridiem(this.getHours(aDate)))),

      //  Uppercase Ante meridiem and Post meridiem
      new Array(new RegExp(/([^\\])A|^A()/g),
                this.addSlashes(this.getMeridiem(this.getHours(aDate)).toUpperCase())),

      // Swatch internet time
      new Array(new RegExp(/([^\\])B|^B()/g), this.getSwatchInternetTime(date)),

      // Day of the month, 2 digits with leading zeros
      new Array(new RegExp(/([^\\])d|^d()/g), this.insertLeadingZero(date.getDate())),

      // A textual representation of a week, three letters
      new Array(new RegExp(/([^\\])D|^D()/g), this.addSlashes(this.weekday[date.getDay()].substr(0,3))),

      // A full textual representation of a month
      new Array(new RegExp(/([^\\])F|^F()/g), this.addSlashes(this.month[date.getMonth()])),

      // 12-hour format of an hour without leading zeros
      new Array(new RegExp(/([^\\])g|^g()/g), this.twelveHour(aDate)),

      // 24-hour format of an hour without leading zeros
      new Array(new RegExp(/([^\\])G|^G()/g), this.getHours(aDate)),

      // 12-hour format of an hour with leading zeros
      new Array(new RegExp(/([^\\])h|^h()/g), this.insertLeadingZero(this.twelveHour(aDate))),

      // 24-hour format of an hour with leading zeros
      new Array(new RegExp(/([^\\])H|^H()/g), this.insertLeadingZero(this.getHours(aDate))),

      // Minutes with leading zeros
      new Array(new RegExp(/([^\\])i|^i()/g), this.insertLeadingZero(date.getMinutes())),

      // Whether or not the date is in daylights savings time
      new Array(new RegExp(/([^\\])I|^I()/g), ""),

      // Day of the month without leading zeros
      new Array(new RegExp(/([^\\])j|^j()/g), date.getDate()),

      // A full textual representation of the day of the week
      new Array(new RegExp(/([^\\])l|^l()/g), this.addSlashes(this.weekday[date.getDay()])),

      // is a leap year?
      new Array(new RegExp(/([^\\])L|^L()/g), this.leapYear(date.getFullYear())),

      // Numeric representation of a month, with leading zeros
      new Array(new RegExp(/([^\\])m|^m()/g), this.insertLeadingZero(date.getMonth()+1)),

      // A short textual representation of a month, three letters
      new Array(new RegExp(/([^\\])M|^M()/g), this.addSlashes(this.month[date.getMonth()].substr(0,3))),

      // Numeric representation of a month, without leading zeros
      new Array(new RegExp(/([^\\])n|^n()/g), date.getMonth()+1),

      // Difference to Greenwich time (GMT) in hours
      new Array(new RegExp(/([^\\])O|^O()/g), this.getGTMDifference(date)),

      // RFC 822 formatted date
      new Array(new RegExp(/([^\\])r|^r()/g), this.getRFCDate(date)),

      // Seconds, with leading zeros
      new Array(new RegExp(/([^\\])s|^s()/g), this.insertLeadingZero(date.getSeconds())),

      // English ordinal suffix for the day of the month, 2 characters
      new Array(new RegExp(/([^\\])S|^S()/g), this.addSlashes(this.getDateSuffix(date.getDate()))),

      // Number of days in the given month
      new Array(new RegExp(/([^\\])t|^t()/g), this.getDaysInMonth(date)),

      // Timezone setting of this machine
      new Array(new RegExp(/([^\\])T|^T()/g), ""),

      // Miliseconds since the Unix Epoch (January 1 1970 00:00:00 GMT)
      new Array(new RegExp(/([^\\])U|^U()/g), date.getTime()),

      // Numeric representation of the day of the week
      new Array(new RegExp(/([^\\])w|^w()/g), date.getDay()),

      // ISO-8601 week number of year, weeks starting on Monday
      new Array(new RegExp(/([^\\])W|^W()/g), this.getWeekNumber(date)),

      // A two digit representation of a year
      new Array(new RegExp(/([^\\])y|^y()/g), (""+date.getFullYear()).substr(2,2)),

      // A full numeric representation of a year, 4 digits
      new Array(new RegExp(/([^\\])Y|^Y()/g), date.getFullYear()),

      // The day of the year
      new Array(new RegExp(/([^\\])z|^z()/g), this.getNumberOfDays(date)),

      // Timezone offset in seconds. The offset for timezones west of
      // UTC is always negative, and for those east of UTC is always positive.
      new Array(new RegExp(/([^\\])Z|^Z()/g), this.getTimezoneOffset(date)),

      // Replace all backslashes followed by a letter with the letter
      new Array(new RegExp(/\\([A-Za-z])/g), "")
    );

    var datePattern;
    while ((datePattern = datePatterns.shift()))
      dateString = dateString.replace(datePattern[0], "$1" + datePattern[1]);

    return dateString;
  },


  /**
   * Returns the hour in a 12-hour format
   */
  twelveHour: function(aDate) {
    var hour;
    if (aDate)
      hour = aDate.getHours();
    else
      hour = (new Date()).getHours();
    if ( this.getMeridiem(hour)=="pm" )
      hour -= 12;
    if ( hour==0 )
      hour = 12;

    return hour;
  },


  /**
   * Returns lowercase am or pm based on the given 24-hour
   */
  getMeridiem: function(hour) {
    if ( hour>11 )
      return "pm";
    else
      return "am";
  },


  /**
   * Return true if year is a leap year otherwise false
   */
  leapYear: function(year) {
    //The Date object automatic corrigates if values is out of limit
    //29 isn't out of limit if it is leap year!
    var date = new Date(year, 1, 29);
    if ( date.getMonth()==1 )
      return 1;
    else
      return 0;
  },


  /**
   * Returns the number of days from new year to current date (incl current day)
   */
  getNumberOfDays: function(date) {
    var currentDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
    var startOfYear = new Date(date.getFullYear(), 0, 1);

    return ( currentDate.getTime()-startOfYear.getTime() )/86400000 +1;
  },


  /**
   * Returns the number of days in the current month of the date object
   */
  getDaysInMonth: function(date) {
    if ( this.leapYear(date.getFullYear()) && date.getMonth() == 1 ) return(29);

    var currentDate = new Date(date.getFullYear(), date.getMonth()+1, 0);
    var startOfYear = new Date(date.getFullYear(), date.getMonth(), 1);

    return ( currentDate.getTime()-startOfYear.getTime() )/86400000 +1;
  },


  /**
   * Returns the english ordinal suffix for the day of the month, 2 characters
   */
  getDateSuffix: function(dayOfMonth) {
    var rv = null;
    if ( dayOfMonth>3 && dayOfMonth<21 ) {
      rv = "th";
    } else {
      switch( dayOfMonth%10 ) {
        case 1:
          rv = "st";
          break;
        case 2:
          rv = "nd";
          break;
        case 3:
          rv = "rd";
          break;
        default:
          rv = "th";
          break;
      }
    }
    return rv;
  },


  daylightSavingsTime: function(date) { return 0; },

  /**
   * Timezone offset in seconds
   * -43200 through 43200
   */
  getTimezoneOffset: function(date) { return date.getTimezoneOffset()*(-60); },


  /**
   * This function isn't implemented yet, but here is an idea of how it might look
   * Before thins function can be made the function daylightSavingsTime() have to be written
   * http://www.timeanddate.com/time/abbreviations.html
   * http://setiathome.ssl.berkeley.edu/utc.html
   */
  getTimezone: function(date) {
    if ( this.daylightSavingsTime(date)==1 ) {
      switch(this.getTimezoneOffset(date)/3600) {
        case -11:
          return "";
          break;
//..........
        case 0:
          return "";
          break;
        case 1:
          return "WEST";
          break;
        case 2:
          return "CEST";
          break;
        case 3:
          return "EEST";
          break;
//............
        case 12:
          return "";
          break;
        default:
          return "";
          break;
      }
    }
    else {
      switch(this.getTimezoneOffset(date)/3600) {
        case -11:
          return "";
          break;
//..........
        case 0:
          return "";
          break;
        case 1:
          return "WET";
          break;
        case 2:
          return "CET";
          break;
        case 3:
          return "EET";
          break;
//............
        case 12:
          return "";
          break;
        default:
          return "";
          break;
      }
    }
    return "";
  },


  /**
   * Difference to Greenwich time (GMT) in hours
   */
  getGTMDifference: function(date) {
    var offset = this.getTimezoneOffset(date)/3600;
    if (offset>0) //adding leading zeros and gives the offset a positive prefix
      return "+" + this.insertLeadingZero(offset)      + "00";
    else  //if negative, make the offset positive before adding leading zeros and give the number a negative prefix
      return "-" + this.insertLeadingZero(offset*(-1)) + "00";
  },


  /**
   * In [ISO8601], the week number is defined by:
   * - weeks start on a monday
   * - week 1 of a given year is the one that includes the first Thursday of that year.
   *   (or, equivalently, week 1 is the week that includes 4 January.)
   * Weeknumbers can be a number between 1 - 53 (53 is not common)
   */
  getWeekNumber: function(date) {
    var weekday = date.getDay();
    if (weekday==0) weekday = 7;

    //currentDate is on the fist monday in the same week of date (could be the same date)
    var currentDate = new Date(date.getFullYear(), date.getMonth(), date.getDate()-(weekday-1));
    var startOfYear = new Date(currentDate.getFullYear(), 0, 1);

    var firstWeekday = startOfYear.getDay();

    var extraDays;
    if ( 5>firstWeekday )
      extraDays = firstWeekday-1;
    else
      extraDays = firstWeekday-8;

    //var numberOfDays = ( ( currentDate.getTime()-startOfYear.getTime() )/86400000 ) + extraDays;
    //var weekNumber = numberOfDays/7 +1;

    return ( ( ( currentDate.getTime()-startOfYear.getTime() )/86400000 ) + extraDays )/7 +1;
  },


  /**
   * RFC 822 formatted
   * http://www.freesoft.org/CIE/RFC/822/39.htm
   */
  getRFCDate: function(aDate) {
    var dayRFC  = this.addSlashes(this.weekday[aDate.getDay()].substr(0,3));
    var dateRFC = aDate.getDate() + " " + this.addSlashes(this.month[aDate.getMonth()].substr(0,3))
                + " " + (""+aDate.getFullYear()).substr(2,2);
    var timeRFC = this.insertLeadingZero(aDate.getHours()) + ":"
                + this.insertLeadingZero(aDate.getMinutes()) + ":"
                + this.insertLeadingZero(aDate.getSeconds()) + " " + this.getGTMDifference(aDate);
    return (dayRFC + ", " + dateRFC + " " + timeRFC).replace(/\\/g,'');
  },


  /**
   * 1min 26.4sek = 1 Swatch beat
   * (60sek + 26.4sek)*1000milliseconds/second = 86400miliseconds = 1 Swatch beat
   */
  getSwatchInternetTime: function(aDate) {
    // A day in Internet Time begins at midnight BMT (@000 Swatch Beats)
    // (Central European Wintertime) (+0100 from GTM)
    // This line makes the Date object corrigate if the hour is 23 then it
    // will be set to 0 and the date will be incremented
    aDate.setUTCHours(aDate.getUTCHours()+1);

    var milliseconds = Date.UTC(1970, 0, 1, aDate.getUTCHours(), aDate.getUTCMinutes(),
                       aDate.getUTCSeconds());
    return "@" + ( milliseconds-( milliseconds%86400 ) )/86400;
  },


  /**
   * Some of the numbers needs leading zeros
   * and this function returns the number with a leading zero
   * if the number is smaller than 10
   */
  insertLeadingZero: function(number) {
   if (number < 10)
      number = "0" + number;
    return number;
  },


  /**
   * This function is used to make every second character a backslash
   * so the insterted charaters isn't converted to hours, dates and so on.
   */
  addSlashes: function(string) {
    var stringTemp = "";
    for(var x=0; x<string.length; x++) {
      stringTemp += "\\";
      stringTemp += string.substr(x, 1);
    }
    return stringTemp;
  },

  getHours: function(aDate) {
    var rv;
    if (aDate)
      rv = aDate.getHours();
    else
      rv = (new Date()).getHours();

    return rv;
  }

}


