//  ----------------------------------------------------------------------------
//  <p>Title: DateTime - test-app.</p>
//  <p>Description: date & time processing</p>
//  <p>Copyright: Copyright (c) 2010</p>
//  <p>Company: SiG Software Integration GmbH</p>
//  @author Dr. Horst Walther
//  @version 1.00.1
//  ----------------------------------------------------------------------------
/*  inherited methods:

  getDate() (Monatstag ermitteln)
  getDay() (Wochentag ermitteln)
  getFullYear() (volles Jahr ermitteln)
  getHours() (Stundenteil der Uhrzeit ermitteln)
  getMilliseconds() (Millisekunden ermitteln)
  getMinutes() (Minutenteil der Uhrzeit ermitteln)
  getMonth() (Monat ermitteln)
  getSeconds() (Sekundenteil der Uhrzeit ermitteln)
  getTime() (Zeitpunkt ermitteln)
  getTimezoneOffset() (Zeitzonenabweichung der Lokalzeit ermitteln)
  getUTCDate() (Monatstag von UTC-Zeit ermitteln)
  getUTCFullYear() (volles Jahr von UTC-Zeit ermitteln)
  getUTCHours() (Stundenteil der UTC-Uhrzeit ermitteln)
  getUTCMilliseconds() (Millisekundenteil der UTC-Uhrzeit ermitteln)
  getUTCMinutes() (Minutenteil der UTC-Uhrzeit ermitteln)
  getUTCMonth()(Monat von UTC-Uhrzeit ermitteln)
  getUTCSeconds()(Sekundenteil der UTC-Uhrzeit ermitteln)
  getYear() (Jahr ermitteln)
  parse() (Millisekunden seit dem 01.01.1970 ermitteln)
  setDate() (Monatstag setzen)
  setFullYear() (volles Jahr setzen)
  setHours() (Stunden der Uhrzeit setzen)
  setMilliseconds() (Millisekunden setzen)
  setMinutes() (Minuten der Uhrzeit setzen)
  setMonth() (Monat setzen)
  setSeconds() (Sekunden der Uhrzeit setzen)
  setTime() (Datum und Uhrzeit setzen)
  setUTCDate() (Monatstag für UTC-Zeit setzen)
  setUTCFullYear() (volles Jahr für UTC-Zeit setzen)
  setUTCHours() (Stunden der UTC-Zeit setzen)
  setUTCMilliseconds() (Millisekunden der UTC-Zeit setzen)
  setUTCMinutes() (Minuten der UTC-Zeit setzen)
  setUTCMonth() (Monat für UTC-Zeit setzen)
  setUTCSeconds() (Sekunden der UTC-Zeit setzen)
  setYear() (Datum und Uhrzeit setzen)
  toGMTString() (Zeitpunkt in UTC-Format konvertieren)
  toLocaleString() (Zeitpunkt in lokales Format konvertieren)
  UTC() (UTC-Zeit seit dem 01.01.1970 ermitteln)
*/
   Date.prototype.id ='Date';
   Date.prototype.SECONDS = 0;
   Date.prototype.MINUTES = 1;
   Date.prototype.HOURS = 2;
   Date.prototype.DAYS = 3;
   Date.prototype.MONTHS = 4;
   Date.prototype.YEARS = 5;
   Date.prototype.SMART = 6;
   Date.prototype.TIME = 7;
   Date.prototype.DATE = 8;
   Date.prototype.ALL  = 9;
   Date.prototype.DOWN = -1;
   Date.prototype.UP = 1;
   Date.prototype.DAY  = 1000*60*60*24;
   Date.prototype.HOUR = 1000*60*60;
   Date.prototype.MIN  = 1000*60;
   Date.prototype.SEC  = 1000;
   Date.prototype.isBritish = true;
   Date.prototype.GC_firstYYYY = this.isBritish ? 1752 : 1582;
   Date.prototype.GC_firstMM = this.isBritish ? 9 : 10;
   Date.prototype.GC_firstDD = this.isBritish ? 14 : 15;
   Date.prototype.Leap100RuleYYYY = ((this.GC_firstYYYY + 99) / 100) * 100;
   Date.prototype.Leap400RuleYYYY = ((this.GC_firstYYYY + 399) / 400) * 400;
   Date.prototype.usual_DaysPerMonthTable = new Array(31, 28, 31, 30, 31, 30,  31, 31, 30,  31, 30, 31);  /* J F M  A M J  J A S  O N D */
   Date.prototype.usual_daysInYearPriorToMonthTable = new Array(0, 31, 59,  90, 120, 151,  181, 212, 243,  273, 304, 334);  /* J F M  A M J  J A S  O N D */
   Date.prototype.walMath = new WalMath();
    
  //  ----------------------------------------------------------------------------
   Date.prototype.now = function() {
    return new Date();
  };
  
  //  ----------------------------------------------------------------------------
  Date.prototype.age = function (asof) {
//    alert ('age: '+this+' --> '+asof+' == '+this.getTime()+' --> '+asof.getTime());
    if (this.getTime() >= asof.getTime()) return new Array(0, 0, 0, 0, 0, 0);
    var birthYYYY = this.getFullYear();
    var birthMM   = this.getMonth();
    var birthDD   = this.getDate();

    var asofYYYY = asof.getFullYear();
    var asofMM   = asof.getMonth();
    var asofDD   = asof.getDate();

    var ageInYears   = asofYYYY          - birthYYYY;
    var ageInMonths  = asofMM            - birthMM;
    var ageInDays    = asofDD            - birthDD;
	var ageInHours   = asof.getHours()   - this.getHours();
	var ageInMinutes = asof.getMinutes() - this.getMinutes();
    var ageInSeconds = asof.getSeconds() - this.getSeconds();
	
//   alert ('raw age: ('+ ageInYears+', '+ ageInMonths+', '+ ageInDays+', '+ ageInHours+', '+ ageInMinutes+', '+ ageInSeconds+') ');

   if (ageInSeconds < 0) {
      ageInSeconds += 60;
      ageInMinutes--;
    }
   if (ageInMinutes < 0) {
      ageInMinutes += 60;
      ageInHours--;
    }
   if (ageInHours < 0) {
      ageInHours += 24;
      ageInDays--;
    }
   if (ageInDays < 0) {
      ageInDays += this.daysInMonthYear(birthMM, birthYYYY);
      ageInMonths--;
    }
    if (ageInMonths < 0) {
      ageInMonths += 12;
      ageInYears--;
    }
    if (birthYYYY < 0 && asofYYYY > 0) {
      ageInYears--;
    }
    if (ageInYears < 0) return new Array(0, 0, 0, 0, 0, 0);
	
    return new Array(ageInSeconds, ageInMinutes, ageInHours, ageInDays, ageInMonths, ageInYears);
  };

  //  ----------------------------------------------------------------------------
   Date.prototype.daysInMonthYear  = function (mm, yyyy) {
    if (mm != 1) return this.usual_DaysPerMonthTable[mm];
    else return new Date (yyyy).isLeap() ? 29 : 28;
  };
 
  //  ----------------------------------------------------------------------------
   Date.prototype.daysInMonth  = function () {
    if (this.getMonth() != 1) return this.usual_DaysPerMonthTable[this.getMonth()];
    else return this.isLeap() ? 29 : 28;
  };
 
 //  ----------------------------------------------------------------------------
   Date.prototype.isLeap  = function () {
    // if you change this code, make sure you make corresponding changes to jan01OfYear, toGregorian, MondayIsZeroAdjustment,
    // SundayIsZeroAdjustment, AD_epochAdjustment and BC_epochAdjustment (yyyy & 3 is a fast way of saying yyyy % 4).
    var yyyy = this.getFullYear();
    if (yyyy < this.Leap100RuleYYYY) {
      if (yyyy < 0) {
        return ((yyyy + 1) & 3) == 0;
      } else {
        return (yyyy & 3) == 0;
      }
    }
    if ((yyyy & 3) != 0) {
      return false;
    }
    if (yyyy % 100 != 0) {
      return true;
    }
    if (yyyy < this.Leap400RuleYYYY) {
      return false;
    }
    return yyyy % 400 == 0;
  };

 //  ----------------------------------------------------------------------------
  Date.prototype.changeLevel = function (d) {
    var diff = this.age(d);
	for (var i=this.YEARS; i>=this.SECONDS; i--) 
	  {if (diff[i] > 0.0) return i;}
    alert ('###  err: '+this.id+'.changeLevel: '+this.toString()+'=='+d.toString());
    return this.ALL;
  };

 //  ----------------------------------------------------------------------------
  Date.prototype.minSignificance = function () {
    if (this.getSeconds()) return 0;
    if (this.getMinutes()) return 1;
    if (this.getHours()) return 2;
    if (this.getDate()>1) return 3;
    if (this.getMonth()) return 4;
    return 5;
  };

 //  ----------------------------------------------------------------------------
  Date.prototype.maxSignificance = function () {
    if (this.getFullYear()) return 5;
    if (this.getMonth()) return 4;
    if (this.getDate()>1) return 3;
    if (this.getHours()) return 2;
    if (this.getMinutes()) return 1;
    return 0;
  };

 //  ----------------------------------------------------------------------------
  Date.prototype.getBounds = function (d) {
    var diff = this.age(d);
//    alert (this.id+'.getBounds: '+this.toString()+' --> '+d.toString()+': '+diff);
    var i=this.SECONDS;
    while (diff[i] <= 0.0) i++;  // -->
    var j=this.YEARS;
    while (diff[j] <= 0.0) j--;  // -->
    return [i, j];
  };

 //  ----------------------------------------------------------------------------
  Date.prototype.getChangeBounds = function (d) {
   var lowDateArray = this.toArray();
   var highDateArray = d.toArray();
//    alert (this.id+'.getChangeBounds: '+this.toString()+' --> '+d.toString()+': '+lowDateArray+' to '+highDateArray);
    for (var i=this.SECONDS; (i < this.YEARS) && (lowDateArray[i] == highDateArray[i]); i++);  // -->
    for (var j=this.YEARS; (j > this.SECONDS) && (highDateArray[j] == lowDateArray[j]); j--);  // -->
    if (i > j) return [d.minSignificance(), d.maxSignificance()];
    return [i, j];
  };

  //  ----------------------------------------------------------------------------
  Date.prototype.toArray  = function () {
    return [this.getSeconds(), this.getMinutes(), this.getHours(), this.getDate(), this.getMonth()+1, this.getFullYear()];
  };

  //  ----------------------------------------------------------------------------
Date.prototype.fromString = function (s) {
  var BLANK = ' ';
  var COL = ':';
  var MINUS = '-';
  var SLASH = '/';
  var DOT = '.';
  var dateStr = '';
  var timeStr = '';
  var ISO = 0;
  var ENGLISH = 1;
  var GERMAN = 2;
  var UNDEF = 9;
  var d = new Date();
  var mm, dd;
  if (!s) alert ('###  err: '+this.id+'.fromString ('+s+')');
  s = s.trim().replace (',', BLANK);
//  alert (this.id+'.fromString ('+s+')');
  var i = s.indexOf(BLANK);
  if (i > -1) {
    dateStr = s.substring (0, i);
    timeStr = s.substring (i+1);
  } else {
    var iCol = s.indexOf (COL);
    if (iCol > -1)
      timeStr = s;
    else {
      dateStr = s;
      d.setHours(0);
      d.setMinutes(0);
      d.setSeconds(0);
    }
  }

  if (timeStr.length) {
//    alert ('timeStr1='+timeStr);
    i = timeStr.indexOf (COL);
    d.setHours (timeStr.substring (0, i));
    timeStr = timeStr.substring(i+1);
//    alert ('timeStr2='+timeStr);
    i = timeStr.indexOf (COL);
    if (i > -1) {
//      alert ('Minutes will be: '+(timeStr.substring (0, i))+', Seconds will be: '+(timeStr.substring(i+1)));
      d.setMinutes (timeStr.substring (0, i));
      d.setSeconds (timeStr.substring (i+1));
    } else {
//      alert ('Minutes will be: '+timeStr+', Seconds will be 0');
      d.setMinutes (timeStr);
      d.setSeconds(0);
    }
//    alert ('time: '+d.toString());
  }

  if (dateStr.length) 
    if (dateStr.length == 4) {
      d.setFullYear (dateStr);
      d.setMonth(0);
      d.setDate(1);
    } else {
      var formatType = dateStr.indexOf (MINUS) > -1 ? ISO : (dateStr.indexOf (SLASH) > -1 ? ENGLISH : (dateStr.indexOf (DOT) ? GERMAN : UNDEF));
//      alert ('dateStr='+dateStr+', formatType='+formatType);
      switch (formatType) {
        case ISO: {
          i = dateStr.indexOf (MINUS);
          d.setFullYear (dateStr.substring (0, i));
          dateStr = dateStr.substring(i+1);
//          alert ('dateStr='+dateStr);
          i = dateStr.indexOf (MINUS);
          if (i > -1) {
            d.setMonth (dateStr.substring (0, i)-1);
            d.setDate  (dateStr.substring(i+1));
          } else {
            d.setMonth (dateStr-1);
            d.setDate  (1);
          }
//          alert ('ISO date : '+d.toString());
          break;
        }
        case ENGLISH: {
          i = dateStr.indexOf (SLASH);
          mm = dateStr.substring (0, i)-1;
          dateStr = dateStr.substring(i+1);
          i = dateStr.indexOf (SLASH);
          if (i > -1) {
            d.setDate (dateStr.substring (0, i));
            d.setMonth (mm);
            d.setFullYear  (dateStr.substring(i+1));
          } else {
            d.setMonth (mm);
            d.setDate (dateStr);
          }
//          alert ('English date : '+d.toString());
          break;
        }
        case GERMAN: {
          i = dateStr.indexOf (DOT);
          dd = dateStr.substring (0, i);
          dateStr = dateStr.substring(i+1);
          i = dateStr.indexOf (DOT);
          if (i > -1) {
            d.setFullYear (dateStr.substring(i+1));
            d.setMonth (dateStr.substring (0, i)-1);
            d.setDate (dd);
          } else {
            d.setFullYear (dateStr);
            d.setMonth (dd-1);
          }
//          alert ('German date : '+d.toString());
          break;
        }
        default: {
           alert ('###  unknown dateFormat of '+s);
          break;
        }
      }
	}  
//  alert ('will return: '+d.toString());
  return d;
}

  //  ----------------------------------------------------------------------------
  Date.prototype.toString  = function (level) {
    var s = "";
    level = level == undefined ? this.SMART : level;
    if (isArray(level)) {
      var from = level[0];
      var to   = level[1];
    } else {
      var from = ((level == this.ALL) || (level == this.SMART) || (level == this.TIME)) ? this.SECONDS : (level == this.DATE ? this.DAYS  : level);
      var to   = ((level == this.ALL) || (level == this.SMART) || (level == this.DATE)) ? this.YEARS   : (level == this.TIME ? this.HOURS : level);
    }
	for (var i=from; i<=to; i++) // >
      switch (i) {
        case this.SECONDS:        { // 0=age in seconds
          s = twoDigit(this.getSeconds());
          s = (level == this.SMART) && (s=='00') ? '' : s+'';
          break;
        }
        case this.MINUTES:        { // 1=age in minutes
          s = twoDigit(this.getMinutes()) + (s.length ? ':'+s : '');
          s = (level == this.SMART) && (s=='00') ? '' : s;
          break;
        }
        case this.HOURS:        { // 2=age in hours
          s = twoDigit(this.getHours()) + (s.length ? ':'+s : '');
          s = (level == this.SMART) && (s=='00') ? '' : s;
          s += s.length == 2 ? ':00' : '';
          break;
        }
        case this.DAYS:        { // 3=age in days.
          s = twoDigit(this.getDate()) + (s.length ? ' '+s : '');
          s = (level == this.SMART) && s=='01' ? '' : s;
          break;
        }
        case this.MONTHS:        { // 4=age in months
          s = twoDigit(this.getMonth()+1) + (s.length ? '-'+s : '');
          s = (level == this.SMART) && s=='01' ? '' : s;
          break;
        }
        case this.YEARS:        { // 5=age in years
          s = this.getFullYear() + (s.length ? '-'+s : '');
          break;
        }
        default:        {
          alert("### Date.toString().err: (" + i + ")");
          break;
        }
      }
    return s;
  }

  //  --------------------------------------------------------------------------->
  Date.prototype.years = function (d) {
    var diff = this.age(d);
    return diff[this.YEARS];
  }

  //  ----------------------------------------------------------------------------
  Date.prototype.months = function (d) {
    var diff = this.age(d);
    return diff[this.MONTHS]+12*diff[this.YEARS];
  }

 //  ----------------------------------------------------------------------------
   Date.prototype.days = function (d) {
    return Math.floor((d.getTime()-this.getTime())/this.DAY);
  }
  //  ----------------------------------------------------------------------------
   Date.prototype.hours = function (d) {
    return Math.floor((d.getTime()-this.getTime())/this.HOUR);
  }
  //  ----------------------------------------------------------------------------
   Date.prototype.minutes = function (d) {
    return Math.floor((d.getTime()-this.getTime())/this.MIN);
  }
  //  ----------------------------------------------------------------------------
   Date.prototype.seconds = function (d) {
    return Math.floor((d.getTime()-this.getTime())/this.SEC);
  }

  //  ----------------------------------------------------------------------------
   Date.prototype.fromMilliSec = function (ms) {
    return new Date(ms);
  }

  //  --------------------------------------------------------------------------->
   Date.prototype.fromSec = function (s) {
    return new Date(s * this.SEC);
  }

  //  ----------------------------------------------------------------------------
   Date.prototype.fromMin = function (m) {
    return new Date(m * this.MIN);
  }

  //  ----------------------------------------------------------------------------
   Date.prototype.fromHour = function (h) {
    return new Date(h * this.HOUR);
  }

  //  ----------------------------------------------------------------------------
   Date.prototype.fromDay = function (d) {
    return new Date(d * this.DAY);
  }

  //  ----------------------------------------------------------------------------
   Date.prototype.copy = function () {
    return this.fromMilliSec (this.getTime())
  };

  //  ----------------------------------------------------------------------------
   Date.prototype.addYears = function (y) {
    if(!y) return this;
    this.setFullYear(this.getFullYear() + y);
    if (!this.isLeap () && (this.getMonth() == 1) && (this.getDate() == 29)) 
      this.setDate(28);
    return this;
  }

  //  ----------------------------------------------------------------------------
   Date.prototype.addMonths = function (m) {
    if (!m) return this;
    var mm = this.getMonth() + m;
    this.setFullYear (this.getFullYear() + mm / 12);
    mm = mm % 12;
    this.setMonth(mm);
    if (!this.isLeap () && (mm == 1) && (this.getDate() == 29)) 
      this.setDate (28);
    return this;
  } 

  //  --------------------------------------------------------------------------->
  Date.prototype.addDays = function (days) {
    if (!days) return this;
    this.setTime (this.getTime() + days * this.DAY);
    return this;
  }

  //  --------------------------------------------------------------------------->
  Date.prototype.addHours = function (hours) {
    if (!hours) return this;
    this.setTime (this.getTime() + hours * this.HOUR);
    return this;
  }

  //  --------------------------------------------------------------------------->
  Date.prototype.addMinutes = function (minutes) {
    if (!minutes) return this;
    this.setTime (this.getTime() + minutes * this.MIN);
    return this;
  }

  //  --------------------------------------------------------------------------->
  Date.prototype.addSeconds = function (seconds) {
    if (!seconds) return this;
    this.setTime (this.getTime() + seconds * this.SEC);
    return this;
  }

  //  ---------------------------------------------------------------------------
  Date.prototype.isLowerEqual = function (d) {
    return this.getTime() <= d.getTime();
  };

  //  --------------------------------------------------------------------------->
  Date.prototype.setZero = function () {
    this.setFullYear(1970);
    this.setMonth(0);
    this.setDate(1);
    this.setHours(0);
    this.setMinutes(0);
    this.setSeconds(0);
    return this;
  };
