/** \file
 *
 *  Contains the Timestamp and Timespan class implementations.
 *
 *  Copyright (c) 2007,2008,2009 MBARI
 *  MBARI Proprietary Information.  All Rights Reserved
 */

#include "Timestamp.h"

#include <ctype.h>
#include <stdlib.h>
#include <math.h>
#include <pthread.h> // for mingw
#include <sys/time.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>

#include "utils/AuvMath.h"
#include "utils/Str.h"

#ifdef __FASTSIM
Timespan Timestamp::TimeOffset_( 0.0 );
#endif

/// Constant value to represent "no time".
/// Set to \> 1 year before beginning of (1970 Jan 1) epoch,
/// so if clock resets, uninitialized items still have an "old" timestamp.
const Timestamp Timestamp::NOT_SET_TIME( -32445540.0 );

/// Constant value to represent beginning of (1970 Jan 1) epoch
const Timestamp Timestamp::EPOCH_START_TIME( 0.0 );

/// Constructor, set from seconds
Timestamp::Timestamp( const double& seconds )
    : timeval_( fromDouble( seconds ) )
{}

/// Constructor, set time from struct timeval
Timestamp::Timestamp( const struct timeval& timeval )
    : timeval_( timeval )
{}

/// Constructor, set time from separate seconds and microseconds
Timestamp::Timestamp( const int seconds, const unsigned int microseconds )
    : timeval_( fromIntegers( seconds, microseconds ) )
{}

/// Constructor, set time from separate date and time integers
Timestamp::Timestamp( const int year, const int month, const int day,
                      const int hour, const int minute, const int second )
    : timeval_( fromIntegers( year, month, day,
                              hour, minute, second ) )
{}

/// Constructor, set time from ISO9601 string
Timestamp::Timestamp( const char* timeString )
    : timeval_( fromString( timeString ) )
{}

/// Tests:
/// -# Create new object copied from another object.  asDouble == asDouble
/// -# Create new object copied from another object.  Equal with == operator
Timestamp::Timestamp( const Timestamp& rhs )
    : timeval_( rhs.timeval_ )
{}

/// Named Constructor, set from seconds << 20 + microseconds
Timestamp Timestamp::FromMicros( const long long& micros )
{
    return Timestamp( TimevalFromMicros( micros ) );
}

#ifndef __suseconds_t
#define __suseconds_t unsigned long int
#endif

#ifndef __time_t
#define __time_t long int
#endif

/// add seconds << 20 + microseconds
void Timestamp::addFromMicros( const long long& micros )
{
    timeval_.tv_usec += static_cast< __suseconds_t >( micros & 0xFFFFF );
    timeval_.tv_sec += static_cast< __time_t >( micros >> 20 )
                       + timeval_.tv_usec / 1000000;
    timeval_.tv_usec %= 1000000;
}

/// add seconds << 10 + microseconds >> 10
void Timestamp::addFromMillis( const long long& millis )
{
    timeval_.tv_usec += static_cast< __suseconds_t >( ( millis & 0x3FF ) << 10 );
    timeval_.tv_sec += static_cast< __time_t >( millis >> 10 )
                       + timeval_.tv_usec / 1000000;
    timeval_.tv_usec %= 1000000;
}

/// Named Constructor, set from seconds << 10 + milliseconds >> 10
Timestamp Timestamp::FromMillis( const long long& millis )
{
    return Timestamp( TimevalFromMillis( millis ) );
}

// Round to the nearest #of seconds indicated.
// i.e., use 60 for nearest minute, 3600 for hour, etc
void Timestamp::round( int seconds )
{
    if( timeval_.tv_usec > 500 )
    {
        ++timeval_.tv_sec;
    }
    timeval_.tv_usec = 0;
    timeval_.tv_sec = ( ( timeval_.tv_sec + ( seconds / 2 ) ) / seconds ) * seconds;
}

/// Returns the time elapsed since this timestamp
Timespan Timestamp::elapsed() const
{
    return Now() - *this;
}

/// Accessor, convert to double
double Timestamp::asDouble() const
{
    return timeval_.tv_sec + ( timeval_.tv_usec / 1000000.0 );
}

/// Accessor, convert to float
double Timestamp::asFloat() const
{
    return timeval_.tv_sec + ( timeval_.tv_usec / 1000000.0f );
}


/// Accessor, convert to usec
long long Timestamp::asMicros() const
{
    return ( ( timeval_.tv_sec * 1000000LL ) + ( timeval_.tv_usec ) );
}


/// Accessor, convert to ms
long long Timestamp::asMillis() const
{
    return ( ( timeval_.tv_sec * 1000LL ) + ( timeval_.tv_usec / 1000LL ) );
}


/// Quickly returns the (seconds << 20) + microseconds
long long Timestamp::asCompactMicros() const
{
    return ( ( long long ) timeval_.tv_sec << 20 )
           + ( long long ) timeval_.tv_usec;
}

/// Quickly returns the (seconds << 10) + (microseconds >> 10)
long long Timestamp::asCompactMillis() const
{
    return ( ( long long ) timeval_.tv_sec << 10 )
           + ( ( long long ) timeval_.tv_usec >> 10 )
           + ( ( timeval_.tv_usec & 0x200 ) == 0x200 ? 1LL : 0LL );
}

/// Returns value as struct tm:
/// int tm_sec;         /* seconds */
/// int tm_min;         /* minutes */
/// int tm_hour;        /* hours */
/// int tm_mday;        /* day of the month */
/// int tm_mon;         /* month */
/// int tm_year;        /* year */
/// int tm_wday;        /* day of the week */
/// int tm_yday;        /* day in the year */
/// int tm_isdst;       /* daylight saving time */
struct tm Timestamp::asStructTm() const
{
    struct tm result;
    ( void )gmtime_r( &timeval_.tv_sec, &result ); // (void) cast for Windows.
    return result;
}

/// Copy operator.
///
/// Tests:
/// -# Create two objects with different double params.  Test they are
///   different.  Use copy to set one to the other.  Test they are same
///   with equality operator.
Timestamp& Timestamp::operator= ( const Timestamp & rhs )
{
    if( this != & rhs )
    {
        timeval_ = rhs.getTimeval();
    }
    return * this;
}

/// Set operator
///
/// Tests:
/// -# Create object with double param A.  Test asDouble is not equal to B.
///    Use operator to set to B.  Test asDouble is equal to B
/// -# Create two objects with different double params.  Test they are
///   different.  Use copy to set one to the other's asDouble.  Test they are same
///   with equality operator.
Timestamp& Timestamp::operator= ( const double & seconds )
{
    fromDouble( seconds );
    return * this;
}

/// Set operator
Timestamp& Timestamp::operator= ( const struct timeval & timeval )
{
    timeval_ = timeval;
    return * this;
}

/// Addition of a Timespan
///
/// Tests:
/// -# Create to time objects params A and Timespan B.  Sum objects A+=B.
///    Verify (A+B).asDouble() == (A.asDouble + B.asDouble )
Timestamp& Timestamp::operator+= ( const Timespan & rhs )
{
    timeval_.tv_usec += rhs.timeval_.tv_usec;
    timeval_.tv_sec += rhs.timeval_.tv_sec + timeval_.tv_usec / 1000000;
    timeval_.tv_usec %= 1000000;
    return * this;
}

/// Subtraction of a Timespan
///
/// Tests:
/// -# Create to time objects params A and Timespan B.  Sum objects A-=B.
///    Verify (A-B).asDouble() == (A.asDouble - B.asDouble )
Timestamp& Timestamp::operator-= ( const Timespan & rhs )
{
    timeval_.tv_usec += 1000000 - rhs.timeval_.tv_usec;
    timeval_.tv_sec -= rhs.timeval_.tv_sec - timeval_.tv_usec / 1000000 + 1;
    timeval_.tv_usec %= 1000000;
    return * this;
}

/// Standard addition/subtraction functions.
///
/// I do wonder if i could return a Timestamp object rather than a stack double.
///  Hmm...
///
/// Tests for each:
/// -# Create double A and B (A>B).  Create timeA, timeB and timespanB.
/// -# Verify timeA+timespanB = A+B
/// -# Verify timeA-timespanB = A-B
/// -# Verify timeA - timeB = A-B
/// -# Verify timeA + B = A+B
/// -# Verify timeA - B = A-B
/// -# Repeat above with B>A
Timestamp Timestamp::operator+ ( const Timespan& rhs ) const
{
    return Timestamp( timeval_ ) += rhs;
}

Timestamp Timestamp::operator- ( const Timespan& rhs ) const
{
    return Timestamp( timeval_ ) -= rhs;
}

Timespan Timestamp::operator- ( const Timestamp& rhs ) const
{
    return Timespan( timeval_ ) -= rhs.timeval_;
}

Timespan Timestamp::operator- ( const struct timeval& rhs ) const
{
    return Timespan( timeval_ ) -= rhs;
}

/// Comparison operator
///
bool Timestamp::operator== ( const Timestamp& rhs ) const
{
    // Actually not a huge fan of this algorithm.  Add the concept of
    // "acceptable error" or go to integer-based system?

    return timeval_.tv_sec == rhs.timeval_.tv_sec
           && timeval_.tv_usec == rhs.timeval_.tv_usec;
}

bool Timestamp::operator!= ( const Timestamp& rhs ) const
{
    // Actually not a huge fan of this algorithm.  Add the concept of
    // "acceptable error" or go to integer-based system?

    return timeval_.tv_sec != rhs.timeval_.tv_sec
           || timeval_.tv_usec != rhs.timeval_.tv_usec;
}

/// Inequality operator
///
/// Tests:
/// -# Generate two times A and B with A > B.  Verify A>B true and A<B false.
bool Timestamp::operator> ( const Timestamp& rhs ) const
{
    return timeval_.tv_sec > rhs.timeval_.tv_sec
           || ( timeval_.tv_sec == rhs.timeval_.tv_sec
                && timeval_.tv_usec > rhs.timeval_.tv_usec );
}
bool Timestamp::operator< ( const Timestamp& rhs ) const
{
    return timeval_.tv_sec < rhs.timeval_.tv_sec
           || ( timeval_.tv_sec == rhs.timeval_.tv_sec
                && timeval_.tv_usec < rhs.timeval_.tv_usec );
}
bool Timestamp::operator>= ( const Timestamp& rhs ) const
{
    return timeval_.tv_sec > rhs.timeval_.tv_sec
           || ( timeval_.tv_sec == rhs.timeval_.tv_sec
                && timeval_.tv_usec >= rhs.timeval_.tv_usec );
}
bool Timestamp::operator<= ( const Timestamp& rhs ) const
{
    return timeval_.tv_sec < rhs.timeval_.tv_sec
           || ( timeval_.tv_sec == rhs.timeval_.tv_sec
                && timeval_.tv_usec <= rhs.timeval_.tv_usec );
}


/// Set this object to current time
///
/// Tests:
/// -# First, check that it works (returns correctly)
/// -# Then, get time, sleep a period, and get time again.  Verify two times
///     are sleep period within some delta.
/// -# (Don't know how to test failure conditions)
Timestamp& Timestamp::setToCurrentTime( void )
{

    if( gettimeofday( & timeval_, NULL ) < 0 )
    {
        //error condition.  what to do?
    }

#ifdef __FASTSIM
    * this += TimeOffset_;
#endif

    return * this;
}

#ifdef __FASTSIM
Timestamp& Timestamp::setToRealCurrentTime( void )
{
    if( gettimeofday( & timeval_, NULL ) < 0 )
    {
        //error condition.  what to do?
    }
    return * this;
}
#endif

Timestamp& Timestamp::setToMonoTime( void )
{
    struct timespec monoTime;
    if( clock_gettime( CLOCK_MONOTONIC, &monoTime ) < 0 )
    {
        //error condition.  what to do?
    }

    fromTimespec( &monoTime );

    return * this;
}

/// Static function
Timestamp Timestamp::Now()
{
    return Timestamp().setToCurrentTime();
}

#ifdef __FASTSIM
Timespan Timestamp::SetNow( const Timestamp newNow )
{
    struct timeval nowTime;
    nowTime.tv_sec = nowTime.tv_usec = 0;
    if( gettimeofday( & nowTime, NULL ) < 0 )
    {
        //error condition.  what to do?
    }
    TimeOffset_ = newNow - nowTime;
    return TimeOffset_;
}
#endif

/// Quite a few ways to run this algorithm.  What's the most precise under
/// standard Linux
///
/// Tests:
/// -# None defined
void Timestamp::sleepTill( void )
{
    Timespan sleepDuration = operator- ( Timestamp::Now() );

    sleepDuration.sleepFor();
}

void Timestamp::toTimespec( struct timespec* out ) const
{
    if( out == NULL )
    {
        return ;
    }

    out->tv_sec = timeval_.tv_sec ;
    out->tv_nsec = timeval_.tv_usec * 1000;
}

void Timestamp::fromTimespec( const struct timespec* in )
{
    if( in == NULL )
    {
        return ;
    }

    timeval_.tv_sec = in->tv_sec;
    timeval_.tv_usec = in->tv_nsec / 1000;
}

//== Input/output functions ==

///
const char* Timestamp::toString( char* buf, int buflen, int precision ) const
{
    struct tm result;
    char subsecs_tr[ 9 ];
    precision = MAX( 0, MIN( precision, 6 ) );

    strftime( buf, buflen - precision - 1, "%Y-%m-%dT%H:%M:%S", gmtime_r( &timeval_.tv_sec, &result ) );

    if( precision > 0 )
    {
        snprintf( subsecs_tr, 9, "%.*f", precision, ( float )timeval_.tv_usec / 1000000.0f );
        strncat( buf, subsecs_tr + 1, precision + 1 );
    }

    strncat( buf, "Z", 1 );

    return buf;

}

const Str Timestamp::toString( int precision ) const
{
    char buf[28];
    toString( buf, 27, precision );
    return Str( buf );
}

/// Writes the Timestamp as a small Str (YYYYmmdd'T'HHMMSS).
const Str Timestamp::toSmallString() const
{
    char buf[16];
    struct tm result;
    strftime( buf, 16, "%Y%m%dT%H%M%S", gmtime_r( &timeval_.tv_sec, &result ) );
    return buf;
}

/// Writes the Timestamp as a smaller Str (YYYYmmddHHMM).
const Str Timestamp::toSmallerString() const
{
    char buf[16];
    struct tm result;
    strftime( buf, 16, "%Y%m%d%H%M", gmtime_r( &timeval_.tv_sec, &result ) );
    return buf;
}

/// Writes the Timestamp as a date-hour Str (YYYYmmddHH).
const Str Timestamp::toDateHourString() const
{
    char buf[14];
    struct tm result;
    strftime( buf, 14, "%Y%m%d%H", gmtime_r( &timeval_.tv_sec, &result ) );
    return buf;
}

struct timeval& Timestamp::fromDouble( const double& seconds )
{
    timeval_.tv_usec = static_cast< __suseconds_t >( ( ( ( seconds - trunc( seconds ) ) + 5e-7 * ( seconds < 0 ? -1 : 1 ) ) * 1e6 ) + 1000000 );
    timeval_.tv_sec = static_cast< __time_t >( ( seconds ) + timeval_.tv_usec / 1000000 - 1 );
    timeval_.tv_usec %= 1000000;
    return timeval_;
}

#ifdef WINNT
// Implement strptime under windows
static const char* kWeekFull[] =
{
    "Sunday", "Monday", "Tuesday", "Wednesday",
    "Thursday", "Friday", "Saturday"
};

static const char* kWeekAbbr[] =
{
    "Sun", "Mon", "Tue", "Wed",
    "Thu", "Fri", "Sat"
};

static const char* kMonthFull[] =
{
    "January", "February", "March", "April", "May", "June",
    "July", "August", "September", "October", "November", "December"
};

static const char* kMonthAbbr[] =
{
    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};

static const char* _parse_num( const char* s, int low, int high, int* value )
{
    const char* p = s;
    for( *value = 0; p != NULL && *p != 0 && isdigit( *p ); ++p )
    {
        *value = ( *value ) * 10 + static_cast<int>( *p ) - static_cast<int>( '0' );
    }

    if( p == s || *value < low || *value > high ) return NULL;
    return p;
}

static char* _strptime( const char *s, const char *format, struct tm *tm )
{
    while( format != NULL && *format != 0 && s != NULL && *s != 0 )
    {
        if( *format != '%' )
        {
            if( *s != *format ) return NULL;

            ++format;
            ++s;
            continue;
        }

        ++format;
        int len = 0;
        switch( *format )
        {
        // weekday name.
        case 'a':
        case 'A':
            tm->tm_wday = -1;
            for( int i = 0; i < 7; ++i )
            {
                len = static_cast<int>( strlen( kWeekAbbr[i] ) );
                if( strnicmp( kWeekAbbr[i], s, len ) == 0 )
                {
                    tm->tm_wday = i;
                    break;
                }

                len = static_cast<int>( strlen( kWeekFull[i] ) );
                if( strnicmp( kWeekFull[i], s, len ) == 0 )
                {
                    tm->tm_wday = i;
                    break;
                }
            }
            if( tm->tm_wday == -1 ) return NULL;
            s += len;
            break;

        // month name.
        case 'b':
        case 'B':
        case 'h':
            tm->tm_mon = -1;
            for( int i = 0; i < 12; ++i )
            {
                len = static_cast<int>( strlen( kMonthAbbr[i] ) );
                if( strnicmp( kMonthAbbr[i], s, len ) == 0 )
                {
                    tm->tm_mon = i;
                    break;
                }

                len = static_cast<int>( strlen( kMonthFull[i] ) );
                if( strnicmp( kMonthFull[i], s, len ) == 0 )
                {
                    tm->tm_mon = i;
                    break;
                }
            }
            if( tm->tm_mon == -1 ) return NULL;
            s += len;
            break;

        // month [1, 12].
        case 'm':
            s = _parse_num( s, 1, 12, &tm->tm_mon );
            if( s == NULL ) return NULL;
            --tm->tm_mon;
            break;

        // day [1, 31].
        case 'd':
        case 'e':
            s = _parse_num( s, 1, 31, &tm->tm_mday );
            if( s == NULL ) return NULL;
            break;

        // hour [0, 23].
        case 'H':
            s = _parse_num( s, 0, 23, &tm->tm_hour );
            if( s == NULL ) return NULL;
            break;

        // minute [0, 59]
        case 'M':
            s = _parse_num( s, 0, 59, &tm->tm_min );
            if( s == NULL ) return NULL;
            break;

        // seconds [0, 60]. 60 is for leap year.
        case 'S':
            s = _parse_num( s, 0, 60, &tm->tm_sec );
            if( s == NULL ) return NULL;
            break;

        // year [1900, 9999].
        case 'Y':
            s = _parse_num( s, 1900, 9999, &tm->tm_year );
            if( s == NULL ) return NULL;
            tm->tm_year -= 1900;
            break;

        // year [0, 99].
        case 'y':
            s = _parse_num( s, 0, 99, &tm->tm_year );
            if( s == NULL ) return NULL;
            if( tm->tm_year <= 68 )
            {
                tm->tm_year += 100;
            }
            break;

        // arbitrary whitespace.
        case 't':
        case 'n':
            while( isspace( *s ) ) ++s;
            break;

        // '%'.
        case '%':
            if( *s != '%' ) return NULL;
            ++s;
            break;

        // All the other format are not supported.
        default:
            return NULL;
        }
        ++format;
    }

    if( format != NULL && *format != 0 )
    {
        return NULL;
    }
    else
    {
        return const_cast<char*>( s );
    }
}

char* strptime( const char *buf, const char *fmt, struct tm *tm )
{
    return _strptime( buf, fmt, tm );
}

time_t timegm( struct tm *tm )
{
    time_t ret;
    char *tz;

    tz = getenv( "TZ" );
    //setenv("TZ", "", 1);
    tzset();
    ret = mktime( tm );
    if( tz )
        ;//setenv("TZ", tz, 1);
    else
        ;//unsetenv("TZ");
    tzset();
    return ret;
}

void nanosleep( const struct timespec* sleep, const struct timespec* remaining )
{
    long usecs = sleep->tv_sec * 1000000 + sleep->tv_nsec / 1000;
    usleep( usecs );
}

#endif  // WINNT

struct timeval& Timestamp::fromString( const char* timeString )
{
    struct tm result;
    unsigned int uMult = 100000;

    char* nextChar;
    char fmt[] = "%m-%d-%Y %H:%M:%S";

    bool hasDash = strchr( timeString, '-' ) != NULL;
    bool hasSlash = strchr( timeString, '/' ) != NULL;
    bool yearFirst = strlen( timeString ) > 3 &&
                     ( ( hasDash && timeString[4] == '-' ) || ( hasSlash && timeString[4] == '/' ) );
    bool hasT = strchr( timeString, 'T' ) != NULL;
    bool hasSpace = strchr( timeString, ' ' ) != NULL;
    bool hasColon = strchr( timeString, ':' ) != NULL ;

    const char* dateFmt = hasDash ? ( yearFirst ? "%Y-%m-%d" : "%m-%d-%Y" ) :
                          hasSlash ? ( yearFirst ? "%Y/%m/%d" : "%m/%d/%Y" ) : "%Y%m%d";

    const char* betweenFmt = hasT ? "T" : ( hasSpace ? " " : "" );

    const char* timeFmt = hasColon ? "%H:%M:%S" : "%H%M%S";

    snprintf( fmt, sizeof( fmt ), "%s%s%s", dateFmt, betweenFmt, timeFmt );

    nextChar = strptime( timeString, fmt, &result );

    if( NULL != nextChar )
    {
        timeval_.tv_sec = timegm( &result );
        timeval_.tv_usec = 0;
        if( *nextChar == '.' )
        {
            ++nextChar;
            for( ; *nextChar >= '0' && *nextChar <= '9'; ++nextChar )
            {
                timeval_.tv_usec += ( *nextChar - '0' ) * uMult;
                uMult /= 10;
            }
        }
    }
    else
    {
        *this = NOT_SET_TIME;
    }

    return timeval_;
}

struct timeval Timestamp::TimevalFromIntegers( const int seconds, const unsigned int microseconds )
{
    struct timeval timeval;
    timeval.tv_sec = static_cast< __time_t >( seconds );
    timeval.tv_usec = static_cast< __suseconds_t >( microseconds );
    return timeval;
}

struct timeval Timestamp::TimevalFromMicros( const long long& micros )
{
    struct timeval timeval;
    timeval.tv_sec = static_cast< __time_t >( micros >> 20 );
    timeval.tv_usec = static_cast< __suseconds_t >( micros & 0xFFFFF );
    return timeval;
}

struct timeval Timestamp::TimevalFromMillis( const long long& millis )
{
    struct timeval timeval;
    timeval.tv_sec = static_cast< __time_t >( millis >> 10 );
    timeval.tv_usec = static_cast< __suseconds_t >( ( millis & 0x3FF ) << 10 );
    return timeval;
}

struct timeval& Timestamp::fromIntegers( const int seconds, const unsigned int microseconds )
{
    timeval_.tv_sec = static_cast< __time_t >( seconds );
    timeval_.tv_usec = static_cast< __suseconds_t >( microseconds );
    return timeval_;
}

struct timeval& Timestamp::fromIntegers( const int year, const int month, const int day,
        const int hour, const int minute, const int second )
{
    struct tm result;
    result.tm_year = year - 1900;
    result.tm_mon = month - 1;
    result.tm_mday = day;
    result.tm_hour = hour;
    result.tm_min = minute;
    result.tm_sec = second;
    timeval_.tv_sec = timegm( &result );
    timeval_.tv_usec = 0;
    return timeval_;
}

//===================================================================

/// This can be represented exactly as a float.
const Timespan Timespan::INVALID_TIMESPAN( TimevalFromIntegers( -2147483647, 1000001 ) );

const Timespan Timespan::ZERO_TIMESPAN( 0.0 );

/// Tests:
/// -# Create a new object w/o params.  Expect asDouble() == 0
/// -# Create a new object with double param, Expect asDouble() == param
Timespan::Timespan( const double& seconds )
    : Timestamp( seconds )
{}

/// Constructor, set time from struct timeval
Timespan::Timespan( const struct timeval& timeval )
    : Timestamp( timeval )
{}

/// Tests:
/// -# Create new object copied from another object.  asDouble == asDouble
/// -# Create new object copied from another object.  Equal with == operator
Timespan::Timespan( const Timespan& rhs )
    : Timestamp( rhs )
{}

/// Named Constructor, set from seconds << 20 + microseconds
Timespan Timespan::FromMicros( const long long& micros )
{
    return Timespan( TimevalFromMicros( micros ) );
}

/// Named Constructor, set from seconds << 10 + milliseconds >> 10
Timespan Timespan::FromMillis( const long long& millis )
{
    return Timespan( TimevalFromMillis( millis ) );
}

/// Accessor, convert to double
double Timespan::asDouble() const
{
    if( *this == Timespan::INVALID_TIMESPAN )
    {
        return nan( "" );
    }
    return Timestamp::asDouble();
}

/// Accessor, convert to float
double Timespan::asFloat() const
{
    if( *this == Timespan::INVALID_TIMESPAN )
    {
        return nanf( "" );
    }
    return Timestamp::asFloat();
}

/// Sets the timespan using an XML-like duration string.
/// format is "P(\d+D)?(\d+H)?(\d+M)?(\d+(.\d+)?S)?"
/// returns true if parsed correctly, false otherwise
bool Timespan::setDuration( const char* durationCstr )
{
    // represets days, hours, minutes, seconds
    double seconds( 0.0 );
    if( !durationCstr || *durationCstr != 'P' )
    {
        return false;
    }
    ++durationCstr;
    while( *durationCstr )
    {
        double decimalDiv = 1;
        bool inDecimal = false;
        double currentNum = 0;
        while( ( *durationCstr >= '0' && *durationCstr <= '9' ) || *durationCstr == '.' )
        {
            if( *durationCstr == '.' )
            {
                inDecimal = true;
            }
            else
            {
                if( inDecimal )
                {
                    decimalDiv *= 0.1;
                }
                else
                {
                    currentNum *= 10;
                }
                currentNum += ( *durationCstr - '0' ) * decimalDiv;
            }
            ++durationCstr;
        }
        switch( *durationCstr )
        {
        case 'D':
            currentNum *= 86400;
            break;
        case 'H':
            currentNum *= 3600;
            break;
        case 'M':
            currentNum *= 60;
            break;
        case 'S':
            break;
        case ' ':
        case 0:
            if( currentNum == 0 )
            {
                break;
            }
        // Intentionally no break here
        default:
            return false;
        }
        seconds += currentNum;
        if( *durationCstr )
        {
            ++durationCstr;
        }
    }
    *this = seconds;
    return true;
}

/// Addition of a Timespan
///
/// Tests:
/// -# Create to time objects params A and Timespan B.  Sum objects A+=B.
///    Verify (A+B).asDouble() == (A.asDouble + B.asDouble )
Timespan& Timespan::operator+= ( const Timespan & rhs )
{
    timeval_.tv_usec += rhs.timeval_.tv_usec;
    timeval_.tv_sec += rhs.timeval_.tv_sec + timeval_.tv_usec / 1000000;
    timeval_.tv_usec %= 1000000;
    return * this;
}

/// Subtraction of a Timespan
///
/// Tests:
/// -# Create to time objects params A and Timespan B.  Sum objects A-=B.
///    Verify (A-B).asDouble() == (A.asDouble - B.asDouble )
Timespan& Timespan::operator-= ( const Timespan & rhs )
{
    *this -= rhs.timeval_;
    return * this;
}

/// Subtraction of a struct timeval
///
Timespan& Timespan::operator-= ( const struct timeval & rhs )
{
    timeval_.tv_usec += 1000000 - rhs.tv_usec;
    timeval_.tv_sec -= rhs.tv_sec - timeval_.tv_usec / 1000000 + 1;
    timeval_.tv_usec %= 1000000;
    return * this;
}


/// Sleep for the specified period.
void Timespan::sleepFor( void ) const
{

    struct timespec sleepTimespec, remainingTimespec;

    toTimespec( & sleepTimespec );

    nanosleep( & sleepTimespec, & remainingTimespec );
    /// Insert error handling here...
}

//== Input/output functions ==
const char* Timespan::toString( char* buf, int buflen, int precision ) const
{
    precision = MAX( 0, MIN( precision, 6 ) );
    if( precision == 0 )
    {
        snprintf( buf, buflen, "%ld", timeval_.tv_sec );
    }
    else
    {
#ifndef __APPLE_CC__
        snprintf( buf, buflen, "%ld.%06ld", timeval_.tv_sec, timeval_.tv_usec );
#else
        snprintf( buf, buflen, "%ld.%06d", timeval_.tv_sec, timeval_.tv_usec );
#endif
        buf[ strlen( buf ) - 5 + precision ] = 0;
    }

    return buf;
}

const Str Timespan::toString( int precision ) const
{
    char buf[17];
    toString( buf, 16, precision );
    return Str( buf );
}


