// Module:  Log4CPLUS
// File:    timehelper.cxx
// Created: 4/2003
// Author:  Tad E. Smith
//
//
// Copyright (C) Tad E. Smith  All rights reserved.
//
// This software is published under the terms of the Apache Software
// License version 1.1, a copy of which has been included with this
// distribution in the LICENSE.APL file.
//
// $Log: timehelper.cxx,v $
// Revision 1.7  2004/01/29 07:27:54  tcsmith
// Fixed Bug #805203 - The getFormattedTime() method now pads the time string with
// leading zeros where needed.
//
// Revision 1.6  2003/08/05 09:20:08  tcsmith
// Modified the getFormattedTime() method to increase performance.
//
// Revision 1.5  2003/06/29 16:36:10  tcsmith
// Removed the setTime(long) method.
//
// Revision 1.4  2003/06/27 15:45:51  tcsmith
// Removed getMillis() method.
//
// Revision 1.3  2003/06/23 20:54:33  tcsmith
// Added mutliplication and division operator implementations for the Time
// class.
//
// Revision 1.2  2003/06/12 23:10:54  tcsmith
// Added the Time class implementation.
//
// Revision 1.1  2003/06/04 18:54:31  tcsmith
// Renamed strftime.cxx to timehelper.cxx
//
// Revision 1.2  2003/04/19 23:51:17  tcsmith
// Now call the strftime() function in the global namespace.
//
// Revision 1.1  2003/04/19 22:55:27  tcsmith
// Initial version.
//

#include <log4cplus/helpers/timehelper.h>
#include <log4cplus/streams.h>
#include <log4cplus/helpers/stringhelper.h>

#include <iomanip>

#if defined(HAVE_FTIME)
#include <sys/timeb.h>
#endif

#if defined(HAVE_GETTIMEOFDAY)
#include <sys/time.h>
#endif

#if defined(HAVE_GMTIME_R) && !defined(LOG4CPLUS_SINGLE_THREADED)
#define LOG4CPLUS_NEED_GMTIME_R
#endif

#if defined(HAVE_LOCALTIME_R) && !defined(LOG4CPLUS_SINGLE_THREADED)
#define LOG4CPLUS_NEED_LOCALTIME_R
#endif


#define BUFFER_SIZE 40
#define ONE_SEC_IN_USEC 1000000


using namespace std;
using namespace log4cplus;
using namespace log4cplus::helpers;



//////////////////////////////////////////////////////////////////////////////
// Time ctors
//////////////////////////////////////////////////////////////////////////////

Time::Time()
: tv_sec(0),
  tv_usec(0)
{
}




Time::Time(long tv_sec, long tv_usec)
: tv_sec(tv_sec),
  tv_usec(tv_usec)
{
}




Time::Time(time_t time)
: tv_sec(time),
  tv_usec(0)
{
}



Time
Time::gettimeofday()
{
#if defined(HAVE_GETTIMEOFDAY)
    timeval tp;
    ::gettimeofday(&tp, 0);

    return Time(tp.tv_sec, tp.tv_usec);
#elif defined(HAVE_FTIME)
    struct timeb tp;
    ::ftime(&tp);

    return Time(tp.time, tp.millitm * 1000);
#else
#warning "Time::gettimeofday()- low resolution timer: gettimeofday and ftime unavailable"
    return Time(::time(0), 0);
#endif
}




//////////////////////////////////////////////////////////////////////////////
// Time methods
//////////////////////////////////////////////////////////////////////////////

int
Time::setTime(struct tm* t)
{
    time_t time = ::mktime(t);
    if(time != -1) {
        tv_sec = time;
    }

    return time;
}



time_t
Time::getTime() const
{
    return tv_sec;
}



void
Time::gmtime(struct tm* t) const
{
    time_t clock = tv_sec;
#ifdef LOG4CPLUS_NEED_GMTIME_R
    ::gmtime_r(&clock, t);
#else
    struct tm* tmp = ::gmtime(&clock);
    *t = *tmp;
#endif
}



void
Time::localtime(struct tm* t) const
{
    time_t clock = tv_sec;
#ifdef LOG4CPLUS_NEED_LOCALTIME_R
    ::localtime_r(&clock, t);
#else
    struct tm* tmp = ::localtime(&clock);
    *t = *tmp;
#endif
}



log4cplus::tstring
Time::getFormattedTime(const log4cplus::tstring& fmt, bool use_gmtime) const
{
    tchar buffer[BUFFER_SIZE];
    struct tm time;

    if(use_gmtime) {
        gmtime(&time);
    }
    else {
        localtime(&time);
    }

#ifdef UNICODE
    size_t len = ::wcsftime(buffer, BUFFER_SIZE, fmt.c_str(), &time);
#else
    size_t len = ::strftime(buffer, BUFFER_SIZE, fmt.c_str(), &time);
#endif

    buffer[len] = '\0';
    tstring ret(buffer);

    size_t pos = ret.find( LOG4CPLUS_TEXT("%q") );
    if(pos != tstring::npos) {
        tstring tmp(ret.substr(0, pos));
        tstring seconds( convertIntegerToString((tv_usec / 1000)) );
        switch(seconds.length()) {
            case 1: tmp += LOG4CPLUS_TEXT("00"); break;
            case 2: tmp += LOG4CPLUS_TEXT("0"); break;
        }
        tmp += seconds;
        tmp += ret.substr(pos + 2);
        ret = tmp;
    }

    pos = ret.find( LOG4CPLUS_TEXT("%Q") );
    if(pos != tstring::npos) {
        tstring tmp(ret.substr(0, pos));
        tstring seconds( convertIntegerToString((tv_usec / 1000)) );
        switch(seconds.length()) {
            case 1: tmp += LOG4CPLUS_TEXT("00"); break;
            case 2: tmp += LOG4CPLUS_TEXT("0"); break;
        }
        tmp += seconds;
#if defined(HAVE_GETTIMEOFDAY)
        tstring usecs( convertIntegerToString((tv_usec % 1000)) );
        switch(usecs.length()) {
            case 1: tmp += LOG4CPLUS_TEXT(".00"); break;
            case 2: tmp += LOG4CPLUS_TEXT(".0"); break;
            case 3: tmp += LOG4CPLUS_TEXT("."); break;
        }
        tmp += usecs;
#endif
        tmp += ret.substr(pos + 2);
        ret = tmp;
    }

    return ret;
}



Time&
Time::operator+=(const Time& rhs)
{
    tv_sec += rhs.tv_sec;
    tv_usec += rhs.tv_usec;

    if(tv_usec > ONE_SEC_IN_USEC) {
        ++tv_sec;
        tv_usec -= ONE_SEC_IN_USEC;
    }

    return *this;
}



Time&
Time::operator-=(const Time& rhs)
{
    tv_sec -= rhs.tv_sec;
    tv_usec -= rhs.tv_usec;

    if(tv_usec < 0) {
        --tv_sec;
        tv_usec += ONE_SEC_IN_USEC;
    }

    return *this;
}



Time&
Time::operator/=(long rhs)
{
    long rem_secs = tv_sec % rhs;
    tv_sec /= rhs;
    
    tv_usec /= rhs;
    tv_usec += ((rem_secs * ONE_SEC_IN_USEC) / rhs);

    return *this;
}



Time&
Time::operator*=(long rhs)
{
    long new_usec = tv_usec * rhs;
    long overflow_sec = new_usec / ONE_SEC_IN_USEC;
    tv_usec = new_usec % ONE_SEC_IN_USEC;

    tv_sec *= rhs;
    tv_sec += overflow_sec;

    return *this;
}





//////////////////////////////////////////////////////////////////////////////
// Time globals
//////////////////////////////////////////////////////////////////////////////


const Time
operator+(const Time& lhs, const Time& rhs)
{
    return Time(lhs) += rhs;
}



const Time
operator-(const Time& lhs, const Time& rhs)
{
    return Time(lhs) -= rhs;
}



const Time
operator/(const Time& lhs, long rhs)
{
    return Time(lhs) /= rhs;
}



const Time
operator*(const Time& lhs, long rhs)
{
    return Time(lhs) *= rhs;
}



bool
operator<(const Time& lhs, const Time& rhs)
{
    return (   (lhs.sec() < rhs.sec())
            || (   (lhs.sec() == rhs.sec()) 
                && (lhs.usec() < rhs.usec())) );
}



bool
operator<=(const Time& lhs, const Time& rhs)
{
    return ((lhs < rhs) || (lhs == rhs));
}



bool
operator>(const Time& lhs, const Time& rhs)
{
    return (   (lhs.sec() > rhs.sec())
            || (   (lhs.sec() == rhs.sec()) 
                && (lhs.usec() > rhs.usec())) );
}



bool
operator>=(const Time& lhs, const Time& rhs)
{
    return ((lhs > rhs) || (lhs == rhs));
}



bool
operator==(const Time& lhs, const Time& rhs)
{
    return (   lhs.sec() == rhs.sec()
            && lhs.usec() == rhs.usec());
}



bool
operator!=(const Time& lhs, const Time& rhs)
{
    return !(lhs == rhs);
}




syntax highlighted by Code2HTML, v. 0.9.1