2005/5/9

     
 

SysDate.cpp

artefaktur
// -*- mode:C++; tab-width:2; c-basic-offset:2; indent-tabs-mode:nil -*- 
//
// Copyright (C) 2000-2005 by Roger Rene Kommer / artefaktur, Kassel, Germany.
// 
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public License (LGPL).
// 
// 
// This library 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 
// License ACDK-FreeLicense document enclosed in the distribution
// for more for more details.
// This file is part of the Artefaktur Component Development Kit:
//                         ACDK
// 
// Please refer to
// - http://www.acdk.de
// - http://www.artefaktur.com
// - http://acdk.sourceforge.net
// for more information.
// 
// $Header: /cvsroot/acdk/acdk/acdk_core/src/acdk/util/SysDate.cpp,v 1.19 2005/03/08 12:45:46 kommer Exp $



#include <acdk.h>
#include <stdio.h>
#include <acdk/util/SysDate.h>
#include "GregorianCalendar.h"
#include <time.h>
#if defined(ACDK_OS_LINUX)
# include <sys/time.h>
#endif
/FONT>
#ifdef ACDK_OS_WIN32
# include <windows.h>
#endif
/FONT>

#include <acdk/lang/UnsupportedOperationException.h>
#include <acdk/lang/IllegalArgumentException.h>


namespace acdk {
namespace util {



#define td_milliseconds(val) (jlong(1000) * jlong(val))
#define td_seconds(val) td_milliseconds(jlong(val) * jlong(1000))
#define td_minutes(val) td_seconds(jlong(val) * jlong(60))
#define td_hours(val) td_minutes(jlong(val) * jlong(60))
#define td_days(val) td_hours(jlong(val) * jlong(24))
#define td_weeks(val) td_days(jlong(val) * jlong(7))

#if defined(ACDK_OS_WIN32)


struct timezone 
{
  int tz_minuteswest;
  int tz_dsttime;
};

struct timeval {
        long    tv_sec;         /* seconds */
        long    tv_usec;        /* and microseconds */
};


jlong TIME_FACTOR = JLONG_CONSTANT(0x19db1ded53ea710);
jlong NSPERSEC = 10000000;


jlong
mm_to_clock_t(FILETIME* src, bool flag)
{
  jlong total = ((jlong) src->dwHighDateTime << 32) + (src->dwLowDateTime);
  if (flag == true)
    total -= TIME_FACTOR;
  total /= (jlong) (NSPERSEC / CLOCKS_PER_SEC);
  return total;
}

void
mm_totimeval(struct timeval *dst, FILETIME *src, int sub, bool flag)
{
  jlong x = mm_to_clock_t(src, flag);

  x *= (int) (1e6) / CLOCKS_PER_SEC; 
  x -= (jlong) sub * (int) (1e6);

  dst->tv_usec = long(x % (jlong) (1e6)); 
  dst->tv_sec = long(x / (jlong) (1e6));
}

int
mm_gettimeofday(struct timeval* p, 
                struct timezone* z)
{
  int res = 0;
  DWORD tzid;
  TIME_ZONE_INFORMATION tz;
  LONG bias;
  tzid = GetTimeZoneInformation (&tz);
  if (tzid == TIME_ZONE_ID_INVALID)
    res = -1;
  if (tzid == TIME_ZONE_ID_DAYLIGHT)
    bias = tz.Bias + tz.DaylightBias;
  else
    bias = tz.Bias + tz.StandardBias;
  
  if (p != NULL) {
    SYSTEMTIME t;
    FILETIME f;
    GetSystemTime(&t);
    if (SystemTimeToFileTime(&t, &f) == FALSE)
      res = -1;
    mm_totimeval(p, &f, 0, 1);
  }
  
  if (z != NULL) {
    z->tz_minuteswest = bias;
    z->tz_dsttime = (tzid == TIME_ZONE_ID_DAYLIGHT);
  }
  return res;
}

const jlong TIMETOFILETIMEOFFSET = JLONG_CONSTANT(0x019db1ded53e8000);
                                                  
const jlong FILETIMEMILLISECTICS = JLONG_CONSTANT(10000);

jlong fileTimeToLong(const FILETIME& ft)
{
  LARGE_INTEGER li;
  li.LowPart = ft.dwLowDateTime;
  li.HighPart = ft.dwHighDateTime;
  return li.QuadPart;
}

FILETIME longToFileTime(jlong l)
{
  LARGE_INTEGER li;
  li.QuadPart = l;
  FILETIME ft;
  ft.dwLowDateTime = li.LowPart;
  ft.dwHighDateTime = li.HighPart;
  return ft;
}

//static 
jlong 
SysDate::fileTimeToTime(const FILETIME& ft)
{
  return (fileTimeToLong(ft) - TIMETOFILETIMEOFFSET) / FILETIMEMILLISECTICS;
}

//static 
void 
SysDate::timeToFileTime(jlong t, FILETIME& ft)
{
  ft = longToFileTime((t * FILETIMEMILLISECTICS) + TIMETOFILETIMEOFFSET);
}

#else
/FONT>
# define mm_gettimeofday gettimeofday
#endif //defined(ACDK_OS_WIN32) 


SysDate::SysDate()
: acdk::lang::Object(),
  _seconds(0),
  _useconds(0)
{
  struct timeval time;
  mm_gettimeofday(&time, 0);
  _seconds = time.tv_sec;
  _useconds = time.tv_usec;
  _isSyncTime = true;
  memset(&_fields, 0, sizeof(struct tm));
  _isSyncFields = false;
}


SysDate::SysDate(jlong date)
  : acdk::lang::Object(),
    _seconds(0),
    _useconds(0)
{
  _seconds  = date / jlong(1000);
  _useconds = (date % jlong(1000)) * 1000;
  _isSyncTime = true;
  memset(&_fields, 0, sizeof(struct tm));
  _isSyncFields = false;
}

SysDate::SysDate(IN(RString) s, IN(RString) format)
  : acdk::lang::Object(),
    _seconds(0),
    _useconds(0)
{
  memset(&_fields, 0, sizeof(struct tm));
  _seconds = SysDate::parse(s, format);
  _isSyncFields = false;
  _isSyncTime = true;
}



SysDate::SysDate(int year, int month, int day)
  : acdk::lang::Object(),
    _seconds(0),
    _useconds(0)
{
  memset(&_fields, 0, sizeof(struct tm));
  _fields.tm_sec = 0;
  _fields.tm_min = 0;
  _fields.tm_hour = 0;
  _fields.tm_mon = month;
  _fields.tm_mday = day;
  _fields.tm_year = year - 1900;
  _isSyncFields = true;
  _isSyncTime = false;
}

SysDate::SysDate(int year, int month, int day, int hour, int min)
  : acdk::lang::Object(),
    _seconds(0),
    _useconds(0)
{
  memset(&_fields, 0, sizeof(struct tm));
  _fields.tm_sec = 0;
  _fields.tm_min = min;
  _fields.tm_hour = hour;
  _fields.tm_mon = month;
  _fields.tm_mday = day;
  _fields.tm_year = year - 1900;
  _isSyncFields = true;
  _isSyncTime = false;
}
 
SysDate::SysDate(int year, int month, int day, int hour, int min, int sec)
  : acdk::lang::Object(),  
    _seconds(0),
    _useconds(0)
{
  memset(&_fields, 0, sizeof(struct tm));
  _fields.tm_sec = sec;
  _fields.tm_min = min;
  _fields.tm_hour = hour;
  _fields.tm_mon = month;
  _fields.tm_mday = day;
  _fields.tm_year = year - 1900;
  _isSyncFields = true;
  _isSyncTime = false;
}


//virtual 
int
SysDate::compareTo(IN(acdk::lang::Object) o)
{
  if (instanceof(o, SysDate) == false)
    THROW1(ClassCastException, "SysDate::compareTo(acdk::lang::Object o)");
  return getTime() - RSysDate(o)->getTime() > 0 ? -1 : getTime() - RSysDate(o)->getTime() < 0 ? 1 : 0;
}

//virtual 
bool 
SysDate::equals(IN(acdk::lang::Object) obj)
{
  if (instanceof(obj, SysDate) == false)
    return false;
  return getTime() == RSysDate(obj)->getTime();
}

//virtual 
RString 
SysDate::toString()
{
  time_t ut = time_t(getSecs());
  struct tm* t = localtime(&ut);
  if (t == 0) 
    return "Time not compatible or before January 1, 1900";
  char buffer[256];
  //strftime(buffer, 256, "%a %b %d %H:%M:%S %Y", t);
  strftime(buffer, 256, "%Y-%m-%dT%H:%M:%S", t);
  return new String(buffer, NormalSST | CCAscii);
}

//static
jlong
SysDate::UTC(int year, int month, int day, int hour, int min, int sec)
{
  THROW0(UnsupportedOperationException);
  return -1;
}


//static
jlong
SysDate::parse(IN(RString) s, IN(RString) format)
{
  const char *frmt;
  const char *str = s->c_str();
  if (format == Nil)
  {
    // millis are added below!
    frmt = "%Y-%m-%d_%H-%M-%S";
  } else {
    frmt = format->c_str();
  }
  int str_idx = 0;
  int str_len = strlen(str);
  int frmt_idx = 0;
  int frmt_len = strlen(frmt);
  struct tm tf;
  memset(&tf, 0, sizeof(struct tm));

  while (frmt_idx < frmt_len)
  {
    if (frmt[frmt_idx] == '%')
    {
      // match fields ...
      frmt_idx++;
      switch (frmt[frmt_idx])
      {
        case 'Y':
          // Year
          if (sscanf(str + str_idx, "%4d", &tf.tm_year) != 1)
            THROW1(IllegalArgumentException, RString("Error in %Y field"));
          tf.tm_year -= 1900;
          str_idx += 4;
          break;
        case 'm':
          // Month
          if (sscanf(str + str_idx, "%2d", &tf.tm_mon) != 1)
            THROW1(IllegalArgumentException, RString("Error in %m field"));
          str_idx += 2;
          break;
        case 'd':
          // Day
          if (sscanf(str + str_idx, "%2d", &tf.tm_mday) != 1)
            THROW1(IllegalArgumentException, RString("Error in %d field"));
          str_idx += 2;
          break;
        case 'H':
          // Hour (24)
          if (sscanf(str + str_idx, "%2d", &tf.tm_hour) != 1)
            THROW1(IllegalArgumentException, RString("Error in %H field"));
          str_idx += 2;
          break;
        case 'I':
          // Hour (12)
          if (sscanf(str + str_idx, "%2d", &tf.tm_hour) != 1)
            THROW1(IllegalArgumentException, RString("Error in %I field"));
          str_idx += 2;
          break;
        case 'p':
          // AM/PM
          char stamp[16];
          if (sscanf(str + str_idx, "%2s", stamp) != 1)
            THROW1(IllegalArgumentException, RString("Error in %p field"));
          str_idx += 2;
          if (strcmp(stamp,"AM") == 0)
            break;
          if (strcmp(stamp,"PM") == 0)
          {
            tf.tm_hour += 12;
            break;
          }
            THROW1(IllegalArgumentException, RString("Error in %p field"));
        case 'M':
          // Minute
          if (sscanf(str + str_idx, "%2d", &tf.tm_min) != 1)
            THROW1(IllegalArgumentException, RString("Error in %M field"));
          str_idx += 2;
          break;
        case 'S':
          // second
          if (sscanf(str + str_idx, "%2d", &tf.tm_sec) != 1)
            THROW1(IllegalArgumentException, RString("Error in %S field"));
          str_idx += 2;
          break;
        case 'j':
          // second
          if (sscanf(str + str_idx, "%3d", &tf.tm_yday) != 1)
            THROW1(IllegalArgumentException, RString("Error in %j field"));
          str_idx += 3;
          break;
        case 'w':
          // second
          if (sscanf(str + str_idx, "%1d", &tf.tm_wday) != 1)
            THROW1(IllegalArgumentException, RString("Error in %w field"));
          str_idx += 1;
          break;
        default:
          THROW1(IllegalArgumentException, "Option %" + String::valueOf(frmt[frmt_idx]) + " is not Supported!");
      } // case option_char
    } // if match %option_char
    else
    {
      // match normal chars
      if (str[str_idx] != frmt[frmt_idx])
      {
        // oops missmatch
        THROW1(IllegalArgumentException, "Error matching character " + String::valueOf(frmt_idx) +
                                         " of Format-String (" + frmt + ")");
      }
      str_idx++;
    } // else match normal chars
    frmt_idx++;
  } // while
  jlong time = mktime(&tf);
  if (time == -1)
  {
    THROW1(Exception, "Sorry invalide Date or < 1970-01-01");
  }

  // Millibased Systime is returned
  return jlong(1000) * time;
}


int
SysDate::getDate()
{
  return _fields.tm_mday;
}

int
SysDate::getDay()
{
  syncFields();
  return _fields.tm_mday;
}

int
SysDate::getHours()
{
  syncFields();
  return _fields.tm_hour;
}

int
SysDate::getMinutes()
{
  syncFields();
  return _fields.tm_min;
}

int
SysDate::getMonth()
{
  syncFields();
  return _fields.tm_mon;
}

int
SysDate::getSeconds()
{
  syncFields();
  return _fields.tm_sec;
}

int
SysDate::getTimezoneOffset()
{
  return 0;
}

int
SysDate::getYear()
{
  syncFields();
  return _fields.tm_year;
}

void
SysDate::setDate(int date)
{
  _fields.tm_mday = date;
  _isSyncTime = false;
}

void
SysDate::setDay(int day)
{
  _fields.tm_mday = day;
  _isSyncTime = false;
}

void
SysDate::setHours(int hours)
{
  _fields.tm_hour = hours;
  _isSyncTime = false;
}

void
SysDate::setMinutes(int minutes)
{
  _fields.tm_min = minutes;
  _isSyncTime = false;
}

void
SysDate::setMonth(int month)
{
  _fields.tm_mon = month;
  _isSyncTime = false;
}

void
SysDate::setSeconds(int seconds)
{
  _fields.tm_sec = seconds;
  _isSyncTime = false;
}

void
SysDate::setYear(int year)
{
  _fields.tm_year = year - 1900;
  _isSyncTime = false;
}

RString
SysDate::toGMTString()
{
  THROW0(UnsupportedOperationException);
  return Nil;
}

RString
SysDate::toLocaleString()
{
  THROW0(UnsupportedOperationException);
  return toString();
}

RString 
SysDate::getTimeStamp(IN(RString) format)
{
  const char* frmt;
  if (format == Nil)
  {
    // millis are added below!
    frmt = "%Y-%m-%d_%H-%M-%S";
  } else {
    frmt = format->c_str();
  }
  time_t ut = time_t(getSecs());
  struct tm* t = localtime(&ut);
  if (t == 0) 
    return "Time not compatible or before January 1, 1900";
  char buffer[256];
  strftime(buffer, 256, frmt, t);

  // The default format has millis!
  if (format == Nil)
  {
    char* ptr = buffer + strlen(buffer);
    sprintf(ptr,"_%03d", (int)(getUSecs() / 1000));
  }
  return new String(buffer, NormalSST | CCAscii);
}

//static 
jlong 
SysDate::getTickCount()
{
  return SysDate().getTime();
}

//protected
void
SysDate::syncFields()
{
  if (_isSyncFields == false)
  {
    time_t ut = time_t(getSecs());
    struct tm* t = localtime(&ut);
    memcpy(&_fields, t, sizeof(struct tm));
    _isSyncFields = true;
  }
}

void
SysDate::syncTime()
{
  if (_isSyncTime == false)
  {
    _seconds  = mktime(&_fields);
    _useconds = 0;
    if (_seconds == -1)
    {
      THROW1(Exception, "Sorry invalide Date or < 1970-01-01");
    }
    _isSyncTime = true;
  }
}


} // util
} // acdk