/*
_____       _    _    Corso   Italia,  178
(_|__   .  (_   |_|_  56125           Pisa
(_|_) |)|(()_)()| |   tel.  +39  050 46380
  |   |               picosoft@picosoft.it

 Copyright (C) Picosoft s.r.l. 1995-2002

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2, or (at your option)
 any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/
# include "odbcdate.h"
static char rcsid[] = "$Id: odbcdate.cpp 5.1 1999/11/11 15:41:59 picoSoft Exp Marco $";
static char rcsidh[] = odbcdate_h;
extern "C" {
# include <ctype.h>
# include <string.h>
}

const static int DayInMonth[13][2] = {
   { 0,  0},
   {31, 31},
   {28, 29},
   {31, 31},
   {30, 30},
   {31, 31},
   {30, 30},
   {31, 31},
   {31, 31},
   {30, 30},
   {31, 31},
   {30, 30},
   {31, 31}
};

inline static int
IsBisestile (int y)
{
   if ((y) <= 1752)
      return y % 4 == 0 ? 1 : 0;
   else
      return y % 4 == 0 && y % 100 != 0 || y % 400 == 0 ? 1 : 0;
}

static long
ToDayOfYear (const TIMESTAMP_STRUCT *ts)
{
   register unsigned int i;
   long Return = ts->day;
   for (i = 1; i < ts->month; i++)
      Return += DayInMonth[i][IsBisestile(ts->year)];
      
   return Return;
}

static void
FromDayOfYear (TIMESTAMP_STRUCT *ts, long jdate)
{
   register int i;
   
   if (jdate > 365 + IsBisestile(ts->year))
      ts->fraction = ts->year = ts->month = ts->day =
      ts->hour = ts->minute = ts->second = 0;
   else   
      for (i = 1; ; i++) {
         jdate -= DayInMonth[i][IsBisestile(ts->year)];
         if (jdate <= 0) {
            ts->month = i;
            ts->day = jdate + DayInMonth[i][IsBisestile(ts->year)];
            break;
         } 
      }
}

const char *
isStringTimeStamp(const char *Buffer, long len)
{
   char *Return = 0;

   if (Buffer && len >= 8)
      if (Buffer[0] == '{' &&
          (Buffer[1] == 'T' || Buffer[1] == 't') &&
          (Buffer[2] == 'S' || Buffer[2] == 's'))
         return TIMEST_TS_FMT;
      else if (Buffer[0] == '{' &&
              (Buffer[1] == 'D' || Buffer[1] == 'd'))
         return DATE_DT_FMT;
      else if (Buffer[0] == '{' &&
              (Buffer[1] == 'T' || Buffer[1] == 't'))
         return TIME_TM_FMT;
      else if(isdigit(Buffer[0]) &&
              isdigit(Buffer[1]) &&
              isdigit(Buffer[2]) &&
              isdigit(Buffer[3]) &&
              Buffer[4] == '-' &&
              isdigit(Buffer[5]) &&
              isdigit(Buffer[6]) &&
              Buffer[7] == '-' &&
              isdigit(Buffer[8]) &&
              isdigit(Buffer[9]))
         if (Buffer[10] == ' ' &&
             isdigit(Buffer[11]) &&
             isdigit(Buffer[12]) &&
             Buffer[13] == ':' &&
             isdigit(Buffer[14]) &&
             isdigit(Buffer[15]) &&
             Buffer[16] == ':' &&
             isdigit(Buffer[17]) &&
             isdigit(Buffer[18]))
            return TIMEST_STRFMT;
         else
            return DATE_STRFMT;
      else if (isdigit(Buffer[0]) &&
              isdigit(Buffer[1]) &&
              Buffer[2] == ':' &&
              isdigit(Buffer[3]) &&
              isdigit(Buffer[4]) &&
              Buffer[5] == ':' &&
              isdigit(Buffer[6]) &&
              isdigit(Buffer[7]))
            return TIME_STRFMT;
         
   return Return;
}

Error
StringToTimeStamp (TIMESTAMP_STRUCT * ts, const char *dateBuffer,
                   long & len, const char *fmt, long jStart)
{
   int fmtLen = strlen(fmt);
   const char *pFmt = fmt;
   const char *pDate = dateBuffer;
   long jdate = 0;

   ts->year = 0;
   ts->month = 0;
   ts->day = 0;
   ts->hour = 0;
   ts->minute = 0;
   ts->second = 0;
   ts->fraction = 0;

   while (*pFmt && *pDate) {
      if (*pFmt == ' ')
         pFmt++;
      else if (*pDate == ' ')
         pDate++;
      else if (!isdigit(*pDate)) {
         if (*pFmt != *pDate) // sync sui separatori
            *pFmt++;
         else
            pFmt++, pDate++;
      } else {
         switch (*pFmt) {
         case 'Y':
            ts->year = ts->year * 10 + *pDate - '0';
            break;
         case 'M':
            ts->month = ts->month * 10 + *pDate - '0';
            break;
         case 'D':
            ts->day = ts->day * 10 + *pDate - '0';
            break;
         case 'E':
         case 'J':
            jdate = jdate * 10 + *pDate - '0';
            break;
         case 'H':
            ts->hour = ts->hour * 10 + *pDate - '0';
            break;
         case 'N':
            ts->minute = ts->minute * 10 + *pDate - '0';
            break;
         case 'S':
            ts->second = ts->second * 10 + *pDate - '0';
            break;
         case 'T':
            ts->fraction = ts->fraction * 10 + *pDate - '0';
            break;
         default:
            if (*pFmt != *pDate) // sync sui separatori
               pFmt--;
         }
         pFmt++, pDate++;
      }
   }
   if (strstr (fmt, "JJJJ"))
      FromJulian (ts, jStart + jdate);
   else if (strstr (fmt, "EEE"))
         FromDayOfYear(ts, jdate);
   if (!ts->year && !ts->day && !ts->month)
      ts->year = ts->day = ts->month = 1;

   if (isTimeStampOk (ts))
      len = sizeof (TIMESTAMP_STRUCT);
   else {
      ts->fraction = ts->year = ts->month = ts->day =
      ts->hour = ts->minute = ts->second = 0;
      len = SQL_NULL_DATA;
   }
   return SQL_SUCCESS;
}

Error
TimeStampToString (const TIMESTAMP_STRUCT * its,
                   char *dateBuffer,
                   const char *fmt,
                   long jStart)
{
   int fmtLen = strlen(fmt);
   register int i;
   TIMESTAMP_STRUCT ts = *its;
   long jdate = 0;
   if (strstr (fmt, "EEE"))
      jdate = ToDayOfYear (&ts);
   else if (strstr (fmt, "JJJJ"))
      jdate = ToJulian (&ts) - jStart;

   dateBuffer[fmtLen] = 0;
   for (i = fmtLen -1; i >= 0; i--) {
      switch (fmt[i]) {
      case 'Y':
         dateBuffer[i] = (char) (ts.year % 10 + '0');
         ts.year /= 10;
         break;
      case 'M':
         dateBuffer[i] = (char) (ts.month % 10 + '0');
         ts.month /= 10;
         break;
      case 'D':
         dateBuffer[i] = (char) (ts.day % 10 + '0');
         ts.day /= 10;
         break;
      case 'J':
      case 'E':
         dateBuffer[i] = (char) (jdate % 10 + '0');
         jdate /= 10;
         break;
      case 'A':
      case 'H':
         dateBuffer[i] = (char) (ts.hour % 10 + '0');
         ts.hour /= 10;
         break;
      case 'N':
         dateBuffer[i] = (char) (ts.minute % 10 + '0');
         ts.minute /= 10;
         break;
      case 'S':
         dateBuffer[i] = (char) (ts.second % 10 + '0');
         ts.second /= 10;
         break;
      case 'T':
         dateBuffer[i] = (char) (ts.fraction % 10 + '0');
         ts.fraction /= 10;
         break;
      default:
         break;
      }
   }
   return SQL_SUCCESS;
}

PBool
isTimeStampOk (TIMESTAMP_STRUCT *ts)
{
   if (ts->month >= 1 && ts->month <= 12 &&
       ts->day >= 1 && ts->day <= 31 &&
       ts->hour < 24 &&
       ts->minute < 60 &&
       ts->second < 60)
      return PTrue;
   else
      return PFalse;
}



void
FromJulian (TIMESTAMP_STRUCT * ts, long julian)
{
   register int i;
   
   ts->day = ts->month = ts->year = 0;
   if (julian > 639803L) /* giuliano del 2/9/1752 */
      julian += 11; /* Compensazione gregoriana */
   julian -= 5;  /* 01/01/0001 era sabato (?); cosi' j % 7 da domenica = 0 */
   if ( julian > 0) {
      for (i = 1; ; i++)
         if (IsBisestile(i)) {
            julian -= 366;
            if (julian <= 0) {
               ts->year = i;
               julian += 366;
               break;
            }
         } else {
            julian -= 365;
            if (julian <= 0) {
               ts->year = i;
               julian += 365;
               break;
            }
         }
      for (i = 1; ; i++) {
         julian -= DayInMonth[i][IsBisestile(ts->year)];
         if (julian <= 0) {
            ts->month = i;
            ts->day = julian + DayInMonth[i][IsBisestile(ts->year)];
            break;
         } 
      }
   }
} 

long
ToJulian(const TIMESTAMP_STRUCT * ts)
{
   long Return = 0;
   register int i;
   
   Return = ts->day;
   for (i = 1; i < ts->month; i++)
      Return += DayInMonth[i][IsBisestile(ts->year)];
   for (i = 1; i < ts->year; i++)
      if (IsBisestile(i))
         Return += 366;
      else
         Return += 365;
   if (ts->year > 1752 ||
       ts->year == 1752 && ts->month > 9 ||
       ts->year == 1752 && ts->month == 9 && ts->day >= 14)
      Return -= 11;
   Return += 5;  /* 01/01/0001 era sabato (?); cosi' j % 7 da domenica = 0 */

   return Return;
}

char *
CurrentTime (time_t tNow, const char *format, long jStart, int yearToAdd)
{
   TIMESTAMP_STRUCT ts;
   struct tm now = *localtime(&tNow);
   int lenFmt = strlen(format);
   char *Return = new char[lenFmt + 1];

   memcpy (Return, format, lenFmt);
   Return[lenFmt] = 0;

   ts.year = now.tm_year + yearToAdd + 1900;
   ts.month = now.tm_mon + 1;
   ts.day = now.tm_mday;
   ts.hour = now.tm_hour;
   ts.minute = now.tm_min;
   ts.second = now.tm_sec;
   ts.fraction = 0;

   TimeStampToString (&ts, Return, format, jStart);
   return Return;
}

double
Extract (const char *val, toknum what, const char *fmt, long jStart)
{
   TIMESTAMP_STRUCT ts;
   long len;
   double Return = 0;

   StringToTimeStamp (&ts, val, len, fmt, jStart);
   switch (what) {
   case DAYOFMONTH:
      Return = ts.day;
      break;
   case HOUR:
      Return = ts.hour;
      break;
   case MILLISECOND:
      Return = ts.fraction;
      break;
   case MINUTE:
      Return = ts.minute;
      break;
   case MONTH:
      Return = ts.month;
      break;
   case SECOND:
      Return = ts.second;
      break;
   case YEAR:
      Return = ts.year;
      break;
   }
   return Return;
}
