Logo Search packages:      
Sourcecode: gambas version File versions  Download package

gbx_date.c

/***************************************************************************

  Date.c

  The date & time management routines

  Code adapted from GLib 1.2.8

  (c) 2000-2004 Benoît Minisini <gambas@users.sourceforge.net>

  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 1, 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.

***************************************************************************/

#define __DATE_C

#include <unistd.h>
#include <ctype.h>

#include "gb_common.h"
#include "gb_common_buffer.h"
#include "gb_error.h"
#include "gbx_value.h"
#include "gbx_local.h"
#include "gbx_number.h"

#include "gbx_date.h"

/*#define DEBUG_DATE*/


static const char days_in_months[2][13] =
{  /* error, jan feb mar apr may jun jul aug sep oct nov dec */
  {  0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
  {  0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } /* leap year */
};

static const short days_in_year[2][14] =
{  /* 0, jan feb mar apr may  jun  jul  aug  sep  oct  nov  dec */
  {  0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
  {  0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }  /* leap year */
};

static long date_timezone;
static int date_daylight;

static double _start_time;



/* Returns 1 for a leap year, 0 else */

static int date_is_leap_year(short year)
{
  if (year < 0)
    year += 8001;

  if ((((year % 4) == 0) && ((year % 100) != 0)) || (year % 400) == 0)
    return 1;
  else
    return 0;
}


static boolean date_is_valid(DATE_SERIAL *date)
{
  return ((date->month >= 1) && (date->month <= 12) &&
          (date->year >= DATE_YEAR_MIN) && (date->year <= DATE_YEAR_MAX) && (date->year != 0) &&
          (date->day >= 1) && (date->day <= days_in_months[date_is_leap_year(date->year)][(short)date->month]) &&
          (date->hour >= 0) && (date->hour <= 23) && (date->min >= 0) && (date->min <= 59) &&
          (date->sec >= 0) && (date->sec <= 59));
}


static short date_to_julian_year(short year)
{
  if (year < 0)
    return year - DATE_YEAR_MIN;
  else
    return year - DATE_YEAR_MIN - 1;
}

static short date_from_julian_year(short year)
{
  if (year < (-DATE_YEAR_MIN))
    return year + DATE_YEAR_MIN;
  else
    return year + DATE_YEAR_MIN + 1;
}

PUBLIC void DATE_init(void)
{
  time_t t = (time_t)0L;
  struct tm *tm;
  struct timeval tv;

  if (gettimeofday(&tv, NULL) == 0)
    _start_time = (double)tv.tv_sec + (double)tv.tv_usec / 1E6;
  else
    _start_time = 0.0;

  tm = localtime(&t);

  #ifdef __FreeBSD__
  date_timezone = tm->tm_gmtoff;
  date_daylight = tm->tm_isdst;
  #else
  date_timezone = timezone;
  date_daylight = daylight;
  #endif

  #ifdef DEBUG_DATE
  printf("TimeZone = %ld DayLight = %d Hour = %d\n", date_timezone, date_daylight, tm->tm_hour);

  tm = gmtime(&t);
  printf("Hour = %d\n", tm->tm_hour);
  #endif
}


PUBLIC DATE_SERIAL *DATE_split(VALUE *value)
{
  static long last_nday, last_nmsec;
  static DATE_SERIAL last_date = { 0 };

  long nday, nmsec;
  long A, B, C, D, E, M;

  nday = value->_date.date;
  nmsec = value->_date.time;

  if (nday > 0)
    nmsec += date_timezone * 1000;

  if (nmsec < 0)
  {
    nday--;
    nmsec += 86400000;
  }
  else if (nmsec >= 86400000)
  {
    nday++;
    nmsec -= 86400000;
  }

  if (last_nmsec != nmsec)
  {
    last_nmsec = nmsec;

    last_date.msec = nmsec % 1000;
    nmsec /= 1000;
    last_date.sec = nmsec % 60;
    nmsec /= 60;
    last_date.min = nmsec % 60;
    nmsec /= 60;
    last_date.hour = nmsec;
  }

  if (last_nday != nday)
  {
    last_nday = nday;

    /*nday += DATE_NDAY_BC;*/
    if (nday <= 0)
    {
      last_date.month = 0;
      last_date.day = 0;
      last_date.year = 0;
      last_date.weekday = 0;
    }
    else
    {
      A = nday - 58 - 1;
      B = (4 * (A + 36524))/ 146097 - 1;
      C = A - (146097 * B)/4;
      D = (4 * (C + 365)) / 1461 - 1;
      E = C - ((1461 * D) / 4);
      M = (5 * (E - 1) + 2) / 153;

      last_date.month = M + 3 - (12 * (M / 10));
      last_date.day = E - (153 * M + 2)/5;
      last_date.year = 100 * B + D + (M / 10) /*- 4800*/ ;
      last_date.year = date_from_julian_year(last_date.year);
      last_date.weekday = (nday - 1) % 7;
    }
  }

  return &last_date;
}


PUBLIC bool DATE_make(DATE_SERIAL *date, VALUE *val)
{
  short year;
  long nday;
  bool timezone;

  if (date->year == 0)
  {
    nday = 0; /*(-DATE_NDAY_BC - 1);*/
    timezone = FALSE;
  }
  else
  {
    if (!date_is_valid(date))
      return TRUE;

    year = date_to_julian_year(date->year);

    nday = year * 365;
    year--;
    nday += (year >>= 2);
    nday -= (year /= 25);
    nday += year >> 2;

    nday += days_in_year[date_is_leap_year(date->year)][(short)date->month] + date->day;

    /*nday -= DATE_NDAY_BC;*/

    timezone = TRUE;
  }

  val->_date.date = nday;
  val->_date.time = ((date->hour * 60) + date->min) * 60 + date->sec;
  if (timezone)
    val->_date.time -= date_timezone;

  if (val->_date.time < 0)
  {
    val->_date.date--;
    val->_date.time += 86400;
  }
  else if (val->_date.time >= 86400)
  {
    val->_date.date++;
    val->_date.time -= 86400;
  }

  val->_date.time = (val->_date.time * 1000) + date->msec;
  val->type = T_DATE;
  
  return FALSE;
}

/*
struct tm
      {
              int     tm_sec;
              int     tm_min;
              int     tm_hour;
              int     tm_mday;         quantième du mois
              int     tm_mon;
              int     tm_year;
              int     tm_wday;         jour de la semaine
              int     tm_yday;         jour de l'année
              int     tm_isdst;        décalage horaire
      };
*/

static int get_current_year(void)
{
  struct tm *tm;
  struct timeval tv;

  if (gettimeofday(&tv, NULL) != 0)
    THROW(E_DATE);

  tm = localtime((time_t *)&tv.tv_sec);
  return tm->tm_year + 1900;
}


PUBLIC void DATE_from_time(time_t time, long usec, VALUE *val)
{
  struct tm *tm;
  DATE_SERIAL date;

  tm = localtime(&time);

  date.year = tm->tm_year + 1900;
  date.month = tm->tm_mon + 1;
  date.day = tm->tm_mday;
  date.hour = tm->tm_hour;
  date.min = tm->tm_min;
  date.sec = tm->tm_sec;
  date.msec = usec / 1000;

  if (DATE_make(&date, val))
    val->type = T_NULL;
}


PUBLIC void DATE_now(VALUE *val)
{
  struct timeval tv;

  if (gettimeofday(&tv, NULL) != 0)
    val->type = T_NULL;
  else
    DATE_from_time((time_t)tv.tv_sec, tv.tv_usec, val);
}


PUBLIC int DATE_to_string(char *buffer, VALUE *value)
{
  DATE_SERIAL *date;
  int len;

  date = DATE_split(value);

  if (value->_date.date == 0)
    len = sprintf(buffer,"%02d:%02d:%02d", date->hour, date->min, date->sec);
  else if ((date->hour | date->min | date->sec | date->msec) == 0)
    len = sprintf(buffer,"%02d/%02d/%04d", date->month, date->day, date->year);
  else
    len = sprintf(buffer,"%02d/%02d/%04d %02d:%02d:%02d", date->month, date->day, date->year, date->hour, date->min, date->sec);
  
  if (date->msec)
  {
    len += sprintf(&buffer[len], ".%03d", date->msec);
    while (buffer[len - 1] == '0')
      len--;
    buffer[len] = 0;
  }
  
  return len;
}


static boolean read_integer(int *number)
{
  int nbr = 0;
  int nbr2;
  int c;
  boolean minus = FALSE;

  c = get_char();

  if (c == '-')
  {
    minus = TRUE;
    c = get_char();
  }
  else if (c == '+')
    c = get_char();

  if ((c < 0) || !isdigit(c))
    return TRUE;

  for(;;)
  {
    nbr2 = nbr * 10 + (c - '0');
    if (nbr2 < nbr)
      return TRUE;
    nbr = nbr2;

    c = look_char();
    if ((c < 0) || !isdigit(c))
      break;

    buffer_pos++;
  }

  *number = nbr;
  return FALSE;
}


static boolean read_msec(int *number)
{
  int nbr = 0;
  int nbr2;
  int c;
  int i;

  c = get_char();

  if ((c < 0) || !isdigit(c))
    return TRUE;

  i = 0;
  for(;;)
  {
    i++;
    nbr2 = nbr * 10 + (c - '0');
    if (nbr2 < nbr)
      return TRUE;
    nbr = nbr2;
    
    if (i == 3)
      break;

    c = look_char();
    if ((c < 0) || !isdigit(c))
      break;

    buffer_pos++;
  }
  
  for (; i < 3; i++)
    nbr *= 10;

  *number = nbr;
  return FALSE;
}


static void set_date(DATE_SERIAL *date, int which, int value)
{
  if (which == LO_YEAR)
  {
    if (value >= 0 && value <= 99)
    {
      if (value > 30)
        value += 1900;
      else
        value += 2000;
    }
    date->year = value;
  }
  else if (which == LO_MONTH)
    date->month = value;
  else if (which == LO_DAY)
    date->day = value;
}


static void set_time(DATE_SERIAL *date, int which, int value)
{
  if (which == LO_HOUR)
    date->hour = value;
  else if (which == LO_MINUTE)
    date->min = value;
  else if (which == LO_SECOND)
    date->sec = value;
}


PUBLIC boolean DATE_from_string(const char *str, long len, VALUE *val, boolean local)
{
  DATE_SERIAL date;
  LOCAL_INFO *info = LOCAL_get(local);
  int nbr, nbr2;
  int c, i;
  boolean has_date = FALSE;
  boolean has_time = FALSE;

  CLEAR(&date);

  buffer_init(str, len);
  jump_space();

  if (read_integer(&nbr))
    return TRUE;

  c = get_char();

  if (c == info->date_sep)
  {
    has_date = TRUE;

    if (read_integer(&nbr2))
      return TRUE;

    c = get_char();

    if ((c < 0) || isspace(c))
    {
      i = 0;

      set_date(&date, LO_YEAR, get_current_year());

      if (info->date_order[i] == LO_YEAR) i++;
      set_date(&date, info->date_order[i], nbr); i++;

      if (info->date_order[i] == LO_YEAR) i++;
      set_date(&date, info->date_order[i], nbr2);
    }
    else if (c == info->date_sep)
    {
      set_date(&date, info->date_order[0], nbr);
      set_date(&date, info->date_order[1], nbr2);

      if (read_integer(&nbr))
        return TRUE;

      set_date(&date, info->date_order[2], nbr);
    }

    jump_space();

    c = look_char();
    if (c < 0)
      goto _OK;

    if (read_integer(&nbr))
      return TRUE;

    c = get_char();
  }

  if (c == info->time_sep)
  {
    has_time = TRUE;

    if (read_integer(&nbr2))
      return TRUE;

    c = get_char();

    if ((c < 0) || isspace(c))
    {
      i = 0;

      if (info->time_order[i] == LO_SECOND) i++;
      set_time(&date, info->time_order[i], nbr); i++;

      if (info->time_order[i] == LO_SECOND) i++;
      set_time(&date, info->time_order[i], nbr2);
    }
    else if (c == info->time_sep)
    {
      set_time(&date, info->time_order[0], nbr);
      set_time(&date, info->time_order[1], nbr2);

      if (read_integer(&nbr))
        return TRUE;

      set_time(&date, info->time_order[2], nbr);
    
      c = get_char();    
      if (c == '.') // msec separator
      {
        if (read_msec(&nbr))
          return TRUE;
        date.msec = nbr;
      }
    }

    c = get_char();
    if ((c < 0) || isspace(c))
      goto _OK;
  }

  return TRUE;

_OK:

  if (DATE_make(&date, val))
    return TRUE;

  if (!has_date)
    val->_date.date = 0;

  return FALSE;
}


PUBLIC int DATE_comp(DATE *date1, DATE *date2)
{
  if (date1->date < date2->date)
    return (-1);

  if (date1->date > date2->date)
    return 1;

  if (date1->time < date2->time)
    return (-1);

  if (date1->time > date2->time)
    return 1;

  return 0;
}


PUBLIC int DATE_comp_value(VALUE *date1, VALUE *date2)
{
  return DATE_comp((DATE *)&date1->_date.date, (DATE *)&date2->_date.date);
}


PUBLIC bool DATE_timer(double *result, int from_start)
{
  struct timeval tv;

  *result = 0;

  if (gettimeofday(&tv, NULL) == 0)
  {
    *result = (double)tv.tv_sec + (double)tv.tv_usec / 1E6;
    if (from_start)
      *result -= _start_time;
    return FALSE;
  }
  else
    return TRUE;
}

Generated by  Doxygen 1.6.0   Back to index