mirror of
https://github.com/ladislav-zezula/FileTest
synced 2026-06-08 15:24:12 +00:00
786 lines
26 KiB
C++
786 lines
26 KiB
C++
/*****************************************************************************/
|
|
/* DateTime.cpp Copyright (c) Ladislav Zezula 2009 */
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Description: Conversion module TEXT <==> FILETIME */
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Date Ver Who Comment */
|
|
/* -------- ---- --- ------- */
|
|
/* 24.06.09 1.00 Lad Created */
|
|
/*****************************************************************************/
|
|
|
|
#include "FileTest.h"
|
|
#include "resource.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Local variables
|
|
|
|
static PSYSTEMTIME pTempSt = NULL;
|
|
static LPCTSTR szTempDateTime = NULL;
|
|
static LPTSTR szStringBufferBegin = NULL;
|
|
static LPTSTR szStringBuffer = NULL;
|
|
static LPTSTR szStringBufferEnd = NULL;
|
|
static BOOL bDateConverted = FALSE;
|
|
static BOOL bTimeConverted = FALSE;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Local functions
|
|
|
|
static bool IsSpecialFileTime(FILETIME & ft)
|
|
{
|
|
// File time with high bit set or file time with high part set to zero
|
|
return ((ft.dwHighDateTime & 0x80000000) || (ft.dwHighDateTime == 0x00000000));
|
|
}
|
|
|
|
static DWORD GetDateFormatToken(LPCTSTR szDateFormat, LPDWORD pdwTokenLength)
|
|
{
|
|
TCHAR chFirstChar = *szDateFormat;
|
|
DWORD dwEqualChars = 0;
|
|
DWORD dwToken = 0;
|
|
|
|
// Ignore empty strings
|
|
if(*szDateFormat != 0)
|
|
{
|
|
// Remember the first char from the date/time format
|
|
chFirstChar = *szDateFormat++;
|
|
dwToken = chFirstChar;
|
|
dwEqualChars++;
|
|
|
|
// Keep going till there is the same character
|
|
while(*szDateFormat == chFirstChar)
|
|
{
|
|
// Add the next character to the token
|
|
// If the token is longer than 4 equal character,
|
|
// the extra characters are simply cut.
|
|
if(dwEqualChars < 4)
|
|
{
|
|
dwToken = (dwToken << 0x08) | *szDateFormat;
|
|
dwEqualChars++;
|
|
}
|
|
|
|
// Nove to the next character in the token
|
|
szDateFormat++;
|
|
}
|
|
}
|
|
|
|
// Return the result
|
|
if(pdwTokenLength != NULL)
|
|
*pdwTokenLength = dwEqualChars;
|
|
return dwToken;
|
|
}
|
|
|
|
static WORD LocaleNameToNumber(LPCTSTR szStr, LCID lcMin, LCID lcMax, LCID lcExtra, int * piNameLength)
|
|
{
|
|
TCHAR szLocaleName[80];
|
|
LCID lcid;
|
|
int nLength;
|
|
|
|
// Set the length to zero as default
|
|
*piNameLength = 0;
|
|
|
|
// We have to go from December downto January. THe reason behind this
|
|
// is that we might confuse "1" with "10".
|
|
for(lcid = lcMax; lcid >= lcMin; lcid--)
|
|
{
|
|
// Get the localized month name
|
|
nLength = GetLocaleInfo(LOCALE_USER_DEFAULT, lcid, szLocaleName, _countof(szLocaleName));
|
|
if(nLength == 0)
|
|
break;
|
|
|
|
// If the month name is identical, we found it
|
|
if(!_tcsnicmp(szStr, szLocaleName, (nLength - 1)))
|
|
{
|
|
*piNameLength = (nLength - 1);
|
|
return (WORD)(lcid - lcMin + 1);
|
|
}
|
|
}
|
|
|
|
// Last chance: check extra locale (for example, 13th month name)
|
|
if(lcExtra != 0)
|
|
{
|
|
// Get the localized 13th month name
|
|
nLength = GetLocaleInfo(LOCALE_USER_DEFAULT, lcExtra, szLocaleName, _countof(szLocaleName));
|
|
if(nLength > 1)
|
|
{
|
|
// If the month name is identical, we found it
|
|
if(!_tcsnicmp(szStr, szLocaleName, (nLength - 1)))
|
|
{
|
|
*piNameLength = (nLength - 1);
|
|
return 13;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Invalid or unrecognized locale name
|
|
return 0xFFFF;
|
|
}
|
|
|
|
// Converts locale-specific month name to month number
|
|
// Only converts genitives
|
|
static WORD MonthNameGenitiveToNumber(LPCTSTR szStr, int * piMonthNameLength)
|
|
{
|
|
SYSTEMTIME stTemp = {0};
|
|
TCHAR szMonthName[80];
|
|
int nLength;
|
|
|
|
// Prepare the temporary SYSTEMTIME structure
|
|
// Note: The recommended way to get month name as genitive is to use GetDateFormat
|
|
// with "ddMMMM" and compare month name starting at second position
|
|
stTemp.wDay = 1;
|
|
stTemp.wYear = 2000;
|
|
|
|
// Go through all month names.
|
|
for(stTemp.wMonth = 1; stTemp.wMonth <= 13; stTemp.wMonth++)
|
|
{
|
|
// Get genitive of month name
|
|
nLength = GetDateFormat(LOCALE_USER_DEFAULT, 0, &stTemp, _T("ddMMMM"), szMonthName, _countof(szMonthName));
|
|
if(nLength < 3)
|
|
break;
|
|
|
|
// Compare the month name
|
|
if(!_tcsnicmp(szStr, &szMonthName[2], nLength - 3))
|
|
{
|
|
*piMonthNameLength = (nLength - 3);
|
|
return stTemp.wMonth;
|
|
}
|
|
}
|
|
|
|
// Month name genitive not recognized
|
|
return 0xFFFF;
|
|
}
|
|
|
|
// Try to recognize the AM/PM string
|
|
static BOOL AmPmNameToWord(LPCTSTR szStr, WORD & wHourModifier, int & nAmPmNameLength)
|
|
{
|
|
TCHAR szAmPmStr[10];
|
|
int nLength;
|
|
|
|
// Check AM
|
|
nLength = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_S1159, szAmPmStr, _countof(szAmPmStr));
|
|
if(nLength > 0 && szStr[0] == szAmPmStr[0])
|
|
{
|
|
nAmPmNameLength = nLength;
|
|
wHourModifier = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
// Check PM
|
|
nLength = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_S2359, szAmPmStr, _countof(szAmPmStr));
|
|
if(nLength > 0 && szStr[0] == szAmPmStr[0])
|
|
{
|
|
nAmPmNameLength = nLength;
|
|
wHourModifier = 12;
|
|
return TRUE;
|
|
}
|
|
|
|
// Not recognized
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL ConvertTextToDate(PSYSTEMTIME pSt, LPCTSTR szDateTimeStr, LPTSTR szDateFormat)
|
|
{
|
|
SYSTEMTIME st = {0};
|
|
LPTSTR szEndChar;
|
|
DWORD dwTokenLength;
|
|
DWORD dwToken;
|
|
BOOL bMonthConverted = FALSE;
|
|
BOOL bYearConverted = FALSE;
|
|
BOOL bDayConverted = FALSE;
|
|
WORD wDummy;
|
|
int nLength;
|
|
|
|
// Remove any leading spaces from szDateTimeStr
|
|
while(*szDateTimeStr == _T(' '))
|
|
szDateTimeStr++;
|
|
|
|
while(*szDateFormat != 0 && *szDateTimeStr != 0)
|
|
{
|
|
// Skip spaces
|
|
while(szDateTimeStr[0] == _T(' '))
|
|
szDateTimeStr++;
|
|
while(szDateFormat[0] == _T(' '))
|
|
szDateFormat++;
|
|
|
|
dwToken = GetDateFormatToken(szDateFormat, &dwTokenLength);
|
|
switch(dwToken)
|
|
{
|
|
// "d": Day of month as digits with no leading zero for single-digit days.
|
|
// "dd" : Day of month as digits with leading zero for single-digit days.
|
|
case 0x64: // 'd'
|
|
case 0x6464: // 'dd'
|
|
{
|
|
// Day must not be there yet
|
|
if(bDayConverted)
|
|
return FALSE;
|
|
|
|
// Store day number to SYSTEMTIME structure
|
|
st.wDay = (WORD)StrToInt(szDateTimeStr, &szEndChar, 10);
|
|
if(IsCharAlpha(szEndChar[0]) || st.wDay == 0 || st.wDay > 31)
|
|
return FALSE;
|
|
|
|
szDateTimeStr = szEndChar;
|
|
szDateFormat += dwTokenLength;
|
|
bDayConverted = TRUE;
|
|
break;
|
|
}
|
|
|
|
// "ddd": Day of week as a three-letter abbreviation (LOCALE_SABBREVDAYNAME)
|
|
// "dddd": Day of week as its full name (LOCALE_SDAYNAME).
|
|
case 0x646464: // 'ddd'
|
|
case 0x64646464: // 'dddd'
|
|
{
|
|
// Convert the day of week. However, ignore the result of the conversion,
|
|
// as day of week is not necessary to get the FILETIME and would require
|
|
// the user to enter correct day of week
|
|
wDummy = LocaleNameToNumber(szDateTimeStr, LOCALE_SDAYNAME1, LOCALE_SDAYNAME7, 0, &nLength);
|
|
if(wDummy == 0xFFFF)
|
|
return FALSE;
|
|
|
|
szDateFormat += dwTokenLength;
|
|
szDateTimeStr += nLength;
|
|
break;
|
|
}
|
|
|
|
// "M": Month as digits with no leading zero for single-digit months.
|
|
// "MM": Month as digits with leading zero for single-digit months.
|
|
// "MMM": Month as a three-letter abbreviation. The function
|
|
// uses the LOCALE_SABBREVMONTHNAME value associated with the specified locale.
|
|
// "MMMM": Month as its full name. The function uses the LOCALE_SMONTHNAME
|
|
// value associated with the specified locale.
|
|
case 0x4D: // 'M'
|
|
case 0x4D4D: // 'MM'
|
|
case 0x4D4D4D: // 'MMM'
|
|
case 0x4D4D4D4D: // 'MMMM'
|
|
{
|
|
// Month must not be there yet
|
|
if(bMonthConverted)
|
|
return FALSE;
|
|
|
|
// Try to convert it from genitive
|
|
st.wMonth = MonthNameGenitiveToNumber(szDateTimeStr, &nLength);
|
|
if(1 <= st.wMonth && st.wMonth <= 13)
|
|
{
|
|
szDateFormat += dwTokenLength;
|
|
szDateTimeStr += nLength;
|
|
bMonthConverted = TRUE;
|
|
break;
|
|
}
|
|
|
|
// Try to convert it from nominative of the full name
|
|
st.wMonth = LocaleNameToNumber(szDateTimeStr, LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME12, LOCALE_SMONTHNAME13, &nLength);
|
|
if(1 <= st.wMonth && st.wMonth <= 13)
|
|
{
|
|
szDateFormat += dwTokenLength;
|
|
szDateTimeStr += nLength;
|
|
bMonthConverted = TRUE;
|
|
break;
|
|
}
|
|
|
|
// Try to convert it from the short name
|
|
st.wMonth = LocaleNameToNumber(szDateTimeStr, LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME12, LOCALE_SABBREVMONTHNAME13, &nLength);
|
|
if(1 <= st.wMonth && st.wMonth <= 13)
|
|
{
|
|
szDateFormat += dwTokenLength;
|
|
szDateTimeStr += nLength;
|
|
bMonthConverted = TRUE;
|
|
break;
|
|
}
|
|
|
|
// Try to convert it from the number
|
|
st.wMonth = (WORD)StrToInt(szDateTimeStr, &szEndChar, 10);
|
|
if(IsCharAlpha(szEndChar[0]) || st.wMonth == 0 || st.wMonth > 12)
|
|
return FALSE;
|
|
|
|
szDateFormat += dwTokenLength;
|
|
szDateTimeStr = szEndChar;
|
|
bMonthConverted = TRUE;
|
|
break;
|
|
}
|
|
|
|
// "y": Year as last two digits, but with no leading zero for years less than 10.
|
|
// "yy": Year as last two digits, but with leading zero for years less than 10.
|
|
// "yyy": Invalid year, but we accept it
|
|
// "yyyy": Year represented by full four digits.
|
|
case 0x79: // 'y'
|
|
case 0x7979: // 'yy'
|
|
case 0x797979: // 'yyy'
|
|
case 0x79797979: // 'yyyy'
|
|
{
|
|
// Convert year as digit
|
|
st.wYear = (WORD)StrToInt(szDateTimeStr, &szEndChar, 10);
|
|
if(IsCharAlpha(szEndChar[0]))
|
|
return FALSE;
|
|
|
|
// Respect 2-digit years
|
|
if(dwTokenLength == 1 || dwTokenLength == 2)
|
|
{
|
|
if(80 <= st.wYear && st.wYear < 100)
|
|
st.wYear += 1900;
|
|
else
|
|
st.wYear += 2000;
|
|
}
|
|
|
|
// We don't accept years less than 1600
|
|
if(st.wYear < 1600)
|
|
return FALSE;
|
|
bYearConverted = TRUE;
|
|
szDateTimeStr = szEndChar;
|
|
szDateFormat += dwTokenLength;
|
|
break;
|
|
}
|
|
|
|
// All other chars are considered unexpected
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
// Skip equal characters
|
|
while(szDateTimeStr[0] != 0 && szDateTimeStr[0] == szDateFormat[0])
|
|
{
|
|
szDateTimeStr++;
|
|
szDateFormat++;
|
|
}
|
|
}
|
|
|
|
// If all three (day-month-year) were converted, we're done
|
|
if(bDayConverted && bMonthConverted && bYearConverted)
|
|
{
|
|
szTempDateTime = szDateTimeStr;
|
|
pSt->wDay = st.wDay;
|
|
pSt->wMonth = st.wMonth;
|
|
pSt->wYear = st.wYear;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL ConvertTextToTime(PSYSTEMTIME pSt, LPCTSTR szDateTimeStr, LPTSTR szTimeFormat)
|
|
{
|
|
SYSTEMTIME st = {0};
|
|
LPTSTR szEndChar;
|
|
DWORD dwTokenLength;
|
|
DWORD dwToken;
|
|
WORD wHourModifier = 0; // 0 or 12, depends on AM/PM string
|
|
BOOL bHourConverted = FALSE;
|
|
BOOL bMinuteConverted = FALSE;
|
|
BOOL bSecondConverted = FALSE;
|
|
BOOL b12HourFormat = FALSE;
|
|
BOOL bAmPmFound = FALSE;
|
|
int nLength;
|
|
|
|
// Remove any leading spaces from szDateTimeStr
|
|
while(*szDateTimeStr == _T(' '))
|
|
szDateTimeStr++;
|
|
|
|
while(*szTimeFormat != 0 && *szDateTimeStr != 0)
|
|
{
|
|
// Skip spaces
|
|
while(szDateTimeStr[0] == _T(' '))
|
|
szDateTimeStr++;
|
|
while(szTimeFormat[0] == _T(' '))
|
|
szTimeFormat++;
|
|
|
|
dwToken = GetDateFormatToken(szTimeFormat, &dwTokenLength);
|
|
switch(dwToken)
|
|
{
|
|
// "h": Hours with no leading zero for single-digit hours; 12-hour clock.
|
|
// "hh" : Hours with leading zero for single-digit hours; 12-hour clock.
|
|
case 0x68: // 'h'
|
|
case 0x6868: // 'hh'
|
|
{
|
|
// Store day number to SYSTEMTIME structure
|
|
st.wHour = (WORD)StrToInt(szDateTimeStr, &szEndChar, 10);
|
|
if(IsCharAlpha(szEndChar[0]) || st.wHour > 12)
|
|
return FALSE;
|
|
|
|
b12HourFormat = TRUE;
|
|
szDateTimeStr = szEndChar;
|
|
szTimeFormat += dwTokenLength;
|
|
break;
|
|
}
|
|
|
|
// "H": Hours with no leading zero for single-digit hours; 24-hour clock.
|
|
// "HH": Hours with leading zero for single-digit hours; 24-hour clock.
|
|
case 0x48: // 'H'
|
|
case 0x4848: // 'HH'
|
|
{
|
|
// Store day number to SYSTEMTIME structure
|
|
st.wHour = (WORD)StrToInt(szDateTimeStr, &szEndChar, 10);
|
|
if(IsCharAlpha(szEndChar[0]) || st.wHour > 23)
|
|
return FALSE;
|
|
|
|
bHourConverted = TRUE;
|
|
szDateTimeStr = szEndChar;
|
|
szTimeFormat += dwTokenLength;
|
|
break;
|
|
}
|
|
|
|
// "m": Minutes with no leading zero for single-digit minutes.
|
|
// "mm": Minutes with leading zero for single-digit minutes.
|
|
case 0x6D: // 'm'
|
|
case 0x6D6D: // 'mm'
|
|
{
|
|
// Store day number to SYSTEMTIME structure
|
|
st.wMinute = (WORD)StrToInt(szDateTimeStr, &szEndChar, 10);
|
|
if(IsCharAlpha(szEndChar[0]) || st.wMinute > 59)
|
|
return FALSE;
|
|
|
|
bMinuteConverted = TRUE;
|
|
szDateTimeStr = szEndChar;
|
|
szTimeFormat += dwTokenLength;
|
|
break;
|
|
}
|
|
|
|
|
|
// "s": Seconds with no leading zero for single-digit seconds.
|
|
// "ss": Seconds with leading zero for single-digit seconds.
|
|
case 0x73: // 's'
|
|
case 0x7373: // 'ss'
|
|
{
|
|
// Store day number to SYSTEMTIME structure
|
|
st.wSecond = (WORD)StrToInt(szDateTimeStr, &szEndChar, 10);
|
|
if(IsCharAlpha(szEndChar[0]) || st.wSecond > 59)
|
|
return FALSE;
|
|
|
|
bSecondConverted = TRUE;
|
|
szDateTimeStr = szEndChar;
|
|
szTimeFormat += dwTokenLength;
|
|
break;
|
|
}
|
|
|
|
// "t": One character time-marker string, such as A or P.
|
|
// "tt": Multicharacter time-marker string, such as AM or PM.
|
|
case 0x74: // 't'
|
|
case 0x7474: // 'tt'
|
|
{
|
|
// Try to recognize the string
|
|
if(AmPmNameToWord(szDateTimeStr, wHourModifier, nLength) == FALSE)
|
|
return FALSE;
|
|
|
|
bAmPmFound = TRUE;
|
|
szDateTimeStr += nLength;
|
|
szTimeFormat += dwTokenLength;
|
|
break;
|
|
}
|
|
|
|
// All other chars are considered unexpected
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
// Skip equal characters
|
|
while(szDateTimeStr[0] != 0 && szDateTimeStr[0] == szTimeFormat[0])
|
|
{
|
|
szDateTimeStr++;
|
|
szTimeFormat++;
|
|
}
|
|
|
|
// If both 12-hour and AM/PM string have been found,
|
|
// consider hour as converted
|
|
if(bHourConverted == FALSE && b12HourFormat && bAmPmFound)
|
|
{
|
|
st.wHour = st.wHour + wHourModifier;
|
|
bHourConverted = TRUE;
|
|
}
|
|
}
|
|
|
|
// If all three (hour-minute-second) were converted, we're done
|
|
if(bHourConverted && bMinuteConverted && bSecondConverted)
|
|
{
|
|
pSt->wHour = st.wHour;
|
|
pSt->wMinute = st.wMinute;
|
|
pSt->wSecond = st.wSecond;
|
|
return TRUE;
|
|
}
|
|
|
|
// If only hour and minute have been converted, it's OK
|
|
if(bHourConverted && bMinuteConverted && bSecondConverted == FALSE)
|
|
{
|
|
pSt->wHour = st.wHour;
|
|
pSt->wMinute = st.wMinute;
|
|
pSt->wSecond = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
// If time is not present at all, we consider this as OK
|
|
if(bHourConverted == FALSE && bMinuteConverted == FALSE && bSecondConverted == FALSE )
|
|
{
|
|
pSt->wHour = 0;
|
|
pSt->wMinute = 0;
|
|
pSt->wSecond = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static BOOL CALLBACK CollectFormatsProc(LPTSTR szFormat)
|
|
{
|
|
if(wcsstr(szStringBufferBegin, szFormat) == NULL)
|
|
{
|
|
while(szStringBuffer < szStringBufferEnd && *szFormat != 0)
|
|
*szStringBuffer++ = *szFormat++;
|
|
|
|
if(szStringBuffer < szStringBufferEnd)
|
|
*szStringBuffer++ = _T('\n');
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL CALLBACK EnumDateFormatsProc(LPTSTR szDateFormat)
|
|
{
|
|
if(ConvertTextToDate(pTempSt, szTempDateTime, szDateFormat))
|
|
{
|
|
bDateConverted = TRUE;
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL CALLBACK EnumTimeFormatsProc(LPTSTR szTimeFormat)
|
|
{
|
|
if(ConvertTextToTime(pTempSt, szTempDateTime, szTimeFormat))
|
|
{
|
|
bTimeConverted = TRUE;
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static LPTSTR FileTimeToLargeInteger(
|
|
LPTSTR szBuffer,
|
|
LPTSTR szEndChar,
|
|
PFILETIME pFt)
|
|
{
|
|
TCHAR szTemp[0x20];
|
|
|
|
// Format the filetime
|
|
StringCchPrintf(szTemp, _countof(szTemp), _T("%08lX-%08lX"), pFt->dwHighDateTime, pFt->dwLowDateTime);
|
|
|
|
// Copy to target buffer
|
|
for(int i = 0; szTemp[i] != 0; i++)
|
|
{
|
|
if(szEndChar > szBuffer)
|
|
*szBuffer++ = szTemp[i];
|
|
}
|
|
|
|
// Return the new buffer pointer
|
|
return szBuffer;
|
|
}
|
|
|
|
static int FileTimeToHumanReadableText(
|
|
LPTSTR szBuffer,
|
|
LPTSTR szEndChar,
|
|
PFILETIME pFt,
|
|
BOOL bEncloseInParentheses)
|
|
{
|
|
SYSTEMTIME st;
|
|
FILETIME lft;
|
|
LPTSTR szSaveBuffer = szBuffer;
|
|
int nLength;
|
|
|
|
// Verify special FILETIME values
|
|
if(IsSpecialFileTime(*pFt))
|
|
return 0;
|
|
|
|
// Convert the filetime to local file time
|
|
if(!FileTimeToLocalFileTime(pFt, &lft))
|
|
return 0;
|
|
if(!FileTimeToSystemTime(&lft, &st))
|
|
return 0;
|
|
|
|
// Add the opening parenthesis, if needed
|
|
if(bEncloseInParentheses && szEndChar > szBuffer)
|
|
*szBuffer++ = _T('(');
|
|
|
|
// Format the date
|
|
nLength = GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, szBuffer, (int)(szEndChar - szBuffer));
|
|
if(nLength > 0)
|
|
szBuffer += (nLength - 1);
|
|
|
|
// Add space as separator
|
|
if(szEndChar > szBuffer)
|
|
*szBuffer++ = _T(' ');
|
|
|
|
// Format the time
|
|
nLength = GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, NULL, szBuffer, (int)(szEndChar - szBuffer));
|
|
if(nLength > 0)
|
|
szBuffer += (nLength - 1);
|
|
|
|
// Add closing bracket
|
|
if(bEncloseInParentheses && szEndChar > szBuffer)
|
|
*szBuffer++ = _T(')');
|
|
|
|
return (int)(szBuffer - szSaveBuffer);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Public functions
|
|
|
|
LPTSTR FileTimeToText(
|
|
LPTSTR szBuffer,
|
|
LPTSTR szBufferEnd,
|
|
PFILETIME pFt,
|
|
BOOL bTextForEdit)
|
|
{
|
|
int nLength;
|
|
|
|
if(bTextForEdit == FALSE)
|
|
{
|
|
// First part: date as LARGE_INTEGER
|
|
szBuffer = FileTimeToLargeInteger(szBuffer, szBufferEnd, pFt);
|
|
|
|
// Add one space
|
|
if(szBuffer < szBufferEnd)
|
|
*szBuffer++ = _T(' ');
|
|
|
|
// Append the filetime in human-readable form
|
|
nLength = FileTimeToHumanReadableText(szBuffer,
|
|
szBufferEnd,
|
|
pFt,
|
|
TRUE);
|
|
szBuffer += nLength;
|
|
}
|
|
else
|
|
{
|
|
// Attempt to convert the filetime to human-readable form
|
|
nLength = FileTimeToHumanReadableText(szBuffer,
|
|
szBufferEnd,
|
|
pFt,
|
|
FALSE);
|
|
|
|
// If failed, just convert it to LARGE_INTEGER
|
|
if(nLength == 0)
|
|
{
|
|
szBuffer = FileTimeToLargeInteger(szBuffer, szBufferEnd, pFt);
|
|
}
|
|
else
|
|
{
|
|
szBuffer += nLength;
|
|
}
|
|
}
|
|
|
|
// Terminate the buffer with zero
|
|
if(szBuffer < szBufferEnd)
|
|
szBuffer[0] = 0;
|
|
return szBuffer;
|
|
}
|
|
|
|
NTSTATUS TextToFileTime(LPCTSTR szText, PFILETIME pFt)
|
|
{
|
|
SYSTEMTIME st = {0};
|
|
FILETIME ft;
|
|
LPTSTR szEndChar = NULL;
|
|
|
|
// Prepare the local variables to be filled by callbacks
|
|
bDateConverted = FALSE;
|
|
bTimeConverted = FALSE;
|
|
szTempDateTime = szText;
|
|
pTempSt = &st;
|
|
|
|
// Check for "current"
|
|
if(!_tcsicmp(szText, _T("current")))
|
|
{
|
|
GetSystemTimeAsFileTime(pFt);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// Check for "-1" and "-2"
|
|
if(szText[0] == _T('-'))
|
|
{
|
|
LARGE_INTEGER IntValue64;
|
|
|
|
IntValue64.QuadPart = _tcstol(szText, &szEndChar, 10);
|
|
if(szEndChar[0] == 0)
|
|
{
|
|
pFt->dwHighDateTime = IntValue64.HighPart;
|
|
pFt->dwLowDateTime = IntValue64.LowPart;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
// Try to convert the filetime from human-readable form
|
|
if(bDateConverted == FALSE)
|
|
EnumDateFormats(EnumDateFormatsProc, LOCALE_USER_DEFAULT, DATE_SHORTDATE);
|
|
if(bDateConverted == FALSE)
|
|
EnumDateFormats(EnumDateFormatsProc, LOCALE_USER_DEFAULT, DATE_LONGDATE);
|
|
if(bTimeConverted == FALSE)
|
|
EnumTimeFormats(EnumTimeFormatsProc, LOCALE_USER_DEFAULT, 0);
|
|
|
|
// If both date and time converted, we have succeeded
|
|
if(bDateConverted && bTimeConverted)
|
|
{
|
|
if(!SystemTimeToFileTime(&st, &ft))
|
|
return STATUS_INVALID_DATA_FORMAT;
|
|
if(!LocalFileTimeToFileTime(&ft, pFt))
|
|
return STATUS_INVALID_DATA_FORMAT;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// Try to convert 64-bit value in the form of ################ or 0x################
|
|
if(Text2Hex64(szText, (PLONGLONG)&ft) != ERROR_SUCCESS)
|
|
{
|
|
// Try to convert the 64-bit value in the form of ########-########
|
|
ft.dwHighDateTime = StrToInt(szText, &szEndChar, 16);
|
|
if(szEndChar == NULL || szEndChar[0] != _T('-'))
|
|
return STATUS_INVALID_DATA_FORMAT;
|
|
|
|
ft.dwLowDateTime = StrToInt(szEndChar+1, &szEndChar, 16);
|
|
if(szEndChar[0] != 0 && szEndChar[0] != _T(' '))
|
|
return STATUS_INVALID_DATA_FORMAT;
|
|
}
|
|
|
|
// Some values are accepted as-is ("00000000-00000000", "FFFFFFFF-FFFFFFFE", "FFFFFFFF-FFFFFFFF")
|
|
if(IsSpecialFileTime(ft))
|
|
{
|
|
pFt->dwHighDateTime = ft.dwHighDateTime;
|
|
pFt->dwLowDateTime = ft.dwLowDateTime;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// Convert from local time to file time
|
|
if(LocalFileTimeToFileTime(&ft, pFt))
|
|
return STATUS_SUCCESS;
|
|
|
|
// Conversion failed
|
|
return STATUS_INVALID_DATA_FORMAT;
|
|
}
|
|
|
|
BOOL GetSupportedDateTimeFormats(
|
|
LPCTSTR szDateFormatPrefix,
|
|
LPCTSTR szTimeFormatPrefix,
|
|
LPTSTR szBuffer,
|
|
int nMaxChars)
|
|
{
|
|
// Initialize the global strings
|
|
szStringBufferBegin = szBuffer;
|
|
szStringBuffer = szBuffer;
|
|
szStringBufferEnd = szBuffer + nMaxChars - 1;
|
|
|
|
// Put the date format prefix, if any
|
|
if(szStringBuffer != NULL)
|
|
{
|
|
while(szStringBuffer < szStringBufferEnd && *szDateFormatPrefix != 0)
|
|
*szStringBuffer++ = *szDateFormatPrefix++;
|
|
}
|
|
|
|
// Put the date formats
|
|
EnumDateFormats(CollectFormatsProc, LOCALE_USER_DEFAULT, DATE_SHORTDATE);
|
|
EnumDateFormats(CollectFormatsProc, LOCALE_USER_DEFAULT, DATE_LONGDATE);
|
|
|
|
// Put the time format prefix, if any
|
|
if(szStringBuffer != NULL)
|
|
{
|
|
while(szStringBuffer < szStringBufferEnd && *szTimeFormatPrefix != 0)
|
|
*szStringBuffer++ = *szTimeFormatPrefix++;
|
|
}
|
|
|
|
// Put the time formats
|
|
EnumTimeFormats(CollectFormatsProc, LOCALE_USER_DEFAULT, 0);
|
|
|
|
// Terminate the string and return
|
|
*szStringBuffer = 0;
|
|
return TRUE;
|
|
}
|