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

//

// Copyright (c) 1997-2001 Microsoft Corporation, All Rights Reserved 
//
//  wbemtime.cpp 
//
//  Purpose: Defines the WBEMTime and WBEMTimeSpan objects which are 
//  similar to the MFC CTime and CTimeSpan objects.  The WBEM versions
//  are capable of storing down to the nsec and also have functions for
//  Creating from and getting BSTRs.
//
//  Note; The current implementation of WBEMTime does not support dates 
//  before 1/1/1601;
//
//  WBEMTime::m_uTime is stored in GMT as 100 nsecs since 1/1/1601
//
//***************************************************************************

#include "precomp.h"
#include <stdio.h>
#pragma warning( disable : 4290 ) 

#ifdef UTILLIB
#include <assertbreak.h>
#else
#define ASSERT_BREAK(a)
#endif //UTILLIB

#include <WbemTime.h>
#include <comdef.h>


// These are here rather than wbemtime.h so we don't have to doc/support
#define DECPOS 14
#define SGNPOS 21
#define DMTFLEN 25

#define DEPRECATED 0
#define INVALID_TIME_FORMAT 0
#define INVALID_TIME_ARITHMETIC 0
#define BAD_TIMEZONE 0

// ****************************************************************
// Static functions and variables.  These can't be called/referenced
// outside of wbemtime.cpp

static WBEMTime g_Jan1970((time_t)0);

//***************************************************************************
//
//  StructtmToSystemTime
//
//  Description:  General utility for converting between the two common
//  data structures.
//
//  Return values: TRUE if OK;  
//
//***************************************************************************

static BOOL StructtmToSystemTime(const struct tm *ptm, SYSTEMTIME * pst)
{
    if (pst && ptm)
    {
        pst->wYear = ptm->tm_year + 1900; 
        pst->wMonth = ptm->tm_mon + 1; 
        pst->wDay = (WORD)ptm->tm_mday; 
        pst->wHour = (WORD)ptm->tm_hour; 
        pst->wMinute = (WORD)ptm->tm_min; 
        pst->wSecond = (WORD)ptm->tm_sec;
        pst->wDayOfWeek = (WORD)ptm->tm_wday;
        pst->wMilliseconds = 0;

        return TRUE;
    }

    return FALSE;
}

static BOOL SystemTimeToStructtm(const SYSTEMTIME *pst, struct tm *ptm)
{
    if (pst && ptm && pst->wYear >= 1900)
    {
        ptm->tm_year = pst->wYear - 1900; 
        ptm->tm_mon = pst->wMonth - 1; 
        ptm->tm_mday = pst->wDay; 
        ptm->tm_hour = pst->wHour; 
        ptm->tm_min = pst->wMinute; 
        ptm->tm_sec = pst->wSecond;
        ptm->tm_wday = pst->wDayOfWeek;
        ptm->tm_isdst = 0;  // Since we are working in gmt...

        return TRUE;
    }

    return FALSE;
}

//***************************************************************************
//
//  FileTimeToui64 
//  ui64ToFileTime
//
//  Description:  Conversion routines for going between FILETIME structures
//  and __int64.
//
//***************************************************************************

static void FileTimeToui64(const FILETIME *pft, ULONGLONG *p64)
{
    *p64 = pft->dwHighDateTime;
    *p64 = *p64 << 32;
    *p64 |=  pft->dwLowDateTime;
}

static void ui64ToFileTime(const ULONGLONG *p64,FILETIME *pft)
{
    unsigned __int64 uTemp = *p64;
    pft->dwLowDateTime = (DWORD)uTemp;
    uTemp = uTemp >> 32;
    pft->dwHighDateTime = (DWORD)uTemp; 
}

static int CompareSYSTEMTIME(const SYSTEMTIME *pst1, const SYSTEMTIME *pst2)
{
    FILETIME ft1, ft2;

    SystemTimeToFileTime(pst1, &ft1);
    SystemTimeToFileTime(pst2, &ft2);

    return CompareFileTime(&ft1, &ft2);
}

// This function is used to convert the relative values that come
// back from GetTimeZoneInformation into an actual date for the year
// in question.  The system time structure that is passed in is updated
// to contain the absolute values.
static void DayInMonthToAbsolute(SYSTEMTIME *pst, const WORD wYear)
{
    const static int _lpdays[] = {
        -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
    };
    
    const static int _days[] = {
        -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364
    };
    
    SHORT shYearDay;
    
    // If this is not 0, this is not a relative date
    if (pst->wYear == 0)
    {
        // Was that year a leap year?
        BOOL bLeap =  ( (( wYear % 400) == 0) || ((( wYear % 4) == 0) && (( wYear % 100) != 0)));
        
        // Figure out the day of the year for the first day of the month in question
        if (bLeap)
            shYearDay = 1 + _lpdays[pst->wMonth - 1];
        else
            shYearDay = 1 + _days[pst->wMonth - 1];
        
        // Now, figure out how many leap days there have been since 1/1/1601
        WORD yc = wYear - 1601;
        WORD y4 = (yc) / 4;
        WORD y100 = (yc) / 100;
        WORD y400 = (yc) / 400;
        
        // This will tell us the day of the week for the first day of the month in question.
        // The '1 +' reflects the fact that 1/1/1601 was a monday (figures).  You might ask,
        // 'why do we care what day of the week this is?'  Well, I'll tell you.  The way
        // daylight savings time is defined is with things like 'the last sunday of the month
        // of october.'  Kinda helps to know what day that is.
        SHORT monthdow = (1 + (yc * 365 + y4 + y400 - y100) + shYearDay) % 7;
        
        if ( monthdow < pst->wDayOfWeek )
            shYearDay += (pst->wDayOfWeek - monthdow) + (pst->wDay - 1) * 7;
        else
            shYearDay += (pst->wDayOfWeek - monthdow) + pst->wDay * 7;
        
            /*
            * May have to adjust the calculation above if week == 5 (meaning
            * the last instance of the day in the month). Check if yearday falls
            * beyond month and adjust accordingly.
        */
        if ( (pst->wDay == 5) &&
            (shYearDay > (bLeap ? _lpdays[pst->wMonth] :
        _days[pst->wMonth])) )
        {
            shYearDay -= 7;
        }

        // Now update the structure.
        pst->wYear = wYear;
        pst->wDay = shYearDay - (bLeap ? _lpdays[pst->wMonth - 1] :
        _days[pst->wMonth - 1]);
    }
    
}

// **************************************************************************
// These are static to WBEMTIME, which means they CAN be called from outside
// wbemtime

LONG WBEMTime::GetLocalOffsetForDate(const time_t &t)
{
    FILETIME ft;
    ULONGLONG ull = Int32x32To64(t, 10000000) + 116444736000000000;

    ui64ToFileTime(&ull, &ft);

    return GetLocalOffsetForDate(&ft);
}

LONG WBEMTime::GetLocalOffsetForDate(const struct tm *ptmin)
{
    SYSTEMTIME st;

    StructtmToSystemTime(ptmin, &st);
    
    return GetLocalOffsetForDate(&st);
}

LONG WBEMTime::GetLocalOffsetForDate(const FILETIME *pft)
{
    SYSTEMTIME st;

    FileTimeToSystemTime(pft, &st);
    
    return GetLocalOffsetForDate(&st);
}

LONG WBEMTime::GetLocalOffsetForDate(const SYSTEMTIME *pst)
{
    TIME_ZONE_INFORMATION tzTime;
    DWORD dwRes = GetTimeZoneInformation(&tzTime);
    LONG lRes = 0xffffffff;

    switch (dwRes)
    {
    case TIME_ZONE_ID_UNKNOWN:
        {
            // Read tz, but no dst defined in this zone
            lRes = tzTime.Bias * -1;
            break;
        }
    case TIME_ZONE_ID_STANDARD:
    case TIME_ZONE_ID_DAYLIGHT:
        {

            // Convert the relative dates to absolute dates
            DayInMonthToAbsolute(&tzTime.DaylightDate, pst->wYear);
            DayInMonthToAbsolute(&tzTime.StandardDate, pst->wYear);

            if ( CompareSYSTEMTIME(&tzTime.DaylightDate, &tzTime.StandardDate) < 0 ) 
            {
                /*
                 * Northern hemisphere ordering
                 */
                if ( CompareSYSTEMTIME(pst, &tzTime.DaylightDate) < 0 || CompareSYSTEMTIME(pst, &tzTime.StandardDate) > 0)
                {
                    lRes = tzTime.Bias * -1;
                }
                else
                {
                    lRes = (tzTime.Bias + tzTime.DaylightBias) * -1;
                }
            }
            else 
            {
                /*
                 * Southern hemisphere ordering
                 */
                if ( CompareSYSTEMTIME(pst, &tzTime.StandardDate) < 0 || CompareSYSTEMTIME(pst, &tzTime.DaylightDate) > 0)
                {
                    lRes = (tzTime.Bias + tzTime.DaylightBias) * -1;
                }
                else
                {
                    lRes = tzTime.Bias * -1;
                }
            }

            break;

        }
    case TIME_ZONE_ID_INVALID:
    default:
        {
            // Can't read the timezone info
            ASSERT_BREAK(BAD_TIMEZONE);
            break;
        }
    }

    return lRes;
}

///////////////////////////////////////////////////////////////////////////
// WBEMTime - This class holds time values. 

//***************************************************************************
//
//  WBEMTime::operator=(BSTR bstrWbemFormat) 
//
//  Description:  Assignment operator which is also used by the constructor.
//  The string must have the format:
//  YYYYMMDDHHSS.123456789    So 3:04 am, 1/1/96 would be 199601010304.0
//
//  or the format yyyymmddhhmmss.mmmmmmsuuu.
//
//  Note that the fractional part can be between 1 and nine digits.   
//
//  Return: WBEMTime object.
//
//***************************************************************************

const WBEMTime & WBEMTime::operator=(const BSTR bstrWbemFormat)
{
    Clear();   // set when properly assigned

    if((NULL == bstrWbemFormat) || 
        bstrWbemFormat[DECPOS] != L'.'
        )
    {
        ASSERT_BREAK(INVALID_TIME_FORMAT);
        return *this;
    }

    if ( (wcslen(bstrWbemFormat) == DMTFLEN) &&
        (bstrWbemFormat[SGNPOS] == L'+' || bstrWbemFormat[SGNPOS] == L'-') )
    {
        SetDMTF(bstrWbemFormat);
    }
    else
    {
        ASSERT_BREAK(INVALID_TIME_FORMAT);
    }
 
    return *this;
}

//***************************************************************************
//
//  WBEMTime::operator=(const SYSTEMTIME) 
//
//  Description:  Assignment operator which is also used by the constructor.
//  This takes a standard WIN32 SYSTEMTIME stucture.  
//
//  Return: WBEMTime object.
//
//***************************************************************************

const WBEMTime & WBEMTime::operator=(const SYSTEMTIME & st)
{
    Clear();   // set when properly assigned
    FILETIME t_ft;

    if ( SystemTimeToFileTime(&st, &t_ft) )
    {
        // now assign using a FILETIME.
        *this = t_ft;
    }
    else
    {
        ASSERT_BREAK(INVALID_TIME_FORMAT);
    }

    return *this;
}

//***************************************************************************
//
//  WBEMTime::operator=(const FILETIME) 
//
//  Description:  Assignment operator which is also used by the constructor.
//  This takes a standard WIN32 FILETIME stucture.  
//
//  Return: WBEMTime object.
//
//***************************************************************************

const WBEMTime & WBEMTime::operator=(const FILETIME & ft)
{
    FileTimeToui64(&ft, &m_uTime);
    return *this;
}

//***************************************************************************
//
//  WBEMTime::operator=(struct tm tmin) 
//
//  Description:  Assignment operator which is also used by the constructor.
//  This takes a standard c runtine struct tm stucture.  
//
//  Return: WBEMTime object.
//
//***************************************************************************

const WBEMTime & WBEMTime::operator=(const struct tm &a_tmin)
{
    Clear();   // set when properly assigned

    SYSTEMTIME systemTime;
    if (StructtmToSystemTime(&a_tmin, &systemTime))
    {
        *this = systemTime;
    }

    return *this;
}

//***************************************************************************
//
//  WBEMTime::operator=(struct time_t t) 
//
//  Description:  Assignment operator which is also used by the constructor.
//  This takes a standard c runtine time_t stucture.  
//
//  Return: WBEMTime object.
//
//***************************************************************************

const WBEMTime & WBEMTime::operator=(const time_t & t)
{
    if (t >= 0)
    {
        m_uTime = Int32x32To64(t, 10000000) + 116444736000000000;
    }
    else
    {
        Clear();
    }

    return *this;
}

//***************************************************************************
//
//  WBEMTime::operator+(const WBEMTime &uAdd)
//
//  Description:  dummy function for adding two WBEMTime.  It doesnt really
//  make sense to add two date, but this is here for Tomas's template.
//
//  Return: WBEMTime object.
//
//***************************************************************************

WBEMTime WBEMTime::operator+(const WBEMTimeSpan &uAdd) const
{
    WBEMTime ret;

    if (IsOk() && uAdd.IsOk())
    {
        ret.m_uTime = m_uTime + uAdd.m_Time;
    }
    else
    {
        ASSERT_BREAK(INVALID_TIME_ARITHMETIC);
    }

    return ret;
}

const WBEMTime &WBEMTime::operator+=( const WBEMTimeSpan &ts )
{ 
    if (IsOk() && ts.IsOk())
    {
        m_uTime += ts.m_Time ; 
    }
    else
    {
        Clear();
        ASSERT_BREAK(INVALID_TIME_ARITHMETIC);
    }

    return *this ; 
}

//***************************************************************************
//
//  WBEMTime::operator-(const WBEMTime & sub)
//
//  Description:  returns a WBEMTimeSpan object as the difference between 
//  two WBEMTime objects.
//
//  Return: WBEMTimeSpan object.
//
//***************************************************************************

WBEMTimeSpan WBEMTime::operator-(const WBEMTime & sub)
{
    WBEMTimeSpan ret;

    if (IsOk() && sub.IsOk() && (m_uTime >= sub.m_uTime))
    {
        ret.m_Time = m_uTime-sub.m_uTime;
    }
    else
    {
        ASSERT_BREAK(INVALID_TIME_ARITHMETIC);
    }

    return ret;
}

WBEMTime WBEMTime::operator-(const WBEMTimeSpan & sub) const
{
    WBEMTime ret;

    if (IsOk() && sub.IsOk() && (m_uTime >= sub.m_Time))
    {
        ret.m_uTime = m_uTime - sub.m_Time;
    }
    else
    {
        ASSERT_BREAK(INVALID_TIME_ARITHMETIC);
    }

    return ret;
}

const WBEMTime &WBEMTime::operator-=(const WBEMTimeSpan & sub)
{
    if (IsOk() && sub.IsOk() && (m_uTime >= sub.m_Time))
    {
        m_uTime -= sub.m_Time;
    }
    else
    {
        Clear();
        ASSERT_BREAK(INVALID_TIME_ARITHMETIC);
    }

    return *this;
}

//***************************************************************************
//
//  WBEMTime::GetBSTR(void)
//
//  This function used to CLAIM to do this:
//
//  WRONG Description:  Converts the time which is stored as the number of 
//  nano seconds since 1970 into a bstr with this format.
//  YYYYMMDDHHSS.123456789    So 3:04 am, 1/1/96 would be 199601010304.000000000
//
//  What it really did was return some bastardized form of a dmtf string.  Now
//  it returns a dmtf string in gmt form (which is what the docs claim).
//
//  Return: BSTR representation of time, or NULL if error.  Note that the
//  caller should free up this string!
//
//***************************************************************************

BSTR WBEMTime::GetBSTR(void) const
{
    return GetDMTF(false) ;
}

//***************************************************************************
//
//  WBEMTime::GetDMTFNonNtfs(void)
//
//***************************************************************************

BSTR WBEMTime::GetDMTFNonNtfs(void) const
{
    FILETIME t_ft1, t_ft2;
    BSTR t_Date = NULL;

    if (GetFILETIME(&t_ft1) && FileTimeToLocalFileTime(&t_ft1, &t_ft2))
    {
        t_Date = WBEMTime(t_ft2).GetDMTF();

        if (t_Date != NULL)
        {
            t_Date[21] = L'+';
            t_Date[22] = L'*';
            t_Date[23] = L'*';
            t_Date[24] = L'*';
        }
    }

    return t_Date;
}

//***************************************************************************
//
//  WBEMTime::time_t(time_t * ptm)
//
//  Return: TRUE if OK.
//
//***************************************************************************

BOOL WBEMTime::Gettime_t(time_t * ptm) const
{
    if( (!IsOk()) || (ptm == NULL) || (*this < g_Jan1970))
    {
        ASSERT_BREAK(INVALID_TIME_ARITHMETIC);
        return FALSE;
    }

    if (g_Jan1970 != *this)
    {
        LONGLONG t_tmp = ( (m_uTime - g_Jan1970.m_uTime) / 10000000);

        if (t_tmp <= (LONGLONG)0xffffffff)
        {
            *ptm = (time_t)t_tmp;
        }
        else
        {
            ASSERT_BREAK(INVALID_TIME_ARITHMETIC);
            return FALSE;
        }
    }
    else
    {
        *ptm = 0;
    }

    return TRUE;
}

//***************************************************************************
//
//  WBEMTime::GetStructtm(struct tm * ptm)
//
//  Return: TRUE if OK.
//
//***************************************************************************

BOOL WBEMTime::GetStructtm(struct tm * ptm) const
{
    SYSTEMTIME systemTime;

    return (GetSYSTEMTIME(&systemTime) && SystemTimeToStructtm(&systemTime, ptm));
}

//***************************************************************************
//
//  WBEMTime::GetSYSTEMTIME(SYSTEMTIME * pst)
//
//  Return: TRUE if OK.
//
//***************************************************************************

BOOL WBEMTime::GetSYSTEMTIME(SYSTEMTIME * pst) const
{
    if ((pst == NULL) || (!IsOk()))
    {
        ASSERT_BREAK(INVALID_TIME_ARITHMETIC);
        return FALSE;
    }

    FILETIME t_ft;

    if (GetFILETIME(&t_ft))
    {
        if (!FileTimeToSystemTime(&t_ft, pst))
        {
            ASSERT_BREAK(INVALID_TIME_ARITHMETIC);
            return FALSE;
        }
    }
    else
    {
        return FALSE;
    }

    return TRUE;
}

//***************************************************************************
//
//  WBEMTime::GetFILETIME(FILETIME * pst)
//
//  Return: TRUE if OK.
//
//***************************************************************************

BOOL WBEMTime::GetFILETIME(FILETIME * pft) const
{
    if ((pft == NULL) || (!IsOk()))
    {
        ASSERT_BREAK(INVALID_TIME_ARITHMETIC);
        return FALSE;
    }

    ui64ToFileTime(&m_uTime, pft);

    return TRUE;
}

//***************************************************************************
//
//  CWbemTime::SetDMTF(BSTR wszText)
//
//  Description:  Sets the time value to the DMTF string datetime value
//  passed as the parameter
//
//  Return: TRUE if OK.
//
//***************************************************************************
BOOL WBEMTime::SetDMTF( const BSTR a_wszText )
{

    wchar_t t_DefaultBuffer[] = {L"16010101000000.000000+000"} ;
    wchar_t t_DateBuffer[ DMTFLEN + 1 ] ;
            t_DateBuffer[ DMTFLEN ] = NULL ;

    bstr_t  t_bstrDate( a_wszText ) ;

    // wildcard cleanup and validation
    // ===============================

    if( DMTFLEN != t_bstrDate.length() )
    {
        ASSERT_BREAK( INVALID_TIME_FORMAT ) ;
        return FALSE ;  
    }
    
    wchar_t *t_pwBuffer = (wchar_t*)t_bstrDate ;
    
    for( int t_i = 0; t_i < DMTFLEN; t_i++ )
    {
        switch( t_pwBuffer[ t_i ] )
        {
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
            {
                // stepping on separator or sign
                if( DECPOS == t_i || SGNPOS == t_i )
                {
                    ASSERT_BREAK( INVALID_TIME_FORMAT ) ;
                    return FALSE ;  
                }
                t_DateBuffer[ t_i ] = t_pwBuffer[ t_i ] ;
                
                break ;
            }           
            case '*':
            {               
                // stepping on separator or sign
                if( DECPOS == t_i || SGNPOS == t_i )
                {
                    ASSERT_BREAK( INVALID_TIME_FORMAT ) ;
                    return FALSE ;  
                }
                else
                {
                    // replace with default stamp
                    t_DateBuffer[ t_i ] = t_DefaultBuffer[ t_i ] ; 
                }   
                break ;
            }           
            case '.':
            {
                if( DECPOS != t_i )
                {
                    ASSERT_BREAK( INVALID_TIME_FORMAT ) ;
                    return FALSE ;  
                }
                t_DateBuffer[ t_i ] = t_pwBuffer[ t_i ] ;

                break ;
            }           
            case '+':
            case '-':
            {
                if( SGNPOS != t_i )
                {
                    ASSERT_BREAK( INVALID_TIME_FORMAT ) ;
                    return FALSE ;  
                }
                t_DateBuffer[ t_i ] = t_pwBuffer[ t_i ] ;
                break ;
            }           
            default:
            {
                ASSERT_BREAK( INVALID_TIME_FORMAT ) ;
                return FALSE ;
            }           
        }
    }

    // Parse it
    // ========

    int nYear, nMonth, nDay, nHour, nMinute, nSecond, nMicro, nOffset;
    WCHAR wchSep;

    int nRes = swscanf (

        (LPCWSTR)&t_DateBuffer, 
        L"%4d%2d%2d%2d%2d%2d.%6d%c%3d", 
        &nYear, 
        &nMonth, 
        &nDay, 
        &nHour, 
        &nMinute, 
        &nSecond, 
        &nMicro, 
        &wchSep, 
        &nOffset
    );

    if ( ( 9 != nRes )  || ( 1601 > nYear) )    
    {
        ASSERT_BREAK(INVALID_TIME_FORMAT);
        return FALSE;
    }

    // Convert it to SYSTEMTIME
    // ========================

    SYSTEMTIME st;
    st.wYear        = (WORD)nYear;
    st.wMonth       = (WORD)nMonth;
    st.wDay         = (WORD)nDay;
    st.wHour        = (WORD)nHour;
    st.wMinute      = (WORD)nMinute;
    st.wSecond      = (WORD)nSecond;
    st.wMilliseconds = nMicro / 1000;
    st.wDayOfWeek   = 0;

    *this = st;

    // NOW we adjust for the offset
    // ============================

    if ( IsOk() )
    {
        int nSign = (wchSep == L'+') ? 1 : -1 ;
        
        m_uTime -= (LONGLONG)nSign * (LONGLONG)nOffset * 60 * 10000000;
    }
    else
    {
        ASSERT_BREAK( INVALID_TIME_ARITHMETIC ) ;
        return FALSE ;
    }

    return TRUE;
}

//***************************************************************************
//
//  BSTR WBEMTime::GetDMTF(void)
//
//  Description:  Gets the time in DMTF string datetime format. User must call
//  SysFreeString with the result. If bLocal is true, then the time is given
//  in the local timezone, else the time is given in GMT.
//
//  Return: NULL if not OK.
//
//***************************************************************************


BSTR WBEMTime::GetDMTF(BOOL bLocal) const
{

    if (!IsOk())
    {
        ASSERT_BREAK(INVALID_TIME_ARITHMETIC);
        return NULL;
    }

    SYSTEMTIME t_Systime;
    wchar_t chsign = L'-';
    int offset = 0;

    // If the date to be converted is within 12 hours of
    // 1/1/1601, return the greenwich time
    ULONGLONG t_ConversionZone = 12L * 60L * 60L ;
    t_ConversionZone = t_ConversionZone * 10000000L ;
    if ( !bLocal || ( m_uTime < t_ConversionZone ) )
    {
        if(!GetSYSTEMTIME(&t_Systime))
        {
            return NULL;
        }
    }
    else
    {
        if (GetSYSTEMTIME(&t_Systime))
        {
            offset = GetLocalOffsetForDate(&t_Systime);

            WBEMTime wt;
            if (offset >= 0)
            {
                chsign = '+';
                wt = *this + WBEMTimeSpan(0, 0, offset, 0);
            }
            else
            {
                offset *= -1;
                wt = *this - WBEMTimeSpan(0, 0, offset, 0);
            }
            wt.GetSYSTEMTIME(&t_Systime);
        }
        else
        {
            return NULL;
        }
    }

    LONGLONG tmpMicros = m_uTime%10000000;
    LONG micros = (LONG)(tmpMicros / 10);

    BSTR t_String = SysAllocStringLen(NULL, DMTFLEN + 1);
    if ( ! t_String ) 
    {
        throw CHeap_Exception ( CHeap_Exception :: E_ALLOCATION_ERROR ) ;
    }

    swprintf(

        t_String,
        L"%04.4d%02.2d%02.2d%02.2d%02.2d%02.2d.%06.6d%c%03.3ld",
        t_Systime.wYear,
        t_Systime.wMonth, 
        t_Systime.wDay,
        t_Systime.wHour,
        t_Systime.wMinute,
        t_Systime.wSecond,
        micros, 
        chsign, 
        offset
    );

    return t_String ;

}

///////////////////////////////////////////////////////////////////////////
// WBEMTimeSpan - This class holds timespan values.  The data is stored
// in 100 nanosecond units (like FILETIME). 

//***************************************************************************
//
//  WBEMTimeSpan::WBEMTimeSpan(int iDays, int iHours, int iMinutes, int iSeconds, 
//                int iMSec, int iUSec, int iNSec)
//
//  Description:  Constructor.
//
//***************************************************************************

WBEMTimeSpan::WBEMTimeSpan(int iDays, int iHours, int iMinutes, int iSeconds, 
                int iMSec, int iUSec, int iNSec)
{
    m_Time = 0;        //todo, check values!!!
    m_Time += iSeconds;
    m_Time += iMinutes * 60;
    m_Time += iHours * 60 * 60;
    m_Time += iDays * 24 * 60 * 60;
    m_Time *= 10000000;
    m_Time += iNSec / 100;  // Nanoseconds
    m_Time += iUSec*10;   // Microseconds
    m_Time += iMSec*10000; // Milliseconds
}

WBEMTimeSpan::WBEMTimeSpan ( const FILETIME &ft )
{
    ASSERT_BREAK(DEPRECATED);
    *this = ft ; 
}

WBEMTimeSpan::WBEMTimeSpan ( const time_t & t )
{
    ASSERT_BREAK(DEPRECATED);
    *this = t ; 
} ;

//***************************************************************************
//
//  WBEMTimeSpan::operator=(const BSTR bstrWbemFormat) 
//
//  Return: WBEMTimeSpan object.
//
//***************************************************************************

const WBEMTimeSpan & WBEMTimeSpan::operator=(const BSTR bstrWbemFormat)
{
    Clear();

    // all characters should be digits except for one which 
    // must be a period

    if ((bstrWbemFormat == NULL) || (bstrWbemFormat[DECPOS] != L'.') ||
        (wcslen(bstrWbemFormat) != DMTFLEN) || (bstrWbemFormat[SGNPOS] != L':') )
    {
        ASSERT_BREAK(INVALID_TIME_FORMAT);
        return *this;
    }

    int nDays, nHours, nMinutes, nSeconds, nMicros, nOffset;
    WCHAR wchSep;

    int nRes = swscanf (

        bstrWbemFormat, 
        L"%8d%2d%2d%2d.%6d%c%3d", 
        &nDays, 
        &nHours, 
        &nMinutes, 
        &nSeconds, 
        &nMicros, 
        &wchSep, 
        &nOffset
    );

    if ( (nRes != 7) || ( nOffset != 0) )
    {
        ASSERT_BREAK(INVALID_TIME_FORMAT);
        return *this;
    }

    *this = WBEMTimeSpan(nDays, nHours, nMinutes, nSeconds, 0, nMicros, 0);

    return *this;
}

//***************************************************************************
//
//  WBEMTimeSpan::operator=(const FILETIME &) 
//  WBEMTimeSpan::operator=(const time_t &) 
//
//  Description:  Assignment operator which is also used by the constructor.
//
//  Return: WBEMTimeSpan object.
//
//***************************************************************************

const WBEMTimeSpan &  WBEMTimeSpan::operator=(const FILETIME &ft)
{
    ASSERT_BREAK(DEPRECATED);

    ULONGLONG uTemp;
    FileTimeToui64(&ft, &uTemp);
    m_Time = uTemp;

    return *this;
}

const WBEMTimeSpan &  WBEMTimeSpan::operator=(const time_t & t)
{
    ASSERT_BREAK(DEPRECATED);

    ULONGLONG uTemp = 0;

    uTemp = t;
    if (t >= 0)
    {
        m_Time = uTemp * 10000000;
    }
    else
    {
        Clear();
    }

    return *this;
}

//***************************************************************************
//
//  WBEMTimeSpan::operator +(const WBEMTimeSpan &uAdd)
//
//  Description:  function for adding two WBEMTimeSpan objects.
//
//  Return: WBEMTimeSpan object.
//
//***************************************************************************

WBEMTimeSpan WBEMTimeSpan::operator+(const WBEMTimeSpan &uAdd) const
{
    WBEMTimeSpan ret;

    if (IsOk() && uAdd.IsOk())
    {
        ret.m_Time = m_Time + uAdd.m_Time;
    }
    else
    {
        ASSERT_BREAK(INVALID_TIME_ARITHMETIC);
    }

    return ret;
}

const WBEMTimeSpan &WBEMTimeSpan::operator+= ( const WBEMTimeSpan &uAdd )
{ 
    if (IsOk() && uAdd.IsOk())
    {
        m_Time += uAdd.m_Time ; 
    }
    else
    {
        Clear();
        ASSERT_BREAK(INVALID_TIME_ARITHMETIC);
    }

    return *this ; 
}

//***************************************************************************
//
//  WBEMTimeSpan::operator -(const WBEMTimeSpan &uAdd)
//
//  Description:  function for adding two WBEMTimeSpan objects.
//
//  Return: WBEMTimeSpan object.
//
//***************************************************************************

WBEMTimeSpan WBEMTimeSpan::operator-(const WBEMTimeSpan &uSub) const
{
    WBEMTimeSpan ret;
    
    if (IsOk() && uSub.IsOk() && (m_Time >= uSub.m_Time))
    {
        ret.m_Time = m_Time - uSub.m_Time;
    }
    else
    {
        ASSERT_BREAK(INVALID_TIME_ARITHMETIC);
    }

    return ret;
}

const WBEMTimeSpan &WBEMTimeSpan::operator-= ( const WBEMTimeSpan &uSub ) 
{
    if (IsOk() && uSub.IsOk() && (m_Time >= uSub.m_Time))
    {
        m_Time -= uSub.m_Time;
    }
    else
    {
        Clear();
        ASSERT_BREAK(INVALID_TIME_ARITHMETIC);
    }

    return *this;
}

//***************************************************************************
//
//  WBEMTimeSpan::GetBSTR(void)
//
//  Description:  Converts the time which is stored as the number of 
//  100 nano second units into a dmtf formatted string
//  ddddddddhhmmss.mmmmmm:000
//
//  Return: BSTR representation of time, or NULL if error.  Note that the
//  caller should free up this string!
//
//***************************************************************************

BSTR WBEMTimeSpan::GetBSTR(void) const
{
    if(!IsOk())
    {
        return NULL;
    }

    ULONGLONG Time = m_Time;

    // The /10 is to convert from 100ns to microseconds
    long iMicro = (long)((Time % 10000000) / 10);
    Time /= 10000000;
    int iSec = (int)(Time % 60);
    Time /= 60;
    int iMin = (int)(Time % 60);
    Time /= 60;
    int iHour = (int)(Time % 24);
    Time /= 24;

    BSTR t_String = SysAllocStringLen(NULL, DMTFLEN + 1);
    if ( ! t_String ) 
    {
        throw CHeap_Exception ( CHeap_Exception :: E_ALLOCATION_ERROR ) ;
    }

    swprintf(t_String, L"%08I64i%02d%02d%02d.%06ld:000",
                Time, iHour, iMin, iSec, iMicro);

    return t_String ;
}

//***************************************************************************
//
//  WBEMTimeSpan::Gettime_t(void)
//  WBEMTimeSpan::GetFILETIME(void)
//
//  Description:  Converts the time span which is stored as the number of 
//  nano seconds into common stuctures.
//
//  Return: TRUE if OK.
//
//***************************************************************************

BOOL WBEMTimeSpan::Gettime_t(time_t * ptime_t) const
{
    ASSERT_BREAK(DEPRECATED);

    if(!IsOk())
    {
        return FALSE;
    }

    *ptime_t = (DWORD)(m_Time / 10000000);

    return TRUE;
}

BOOL WBEMTimeSpan::GetFILETIME(FILETIME * pst) const
{
    ASSERT_BREAK(DEPRECATED);

    if(!IsOk())
    {
        return FALSE;
    }

    ULONGLONG uTemp;
    uTemp = m_Time;
    ui64ToFileTime(&uTemp,pst);
    return TRUE;
}