/*
** October 2005
**
** This file is a ported version of SQLite's DateTime functions. We have implemented
** only the date bits that we needed.
**
** We cannot claim copyright as most of the work was not ours.
**
** Below is the original header, enjoy.
*/

/*
** 2003 October 31
**
** The author disclaims copyright to this source code.  In place of
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains the C functions that implement date and time
** functions for SQLite.  
**
** There is only one exported symbol in this file - the function
** sqlite3RegisterDateTimeFunctions() found at the bottom of the file.
** All other code has file scope.
**
** $Id: date.c,v 1.45 2005/06/25 18:42:14 drh Exp $
**
** NOTES:
**
** SQLite processes all times and dates as Julian Day numbers.  The
** dates and times are stored as the number of days since noon
** in Greenwich on November 24, 4714 B.C. according to the Gregorian
** calendar system.
**
** 1970-01-01 00:00:00 is JD 2440587.5
** 2000-01-01 00:00:00 is JD 2451544.5
**
** This implemention requires years to be expressed as a 4-digit number
** which means that only dates between 0000-01-01 and 9999-12-31 can
** be represented, even though julian day numbers allow a much wider
** range of dates.
**
** The Gregorian calendar system is used for all dates and times,
** even those that predate the Gregorian calendar.  Historians usually
** use the Julian calendar for dates prior to 1582-10-15 and for some
** dates afterwards, depending on locale.  Beware of this difference.
**
** The conversion algorithms are implemented based on descriptions
** in the following text:
**
**      Jean Meeus
**      Astronomical Algorithms, 2nd Edition, 1998
**      ISBM 0-943396-61-1
**      Willmann-Bell, Inc
**      Richmond, Virginia (USA)
*/

function DateTime()
{
  this.julian   = null;   /* The julian day number */
  this.year     = null;   /* Year */
  this.month    = null;  /* Month */
  this.day      = null;    /* Day */
  this.hour     = null;   /* Hour */
  this.minute   = null;   /* Minutes */
  this.timezone = null;   /* Timezone offset in minutes */
  this.second   = null;   /* Seconds */
  
  
  if (arguments.length == 0)
  {
    var date = new Date();
    this.year = date.getYear() + 1900;
    this.month = date.getMonth()+1;
    this.day = date.getDate();
  }
  else if (arguments.length == 1)
  {
    this.julian = parseFloat(arguments[0]);
  }
  else if (arguments.length == 3)
  {
    this.year     = arguments[0];
    this.month    = arguments[1];
    this.day      = arguments[2];
  }
}

DateTime.prototype.computeJulian = function()
{
  if (!this.julian)
  { 
    var Y, M, D, A, B, X1, X2;
  
    Y = this.year;
    M = this.month;
    D = this.day;
    
    if( M<=2 )
    {
      Y--;
      M += 12;
    }
    A = Math.floor(Y/100);
    B = Math.floor(2 - A + A/4);
    X1 = Math.floor(365.25*(Y+4716));
    X2 = Math.floor(30.6001*(M+1));
    this.julian = X1 + X2 + D + B - 1524.5 + 0.9; // For some reason I need to add an extra 0.9 here. I don't know why.
  }
}

DateTime.prototype.computeGregorian = function()
{
  if (this.julian)
  {
    var Z, A, B, C, D, E, X1;
    Z = this.julian + 0.5;
    A = Math.floor((Z - 1867216.25)/36524.25);
    A = Math.floor(Z + 1 + A - (A/4));
    B = A + 1524;
    C = Math.floor((B - 122.1)/365.25);
    D = Math.floor(365.25*C);
    E = Math.floor((B-D)/30.6001);
    X1 = Math.floor(30.6001*E);
    this.day = B - D - X1;
    this.month = E<14 ? E-1 : E-13;
    this.year = this.month>2 ? C - 4716 : C - 4715;
    this.julian = null;
  }
}

DateTime.prototype.modify = function()
{
  //       1                          2
  var regex = /((?:[+-]?\d)|(?:start of)) (day|week|month|year|decade|century)/;
  for (var index=0; index<arguments.length; index++)
  {
    var results = regex.exec(arguments[index]);
    if (results == null)
    {
      throw new Error ("'"+arguments[index]+"' is not a valid modifier");
    }
    
    if (results[1] == "start of")
    {
      if (results[2] == "week")
      {
        this.computeJulian();
        this.julian -= this.getWeekDay();
      }
      else
      {
        this.computeGregorian();
        switch (results[2])
        {
          case "century" : this.year -= this.year % 100;
          case "decade"  : this.year -= this.year % 10;
          case "year"    : this.month = 1;
          case "month"   : this.day   = 1;
          case "day"     : break;
        }
      } 
    }
    else
    {
      if (results[2] =="day")
      {
        this.computeJulian();
        this.julian += parseInt(results[1]);
        //this.computeGregorian();
      }
      else if (results[2] =="week")
      {
        this.computeJulian();
        this.julian += parseInt(results[1]) * 7;
        //this.computeGregorian();
      }
      else if (results[2] =="month")
      {
        this.computeGregorian();
        this.month += parseInt(results[1]);
        //alert(parseInt(results[1]));
        //var x       = this.month>0 ? (this.month-1)/12 : (this.month-12)/12;
        //this.year  += Math.floor(x);
        //this.month -= x*12;
        this.computeJulian();
        //this.computeGregorian();
      }
      else if (results[2] =="year")
      {
        this.computeGregorian();
        this.year += parseInt(results[1]);
      }
      else if (results[2] =="decade")
      {
        this.computeGregorian();
        this.year += parseInt(results[1]) * 10;
      }
      else if (results[2] =="century")
      {
        this.computeGregorian();
        this.year += parseInt(results[1]) * 100;
      }
      else
      {
      }  
    }
  }
  return this;
}

DateTime.prototype.toString = function ()
{
  return this.getJulian();
}

DateTime.prototype.toDate = function ()
{
  return new Date(this.year,this.month-1,this.day);
}

DateTime.prototype.toDateTime = function ()
{
  var object = new Object();
  
  for (property in this)
  { 
    object[property] = this[property]; 
  }
  return object;
}

DateTime.prototype.weekday = function ()
{
  this.computeJulian();
  var Z = Math.floor(this.julian + 1.5);
  return Z % 7;
}

DateTime.prototype.getJulian = function ()
{
  this.computeJulian();
  return this.julian;
}

DateTime.prototype.setJulian = function (julian)
{
  this.julian = julian;
  return this.julian;
}

DateTime.prototype.getDay = function ()
{
  this.computeGregorian();
  return this.day;
}

DateTime.prototype.setDay = function (day)
{
  this.computeGregorian();
  this.day = day;
  return this.day;
}

DateTime.prototype.getMonth = function ()
{
  this.computeGregorian();
  return this.month;
}

DateTime.prototype.setMonth = function (month)
{
  this.computeGregorian();
  this.month = month;
  return this.month;
}

DateTime.prototype.getYear = function ()
{
  this.computeGregorian();
  return this.year;
}

DateTime.prototype.setYear = function (year)
{
  this.computeGregorian();
  this.year = year;
  return this.year;
}

DateTime.prototype.getWeekDay = function ()
{
  this.computeJulian();
  return Math.floor(this.julian + 1.5) % 7;
}

DateTime.prototype.DAY_NAMES = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
DateTime.prototype.MONTH_NAMES = [null,"January","February","March","April","May","June","July","August","September","October","November","December"];


DateTime.prototype.getDayName = function ()
{
  return this.DAY_NAMES[this.getWeekDay()];
}

DateTime.prototype.getMonthName = function ()
{
  return this.MONTH_NAMES[this.getMonth()];
}


