/**********************************************************************/
/**                       Microsoft Windows/NT                       **/
/**                Copyright(c) Microsoft Corporation, 1995 - 1999 **/
/**********************************************************************/

/*
    FILE HISTORY:

*/

#define OEMRESOURCE
#include "stdafx.h"

#include <stdlib.h>
#include <memory.h>
#include <ctype.h>
#include <string.h>

#include "dbgutil.h"
#include "objplus.h"
#include "intltime.h"

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

#define new DEBUG_NEW

/////////////////////////////////////////////////////////////////////////////
// FormatDateTime
//
//  Generates a current local date/time string
//
//
/////////////////////////////////////////////////////////////////////////////
void
InternalFormatDateTime(CString & strOutput, SYSTEMTIME * psystemtime, BOOL fLongDate)
{
    int     nLen;
    CString strDate, strTime;
    DWORD   dwFlags = 0;
    
    dwFlags = fLongDate ? DATE_LONGDATE : DATE_SHORTDATE;

    // call once to get the length, and again to format the string
    nLen = GetDateFormat(GetThreadLocale(), dwFlags, psystemtime, NULL, NULL, 0);
    nLen = GetDateFormat(GetThreadLocale(), dwFlags, psystemtime, NULL, strDate.GetBuffer(nLen + 1), nLen + 1);
    strDate.ReleaseBuffer();

    // now the time
    nLen = GetTimeFormat(GetThreadLocale(), 0, psystemtime, NULL, NULL, 0);
    nLen = GetTimeFormat(GetThreadLocale(), 0, psystemtime, NULL, strTime.GetBuffer(nLen + 1), nLen + 1);
    strTime.ReleaseBuffer();

    strOutput = strDate + _T(" ") + strTime;
}

void
FormatDateTime(CString & strOutput, SYSTEMTIME * psystemtime, BOOL fLongDate)
{
    InternalFormatDateTime(strOutput, psystemtime, fLongDate);
}

void
FormatDateTime(CString & strOutput, FILETIME * pfiletime, BOOL fLongDate)
{
    FILETIME    localTime;
    SYSTEMTIME  systemtime;

	if (!FileTimeToLocalFileTime(pfiletime, &localTime))
	{
		return;
	}

    if (!FileTimeToSystemTime(&localTime, &systemtime))
    {
        return;
    }

    InternalFormatDateTime(strOutput, &systemtime, fLongDate);
}

void
FormatDateTime(CString & strOutput, CTime & time, BOOL fLongDate)
{
    SYSTEMTIME systemtime;
    struct tm * ptm = time.GetLocalTm(NULL);

    if (ptm != NULL)
    {
	    systemtime.wYear = (WORD) (1900 + ptm->tm_year);
	    systemtime.wMonth = (WORD) (1 + ptm->tm_mon);
	    systemtime.wDayOfWeek = (WORD) ptm->tm_wday;
	    systemtime.wDay = (WORD) ptm->tm_mday;
	    systemtime.wHour = (WORD) ptm->tm_hour;
	    systemtime.wMinute = (WORD) ptm->tm_min;
	    systemtime.wSecond = (WORD) ptm->tm_sec;
	    systemtime.wMilliseconds = 0;

        InternalFormatDateTime(strOutput, &systemtime, fLongDate);
    }
    else
    {
        strOutput.Empty();
    }
}

/////////////////////////////////////////////////////////////////////////////
// CIntlTime
//
//          These allocations cause a phoney "memory leak" error, since
//          they're not freed until after the audit-check.  Anyway
//          around this?
//
/////////////////////////////////////////////////////////////////////////////

// Initialise static members
CIntlTime::INTL_TIME_SETTINGS CIntlTime::m_itsInternationalSettings;
BOOL CIntlTime::m_fIntlOk = CIntlTime::SetIntlTimeSettings();
CString CIntlTime::m_strBadDate("--");
CString CIntlTime::m_strBadTime("--");

/***
 *
 *  CIntlTime::SetIntlTimeSettings
 *
 *  Purpose:
 *
 *      This is a static function which initialises the international
 *      settings (date seperator, etc) of the CIntlTime class.
 *
 *  Returns:
 *
 *      TRUE if the international settings are properly initialised,
 *      or FALSE if they are not.
 *
 */
BOOL CIntlTime::SetIntlTimeSettings ()
{
#ifdef _WIN32
    #define MAXSTR 128

    BOOL fOk;
    TCHAR str[MAXSTR];

    #define GETCSTRINGFIELD(field,cstring)\
         ::GetLocaleInfo(GetUserDefaultLCID(), field, cstring.GetBuffer(MAXSTR), MAXSTR);\
         cstring.ReleaseBuffer()
    #define GETINTFIELD(field, integer)\
         ::GetLocaleInfo(GetUserDefaultLCID(), field, str, MAXSTR);\
         integer = _ttol(str)
    #define GETBOOLFIELD(field, boolean)\
         ::GetLocaleInfo(GetUserDefaultLCID(), field, str, MAXSTR);\
         boolean=*str == '1'

    fOk = GETCSTRINGFIELD(LOCALE_SDATE, CIntlTime::m_itsInternationalSettings.strDateSeperator);
    fOk &= GETCSTRINGFIELD(LOCALE_STIME, CIntlTime::m_itsInternationalSettings.strTimeSeperator);
    fOk &= GETINTFIELD(LOCALE_IDATE, CIntlTime::m_itsInternationalSettings.nDateFormat);
    ASSERT((CIntlTime::m_itsInternationalSettings.nDateFormat >= 0) && (CIntlTime::m_itsInternationalSettings.nDateFormat <= 2));
    fOk &= GETBOOLFIELD(LOCALE_ITIME, CIntlTime::m_itsInternationalSettings.f24HourClock);
    fOk &= GETBOOLFIELD(LOCALE_ICENTURY, CIntlTime::m_itsInternationalSettings.fCentury);
    fOk &= GETBOOLFIELD(LOCALE_ITLZERO, CIntlTime::m_itsInternationalSettings.fLeadingTimeZero);
    fOk &= GETBOOLFIELD(LOCALE_IDAYLZERO, CIntlTime::m_itsInternationalSettings.fLeadingDayZero);
    fOk &= GETBOOLFIELD(LOCALE_IMONLZERO, CIntlTime::m_itsInternationalSettings.fLeadingMonthZero);
    if (CIntlTime::m_itsInternationalSettings.f24HourClock)
    {
        CIntlTime::m_itsInternationalSettings.strAM = "";
        CIntlTime::m_itsInternationalSettings.strPM = "";
    }
    else
    {
        fOk &= GETCSTRINGFIELD(LOCALE_S1159, CIntlTime::m_itsInternationalSettings.strAM);
        fOk &= GETCSTRINGFIELD(LOCALE_S2359, CIntlTime::m_itsInternationalSettings.strPM);
    }

#ifdef _DEBUG
    if (!fOk)
    {
        Trace0("There was a problem with some of the intl time settings\n");
    }
#endif // _DEBUG

    return(fOk);

#endif // _WIN32

#ifdef _WIN16

    #define MAXSTR 128

    CString strMisc;

    #define GETCSTRINGFIELD(field,cstring,defstring)\
        ::GetProfileString("Intl", field, defstring, cstring.GetBuffer(MAXSTR), MAXSTR);\
        cstring.ReleaseBuffer()
    #define GETINTFIELD(field, integer, defint)\
        integer = ::GetProfileInt("Intl", field, defint)
    #define GETBOOLFIELD(field, boolean, defint)\
        boolean = ::GetProfileInt("Intl", field, defint)==1

    // Get the values.  Assume American defaults in case of failure.

    GETCSTRINGFIELD("sDate", CIntlTime::m_itsInternationalSettings.strDateSeperator, "/");
    GETCSTRINGFIELD("sTime", CIntlTime::m_itsInternationalSettings.strTimeSeperator, ":");
    GETINTFIELD("iDate", CIntlTime::m_itsInternationalSettings.nDateFormat, 0);
    ASSERT((CIntlTime::m_itsInternationalSettings.nDateFormat >= 0) && (CIntlTime::m_itsInternationalSettings.nDateFormat <= 2));
    GETBOOLFIELD("iTime", CIntlTime::m_itsInternationalSettings.f24HourClock, FALSE);
    GETBOOLFIELD("iTLZero", CIntlTime::m_itsInternationalSettings.fLeadingTimeZero, FALSE);
    if (CIntlTime::m_itsInternationalSettings.f24HourClock)
    {
        CIntlTime::m_itsInternationalSettings.strAM = "";
        CIntlTime::m_itsInternationalSettings.strPM = "";
    }
    else
    {
        GETCSTRINGFIELD("s1159", CIntlTime::m_itsInternationalSettings.strAM, "AM");
        GETCSTRINGFIELD("s2359", CIntlTime::m_itsInternationalSettings.strPM, "PM");
    }


    GETCSTRINGFIELD("sShortDate", strMisc, "M/d/yy");
    // These settings are determined from the short date sample, as
    // there is no direct equivalent in the win.ini
    CIntlTime::m_itsInternationalSettings.fCentury = strMisc.Find("yyyy") != -1;
    CIntlTime::m_itsInternationalSettings.fLeadingDayZero = strMisc.Find("dd") != -1;
    CIntlTime::m_itsInternationalSettings.fLeadingMonthZero = strMisc.Find("MM") != -1;

    return(TRUE);

#endif // _WIN16

}

/***
 *
 *  CIntlTime::Reset()
 *
 *  Purpose:
 *
 *      Reset the international settings. Usually in response to
 *      a change in those international settings by the user.
 *
 *  Notes:
 *
 *      This is a publically available static function.
 *
 */
void CIntlTime::Reset()
{
    CIntlTime::m_fIntlOk = CIntlTime::SetIntlTimeSettings();
}

/***
 *
 *  CIntlTime::IsLeapYear
 *
 *  Purpose:
 *
 *      Determine if the given year is/was a leap year
 *
 *  Arguments:
 *
 *      int nYear   The year in question.
 *
 *  Returns:
 *
 *      TRUE if the year is/was a leap year, or FALSE otherwise.
 *
 *  Comments:
 *
 *      A year is a leap year, if is divisible by 4, but not by a 100, unless
 *      it is divisible by 400. e.g. 1900 was not a leap year, but 2000 will
 *      be.
 *
 */
BOOL CIntlTime::IsLeapYear(UINT nYear)
{
    return(!(nYear % 4) && ( (nYear % 100) || !(nYear % 400) ));
}

/***
 *
 *  CIntlTime::IsValidDate
 *
 *  Purpose:
 *
 *      Determine if the given month, day year values are
 *      valid.
 *
 *  Arguments:
 *
 *      int nMonth      Month
 *      int nDay        Day
 *      int nYear       Year
 *
 *  Returns:
 *
 *      TRUE for a valid date, FALSE otherwise.
 *
 */
BOOL CIntlTime::IsValidDate(UINT nMonth, UINT nDay, UINT nYear)
{
    // Sanity Check:
    BOOL fOk = ((nYear <100) || (nYear >= 1970)) &&
                (nYear <= 2037)                  &&
               ((nMonth >= 1) && (nMonth <= 12)) &&
               ((nDay >= 1) && (nDay <= 31));

    // Detailed check of days per month
    if (fOk)
    {
        switch(nMonth)
        {
            case 1:
            case 3:
            case 5:
            case 7:
            case 8:
            case 10:
            case 12:
                break;
            case 4:
            case 6:
            case 9:
            case 11:
                fOk = (nDay <= 30);
                break;
            case 2:
                fOk = (nDay <= (UINT)(IsLeapYear(nYear) ? 29 : 28));
                break;
        }
    }

    return(fOk);
}

/***
 *
 *  CIntlTime::IsValidTime
 *
 *  Purpose:
 *
 *      Determine if the given hour, minute, second values
 *      valid.
 *
 *  Arguments:
 *
 *      int nHour       Hour
 *      int nMinute     Minute
 *      int nSecond     Second
 *
 *  Returns:
 *
 *      TRUE for a valid time, FALSE otherwise.
 *
 */

BOOL CIntlTime::IsValidTime(UINT nHour, UINT nMinute, UINT nSecond)
{
    return ((nHour < 24) && (nMinute < 60) && (nSecond < 60));
}

// Constructors.  m_fInitOk will indicate whether or not the object
// was succesfully constructed.  This can be checked at runtime by
// the IsValid() member function

CIntlTime::CIntlTime()
    :CTime()
{
    // Time set to 0, always bad.
    m_fInitOk = FALSE;
}

CIntlTime::CIntlTime(const CTime &timeSrc)
    :CTime(timeSrc)
{
    m_fInitOk = GetTime() > 0L;
}

CIntlTime::CIntlTime(time_t time)
    :CTime(time)
{
    m_fInitOk = (time > 0);
}

CIntlTime::CIntlTime(int nYear, int nMonth, int nDay, int nHour, int nMin, int nSec)
    :CTime(nYear, nMonth, nDay, nHour, nMin, nSec)
{
    m_fInitOk = IsValidDate(nMonth, nDay, nYear) && IsValidTime(nHour, nMin, nSec);
}

CIntlTime::CIntlTime(WORD wDosDate, WORD wDosTime)
    :CTime(wDosDate, wDosTime)
{
    m_fInitOk = GetTime() != 0L;
}

// Constructor taking a string as an argument. The string can contain
// either a time, a date or both.  If the string is missing the date,
// the current date will be filled in.  If the string is missing the time,
// the current time will be filled in.  As with all constructors, be
// sure the call IsValid() to determine proper contruction.

CIntlTime::CIntlTime(const CString & strTime, int nFormat, time_t * ptmOldValue)
    :CTime(ConvertFromString(strTime, nFormat, ptmOldValue,  &m_fInitOk))
{
}

CIntlTime::CIntlTime(const CIntlTime &timeSrc)
{
    CTime::operator=(timeSrc.GetTime());
    m_fInitOk = timeSrc.IsValid();
}

#ifdef _WIN32
CIntlTime::CIntlTime(const SYSTEMTIME& sysTime)
    : CTime(sysTime)
{
    m_fInitOk = IsValidDate((UINT)sysTime.wMonth, (UINT)sysTime.wDay, (UINT)sysTime.wYear)
             && IsValidTime((UINT)sysTime.wHour, (UINT)sysTime.wMinute, (UINT)sysTime.wSecond);
}

CIntlTime::CIntlTime(const FILETIME& fileTime)
    : CTime(fileTime)
{
    m_fInitOk = GetTime() != 0L;
}

#endif // _WIN32
// Desctructor
CIntlTime::~CIntlTime()
{
}

// Assignment operators.  As with constructors, be sure to check the
// IsValid() member function to determine succesfull assignment, as
// assignment operators do set the m_fInitOk member variable.

const CIntlTime& CIntlTime::operator =(const CString & strValue)
{
    time_t tmValue = ConvertFromString (strValue, CIntlTime::TFRQ_TIME_OR_DATE, NULL, &m_fInitOk);
    if (m_fInitOk)
    {
        CTime::operator=(tmValue);
    }
    return(*this);
}

// Assignment operator taking a time_t argument
const CIntlTime& CIntlTime::operator =(time_t tmValue)
{
    CTime::operator=(tmValue);
    m_fInitOk = (tmValue > 0);
    return(*this);
}

const CIntlTime& CIntlTime::operator =(const CTime & time)
{
    CTime::operator=(time.GetTime());
    m_fInitOk = (GetTime() > 0);
    return(*this);
}

const CIntlTime& CIntlTime::operator =(const CIntlTime & time)
{
    CTime::operator=(time.GetTime());
    m_fInitOk = (GetTime() > 0);
    return(*this);
}

// Conversion operators
CIntlTime::operator const time_t() const
{
    return(GetTime());
}

// Conversion operator that returns the date followed by the time
// in international format as a CString.

CIntlTime::operator const CString() const
{
    return(ConvertToString(TFRQ_TIME_AND_DATE));
}

CIntlTime::operator CString() const
{
    return(ConvertToString(TFRQ_TIME_AND_DATE));
}

/***
 *
 *  CIntlTime::GetDateString()
 *
 *  Purpose:
 *
 *      Represent the current date in a format consistent with the current
 *      international settings in a CString.
 *
 *  Returns:
 *
 *      A CString containing the date in string format, or "--" if
 *      the date is invalid.
 *
 */
const CString CIntlTime::GetDateString() const
{
    CString strIntl;

    if (!IsValid())
    {
        return(CIntlTime::m_strBadDate);
    }

    TCHAR szPct02D[] = _T("%02d");
    TCHAR szPctD[] = _T("%d");
    TCHAR szDay[3], szMonth[16], szYear[8];
    TCHAR *first, *second, *third;
    int i;

    i = GetYear();
    if(!CIntlTime::m_itsInternationalSettings.fCentury)
    {
        i %= 100;
    }
    
    // fix year 2000 problem -- ericdav
    //::_itot(i, szYear, 10);
    ::wsprintf (szYear, szPct02D, i);
    ::wsprintf (szMonth, CIntlTime::m_itsInternationalSettings.fLeadingMonthZero
                         ? szPct02D : szPctD, GetMonth());
    ::wsprintf (szDay, CIntlTime::m_itsInternationalSettings.fLeadingDayZero
                         ? szPct02D : szPctD, GetDay());

    if (CIntlTime::m_itsInternationalSettings.nDateFormat == _DFMT_YMD)
    {
        first = szYear;
        second = szMonth;
        third = szDay;
    }
    else
    {
        third = szYear;
        if (CIntlTime::m_itsInternationalSettings.nDateFormat == _DFMT_DMY)
        {
            first = szDay;
            second = szMonth;
        }
        else
        {
            first = szMonth;
            second = szDay;
        }
    }
    ::wsprintf (strIntl.GetBuffer(80),
                        _T("%s%s%s%s%s"),
                        first,
                        (LPCTSTR)CIntlTime::m_itsInternationalSettings.strDateSeperator,
                        second,
                        (LPCTSTR)CIntlTime::m_itsInternationalSettings.strDateSeperator,
                        third);
    strIntl.ReleaseBuffer();

    return(strIntl);
}

/***
 *
 *  CIntlTime::GetTimeString()
 *
 *  Purpose:
 *
 *      Represent the current time in a format consistent with the current
 *      international settings in a CString.
 *
 *  Returns:
 *
 *      A CString containing the time in string format, or "--" if
 *      the time is invalid.
 *
 */
const CString CIntlTime::GetTimeString() const
{
    CString strIntl;

    if (!IsValid())
    {
        return(CIntlTime::m_strBadTime);
    }

    int hour = GetHour();
    int minute = GetMinute();
    int second = GetSecond();

    // Set AM/PM depending on non-24 hour clock, and the time
    // of day.  Note: a space is prepended for readability.
    CString strAMPM(CIntlTime::m_itsInternationalSettings.f24HourClock
                    ? "" : " " + ((hour < 12)
                        ? CIntlTime::m_itsInternationalSettings.strAM
                        : CIntlTime::m_itsInternationalSettings.strPM)
                   );

    if ((!CIntlTime::m_itsInternationalSettings.f24HourClock) && (!(hour %= 12)))
    {
        hour = 12;
    }

    ::wsprintf (strIntl.GetBuffer(30), CIntlTime::m_itsInternationalSettings.fLeadingTimeZero
                ? _T("%02d%s%02d%s%02d%s") : _T("%d%s%02d%s%02d%s"),
                hour,
                (LPCTSTR)CIntlTime::m_itsInternationalSettings.strTimeSeperator,
                minute,
                (LPCTSTR)CIntlTime::m_itsInternationalSettings.strTimeSeperator,
                second,
                (LPCTSTR)strAMPM);

    strIntl.ReleaseBuffer();
    return(strIntl);
}

const CString CIntlTime::GetMilitaryTime() const
{
    CString strIntl;

    if (!IsValid())
    {
        return(CIntlTime::m_strBadTime);
    }

    int hour = GetHour();
    int minute = GetMinute();
    int second = GetSecond();

    ::wsprintf (strIntl.GetBuffer(30),
                _T("%02d:%02d:%02d"),
                hour,
                minute,
                second);

    strIntl.ReleaseBuffer();
    return(strIntl);
}

/***
 *
 *  CIntlTime::ConvertToString(int nFormat)
 *
 *  Purpose:
 *
 *      Convert the curent time/date to a string
 *
 *  Arguments:
 *
 *      int nFormat     Format request ID, can be one of the following
 *                      values (enumerated in CIntlTime):
 *
 *                      TFRQ_TIME_ONLY      Only give me the time.
 *                      TFRQ_DATE_ONLY      Only give me the date.
 *                      TFRQ_TIME_AND_DATE  Give me the time and the date.
 *
 *  Returns:
 *
 *      A CString containing the time and/or date in international format.
 *
 */
const CString CIntlTime::ConvertToString(int nFormat) const
{
    switch(nFormat)
    {
        case TFRQ_TIME_ONLY:
             return(GetTimeString());

        case TFRQ_DATE_ONLY:
            return(GetDateString());

        case TFRQ_TIME_AND_DATE:
            return(GetDateString() + CString(" ") + GetTimeString());

        case TFRQ_MILITARY_TIME:
            return(GetMilitaryTime());

        case TFRQ_TIME_OR_DATE:
        default:
            Trace1("Invalid time/date format code %d requested.\n", nFormat);
            return(CIntlTime::m_strBadDate);
    }
}

/***
 *
 *  CIntlTime::ConvertFromString
 *
 *  Purpose:
 *
 *      Convert a given CString into a time_t
 *
 *  Arguments:
 *
 *      const CString & str The string to convert
 *      int nFormat     Format request ID, can be one of the following
 *                      values (enumerated in CIntlTime):
 *
 *                      TFRQ_TIME_ONLY      Only give me the time.
 *                      TFRQ_DATE_ONLY      Only give me the date.
 *                      TFRQ_TIME_AND_DATE  Give me the time and the date.
 *                      TFRQ_TIME_OR_DATE   Give me time or date (or both).
 *
 *      time_t * ptmOldValue    This time_t will be used to fill in the fields
 *                      not given in the string.  If it is NULL, the current
 *                      time or date will be used.
 *      BOOL * pfOk     Returns TRUE for succesfull conversion, FALSE
 *                      otherwise.
 *
 *  Returns:
 *
 *      A time_t representing the time/date string, or 0 in case of error.
 *
 *  Notes:
 *
 *      Full validation of all paremeters will be done, e.g. No Feb 29 in
 *      a non-leap year will be accepted.
 *
 *      [CAVEAT] Time, date seperators longer than one character will not
 *      work.
 *
 */
time_t CIntlTime::ConvertFromString (
    const CString & str,
    int nFormat,
    time_t * ptmOldValue,   // If only getting time or date, count on remaining
                            // fields to be provided here (optionally);
    BOOL * pfOk)
{
    #define MAXSTRLEN 40

    TCHAR dtseps[10] ;      // Date/Time separators passed to _tcstok
    TCHAR *pchToken;
    TCHAR szDateString[MAXSTRLEN+1];
    BOOL fGotDate = FALSE;
    BOOL fGotTime = FALSE;
    BOOL fPM = FALSE;
    BOOL fAM = FALSE;
    int i;
    UINT anValues[6] = { 0, 0, 0, 0, 0, 0 };
    CTime tmTmp;

    *pfOk = FALSE;      // Assume failure.

    if (ptmOldValue != NULL)
    {
        tmTmp = *ptmOldValue;
    }
    else
    {
        tmTmp = CTime::GetCurrentTime();
    }

    if (str.GetLength() > MAXSTRLEN)
    {
        // Too long to be a proper time/date string
        return(0);
    }
    ::lstrcpy(szDateString, (LPCTSTR)str);

    int nIndex = 0;

    // If we're looking for something specific, only
    // accept specific seperators (time, date, both, either)
    if ((nFormat == TFRQ_DATE_ONLY) || (nFormat == TFRQ_TIME_AND_DATE) || (nFormat == TFRQ_TIME_OR_DATE))
    {
        dtseps[nIndex++] = '/';
        dtseps[nIndex++] = '-';
        dtseps[nIndex++] = ',';
        dtseps[nIndex++] = CIntlTime::m_itsInternationalSettings.strDateSeperator[0];
    }

    if ((nFormat == TFRQ_TIME_ONLY) || (nFormat == TFRQ_TIME_AND_DATE) || (nFormat == TFRQ_TIME_OR_DATE))
    {
        dtseps[nIndex++] = ':';
        dtseps[nIndex++] = '.';
        dtseps[nIndex++] = ' ';
        dtseps[nIndex++] = CIntlTime::m_itsInternationalSettings.strTimeSeperator[0];
    }

    ASSERT(nIndex != 0);    // Make sure we asked for something.
    if (nIndex == 0)
    {
        // Request type is illegal
        return(0);
    }
    dtseps[nIndex++] = '\0';

    Trace3("CIntlTime::ConvertFromString.  String: %s Format = %d Seps: %s\n", str, nFormat, dtseps);

    i = 0;
    pchToken = ::_tcstok(szDateString, dtseps);
    while (pchToken != NULL)
    {
        if (i > 6)        // 7 fields max (date, time + AM/PM maximum)
        {
            // Too many values, reject the string.
            return(0);
        }

        // Determine if its a number (can't _ttoi, since it will
        // merely return 0 for inappropriate values)

        BOOL fProperNumber = TRUE;
        int l = ::lstrlen(pchToken);
        if ( (l == 0) || (l == 3) || (l > 4) )
        {
            fProperNumber = FALSE;
        }
        else
        {
            int j;
            for (j=0; j < l; ++j)
            {
                if (!isdigit(*(pchToken+j)))
                {
                    fProperNumber = FALSE;
                    break;
                }
            }
        }

        if (!fProperNumber)
        {
            // Ok, this is not a proper numeric field.  Only
            // if it's AM or PM at the end of the string can this
            // string be saved.
            fGotTime = TRUE;
            if ((CIntlTime::m_itsInternationalSettings.f24HourClock) ||
                (::_tcstok(NULL, dtseps) != NULL))
            {
                return(0);
            }

            if (!CIntlTime::m_itsInternationalSettings.strAM.CompareNoCase(pchToken))
            {
                fAM = TRUE;
            }
            else if (!CIntlTime::m_itsInternationalSettings.strPM.CompareNoCase(pchToken))
            {
                fPM = TRUE;
            }
            else
            {
                // Neither AM nor PM
                return(0);
            }
            break;
        }
        else
        {
            // Value is acceptable
            anValues[i++] = (UINT)::_ttoi(pchToken);
        }

        pchToken = ::_tcstok(NULL, dtseps);
    }
    // Now what did we get, exactly?

    ASSERT(!fAM || !fPM); // Make sure we didn't set both somehow.
    if (i == 0)
    {
        // String without values
        return(0);
    }
    switch(i)
    {
        case 1:     // Hour
        case 2:     // Hour, minutes
            Trace0("We got time\n");
            fGotTime = TRUE;
            break;
        case 3:

            // This one might be ambiguous, try to intelligently decide what
            // we have.  First check if only time or date only was requested,
            // then check for out of bounds time values, and lastly check for
            // the presence of a time seperator.

            if (!fGotTime) // If we didn't already have AM/PM
            {
                Trace0("Picking between time and date by seperator\n");
                if (nFormat == TFRQ_DATE_ONLY)
                {
                    fGotDate = TRUE;
                }
                else if (nFormat == TFRQ_TIME_ONLY)
                {
                    fGotTime = TRUE;
                }
                else if ((anValues[0] > 23) || (anValues[1] > 59) || (anValues[2] > 59))
                {
                    fGotDate = TRUE;
                }
                else if (str.Find(CIntlTime::m_itsInternationalSettings.strTimeSeperator) != -1)
                {
                    fGotTime = TRUE;
                }
                else
                {
                    fGotDate = TRUE;
                }
                Trace1("Decided on %s", (fGotDate ?  "date\n" : "time\n"));
            }
            break;
        case 4: // Date, hour
        case 5: // Date, hours, minutes
        case 6: // Date, hours, minutes, seconds
            Trace0("We got date and time\n");
            fGotDate = TRUE;
            fGotTime = TRUE;
            break;
        default:
            ASSERT(0 && "Incorrect number of values!");
            return(0);
    }

    // Was that what we're looking for?
    if ( ((nFormat == TFRQ_DATE_ONLY) && fGotTime) ||
         ((nFormat == TFRQ_TIME_ONLY) && fGotDate) ||
         ((nFormat == TFRQ_TIME_AND_DATE) && (!fGotTime || !fGotDate))
       )
    {
        Trace0("Entry didn't match expectations\n");
        return(0);

    }
    i = 0;

    int h, m, s, D, M, Y;   // Array indices;
    // Now determine where to find what.
    if (fGotDate) // Date always goes first
    {
        switch(CIntlTime::m_itsInternationalSettings.nDateFormat)
        {
            case _DFMT_MDY:
                M = i++;
                D = i++;
                Y = i++;
                break;

            case _DFMT_DMY:
                D = i++;
                M = i++;
                Y = i++;
                break;

            case _DFMT_YMD:
                Y = i++;
                M = i++;
                D = i++;
                break;
        }
        // If only 2 digits are given, determine if we're talking about
        // the 21st or 20th century
        if (anValues[Y] < 100)
        {
            anValues[Y] += (anValues[Y] > 37) ? 1900 : 2000;
        }
        Trace3("Month = %d Day = %d Year = %d\n", anValues[M], anValues[D], anValues[Y]);

        // Validation.
        if (!IsValidDate(anValues[M], anValues[D], anValues[Y]))
        {
            return(0);
        }
    }

    if (fGotTime)
    {
        h = i++;
        m = i++;
        s = i++;

        Trace3("Hours = %d Minutes = %d Seconds = %d\n", anValues[h], anValues[m], anValues[s]);

        // Shouldn't specify AM or PM with 24 hour clock value.
        if ((anValues[h] > 12) && (fAM || fPM))
        {
            return(0);
        }

        // Adjust for AM/PM modifiers
        if (fPM)
        {
            if (anValues[h] != 12)
            {
                anValues[h] += 12;
            }
        }
        else if (fAM)
        {
            if ( anValues[h] == 12)
            {
                anValues[h] -= 12;
            }
        }

        // Sanity Check:
        if (!IsValidTime(anValues[h], anValues[m], anValues[s]))
        {
            return(0);
        }
    }

    // Fill in the missing fields
    CIntlTime tm( fGotDate ? anValues[Y] : tmTmp.GetYear(),
                  fGotDate ? anValues[M] : tmTmp.GetMonth(),
                  fGotDate ? anValues[D] : tmTmp.GetDay(),
                  fGotTime ? anValues[h] : tmTmp.GetHour(),
                  fGotTime ? anValues[m] : tmTmp.GetMinute(),
                  fGotTime ? anValues[s] : tmTmp.GetSecond()
                );

    *pfOk = (tm.GetTime() > (time_t)0);

    return(tm);
}

#ifdef _DEBUG

// Dump to debug device
CDumpContext& AFXAPI operator<<(CDumpContext& dc, const CIntlTime& tim)
{
    dc << _T("\nDate Seperator: ") << tim.m_itsInternationalSettings.strDateSeperator;
    dc << _T("\nTime Seperator: ") << tim.m_itsInternationalSettings.strTimeSeperator;
    dc << _T("\nAM String: ")  << tim.m_itsInternationalSettings.strAM;
    dc << _T("\nPM String: ")  << tim.m_itsInternationalSettings.strPM;
    dc << _T("\nDate Format: ") << tim.m_itsInternationalSettings.nDateFormat;
    dc << _T("\n24 Hour Clock: ")  << (tim.m_itsInternationalSettings.f24HourClock ? "TRUE" : "FALSE");
    dc << _T("\n4 Digit Century: ") << (tim.m_itsInternationalSettings.fCentury ? "TRUE" : "FALSE");
    dc << _T("\nTime Leading Zero: ")  << (tim.m_itsInternationalSettings.fLeadingTimeZero ? "TRUE" : "FALSE");
    dc << _T("\nDay Leading Zero ")  << (tim.m_itsInternationalSettings.fLeadingDayZero ? "TRUE" : "FALSE");
    dc << _T("\nMonth Leading Zero: ")  << (tim.m_itsInternationalSettings.fLeadingMonthZero ? "TRUE" : "FALSE");
    dc << _T("\n\ntime_t: ") << tim.GetTime();
    return(dc);
}

#endif // _DEBUG