Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1385 lines
33 KiB

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name :
datetime.cxx
Abstract:
This module exports common functions for date and time fields,
Expanding into strings and manipulation.
Author:
Murali R. Krishnan ( MuraliK ) 3-Jan-1995
--*/
#include "precomp.hxx"
#include <stdlib.h>
#include <pudebug.h>
# if !defined(dllexp)
# define dllexp __declspec( dllexport)
# endif
#define DLL_IMPLEMENTATION
#define IMPLEMENTATION_EXPORT
#include <datetime.hxx>
#include <inetsvcs.h>
#include "date.hxx"
class dllexp CDateTime
{
public:
FILETIME_UINT64 m_ftu;
SYSTEMTIME m_st;
CDateTime()
{ /* do nothing */ }
CDateTime(const SYSTEMTIME& rst)
{ SetTime(rst); }
CDateTime(const FILETIME& rft)
{ SetTime(rft); }
CDateTime(const FILETIME& rft, const SYSTEMTIME& rst)
{ m_ftu.ft = rft; m_st = rst; }
BOOL
GetCurrentTime()
{
GetSystemTimeAsFileTime(&m_ftu.ft);
return FileTimeToSystemTime(&m_ftu.ft, &m_st);
}
BOOL
SetTime(const SYSTEMTIME& rst)
{ m_st = rst; return SystemTimeToFileTime(&m_st, &m_ftu.ft); }
BOOL
SetTime(const FILETIME& rft)
{ m_ftu.ft = rft; return FileTimeToSystemTime(&m_ftu.ft, &m_st); }
};
static const CHAR g_rgchTwoDigits[100][2] =
{
{ '0', '0' }, { '0', '1' }, { '0', '2' }, { '0', '3' }, { '0', '4' },
{ '0', '5' }, { '0', '6' }, { '0', '7' }, { '0', '8' }, { '0', '9' },
{ '1', '0' }, { '1', '1' }, { '1', '2' }, { '1', '3' }, { '1', '4' },
{ '1', '5' }, { '1', '6' }, { '1', '7' }, { '1', '8' }, { '1', '9' },
{ '2', '0' }, { '2', '1' }, { '2', '2' }, { '2', '3' }, { '2', '4' },
{ '2', '5' }, { '2', '6' }, { '2', '7' }, { '2', '8' }, { '2', '9' },
{ '3', '0' }, { '3', '1' }, { '3', '2' }, { '3', '3' }, { '3', '4' },
{ '3', '5' }, { '3', '6' }, { '3', '7' }, { '3', '8' }, { '3', '9' },
{ '4', '0' }, { '4', '1' }, { '4', '2' }, { '4', '3' }, { '4', '4' },
{ '4', '5' }, { '4', '6' }, { '4', '7' }, { '4', '8' }, { '4', '9' },
{ '5', '0' }, { '5', '1' }, { '5', '2' }, { '5', '3' }, { '5', '4' },
{ '5', '5' }, { '5', '6' }, { '5', '7' }, { '5', '8' }, { '5', '9' },
{ '6', '0' }, { '6', '1' }, { '6', '2' }, { '6', '3' }, { '6', '4' },
{ '6', '5' }, { '6', '6' }, { '6', '7' }, { '6', '8' }, { '6', '9' },
{ '7', '0' }, { '7', '1' }, { '7', '2' }, { '7', '3' }, { '7', '4' },
{ '7', '5' }, { '7', '6' }, { '7', '7' }, { '7', '8' }, { '7', '9' },
{ '8', '0' }, { '8', '1' }, { '8', '2' }, { '8', '3' }, { '8', '4' },
{ '8', '5' }, { '8', '6' }, { '8', '7' }, { '8', '8' }, { '8', '9' },
{ '9', '0' }, { '9', '1' }, { '9', '2' }, { '9', '3' }, { '9', '4' },
{ '9', '5' }, { '9', '6' }, { '9', '7' }, { '9', '8' }, { '9', '9' },
};
//
// Constants
//
#define APPEND_STR(a,b) \
{CopyMemory(a,b,sizeof(b)); a += sizeof(b)-sizeof(CHAR);}
#define APPEND_PSZ( pszTail, psz ) \
{ DWORD cb = strlen( psz ); \
CopyMemory( (pszTail), (psz), cb + 1 );\
(pszTail) += cb; \
}
//
// Makes a two-digit zero padded number (i.e., "23", or "05")
//
inline
VOID
AppendTwoDigits(
CHAR*& rpszTail,
DWORD Num
)
{
if ( Num < 100 )
{
rpszTail[0] = g_rgchTwoDigits[Num][0];
rpszTail[1] = g_rgchTwoDigits[Num][1];
rpszTail[2] = '\0';
rpszTail += 2;
}
else
{
DBG_ASSERT(!"Num >= 100");
}
}
//
// Years conversion
//
#define MAX_CACHED_YEARS 32
static DWORD g_nMinYear = 0, g_nMaxYear = 0;
static char g_aszYears[MAX_CACHED_YEARS][4+1];
typedef CDataCache<CDateTime> CCacheTime;
static CCacheTime g_ctCurrentTime;
void
InitializeDateTime()
{
SYSTEMTIME st;
GetSystemTime(&st);
g_nMinYear = st.wYear - MAX_CACHED_YEARS / 2;
g_nMaxYear = g_nMinYear + MAX_CACHED_YEARS - 1;
DBG_ASSERT(1000 <= g_nMinYear && g_nMaxYear <= 9999);
for (DWORD i = g_nMinYear; i <= g_nMaxYear; i++)
{
_itoa( i, g_aszYears[i - g_nMinYear], 10 );
}
CDateTime dt(st);
g_ctCurrentTime.Write(dt);
}
void
TerminateDateTime()
{
// nothing to be done, at least for now
}
inline
VOID
AppendYear(
CHAR* &rpszTail,
DWORD dwYear
)
{
DBG_ASSERT(g_nMinYear >= 1000);
DWORD i = dwYear - g_nMinYear;
if (i < MAX_CACHED_YEARS)
{
DBG_ASSERT(g_nMinYear <= dwYear && dwYear <= g_nMaxYear);
const char* pszYear = g_aszYears[i];
*rpszTail++ = *pszYear++;
*rpszTail++ = *pszYear++;
*rpszTail++ = *pszYear++;
*rpszTail++ = *pszYear++;
*rpszTail = '\0';
}
else
{
CHAR __ach[32];
DBG_ASSERT( dwYear >= 1000 && dwYear <= 9999 );
_itoa( dwYear, __ach, 10 );
CopyMemory( rpszTail, __ach, 4+1 );
rpszTail += 4;
}
}
// Since ::GetSystemTime is relatively expensive (310 instructions) and
// ::GetSystemTimeAsFileTime is pretty cheap (20 instructions), we cache
// the SYSTEMTIME representation of the current time with an accuracy of
// 1.0 seconds.
BOOL
IISGetCurrentTime(
OUT FILETIME* pft,
OUT SYSTEMTIME* pst)
{
BOOL fUpdatedCachedTime = FALSE;
CDateTime dt;
while (! g_ctCurrentTime.Read(dt))
{
// empty loop
}
FILETIME_UINT64 ftu;
GetSystemTimeAsFileTime(&ftu.ft);
if (ftu.u64 - dt.m_ftu.u64 >= FILETIME_1_SECOND)
{
#undef WT_INSTRUCTION_COUNTS
#ifndef WT_INSTRUCTION_COUNTS
fUpdatedCachedTime = TRUE;
dt.SetTime(ftu.ft);
g_ctCurrentTime.Write(dt);
#endif
}
if (pft != NULL)
*pft = dt.m_ftu.ft;
if (pst != NULL)
*pst = dt.m_st;
return fUpdatedCachedTime;
}
/************************************************************
* Data
************************************************************/
static const TCHAR* s_rgchDays[] = {
TEXT("Sun"), TEXT("Mon"), TEXT("Tue"), TEXT("Wed"),
TEXT("Thu"), TEXT("Fri"), TEXT("Sat")
};
static const TCHAR* s_rgchMonths[] = {
TEXT("Jan"), TEXT("Feb"), TEXT("Mar"), TEXT("Apr"),
TEXT("May"), TEXT("Jun"), TEXT("Jul"), TEXT("Aug"),
TEXT("Sep"), TEXT("Oct"), TEXT("Nov"), TEXT("Dec")
};
LPCTSTR
DayOfWeek3CharNames(DWORD dwDayOfWeek)
{
return s_rgchDays[dwDayOfWeek];
}
LPCTSTR
Month3CharNames(DWORD dwMonth)
{
return s_rgchMonths[dwMonth];
}
// Custom hash table for make_month() for mapping "Apr" to 4
static const CHAR MonthIndexTable[64] = {
-1,'A', 2, 12, -1, -1, -1, 8, // A to G
-1, -1, -1, -1, 7, -1,'N', -1, // F to O
9, -1,'R', -1, 10, -1, 11, -1, // P to W
-1, 5, -1, -1, -1, -1, -1, -1, // X to Z
-1,'A', 2, 12, -1, -1, -1, 8, // a to g
-1, -1, -1, -1, 7, -1,'N', -1, // f to o
9, -1,'R', -1, 10, -1, 11, -1, // p to w
-1, 5, -1, -1, -1, -1, -1, -1 // x to z
};
static const BYTE TensDigit[10] = { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90 };
/************************************************************
* Functions
************************************************************/
WORD
iis_2atoi(
PCHAR s
)
/*++
Converts a 2 character string to integer
Arguments:
s String to convert
Returns:
numeric equivalent, 0 on failure.
--*/
{
DWORD tens = s[0] - '0';
DWORD ones = s[1] - '0';
if ( (tens <= 9) && (ones <= 9) ) {
return((WORD)(TensDigit[tens] + ones));
}
return(0);
}
#if 1
WORD
make_month(
PCHAR s
)
{
UCHAR monthIndex;
UCHAR c;
LPCTSTR monthString;
//
// use the third character as the index
//
c = (s[2] - 0x40) & 0x3F;
monthIndex = MonthIndexTable[c];
if ( monthIndex < 13 ) {
goto verify;
}
//
// ok, we need to look at the second character
//
if ( monthIndex == 'N' ) {
//
// we got an N which we need to resolve further
//
//
// if s[1] is 'u' then Jun, if 'a' then Jan
//
if ( MonthIndexTable[(s[1]-0x40) & 0x3f] == 'A' ) {
monthIndex = 1;
} else {
monthIndex = 6;
}
} else if ( monthIndex == 'R' ) {
//
// if s[1] is 'a' then March, if 'p' then April
//
if ( MonthIndexTable[(s[1]-0x40) & 0x3f] == 'A' ) {
monthIndex = 3;
} else {
monthIndex = 4;
}
} else {
goto error_exit;
}
verify:
monthString = s_rgchMonths[monthIndex-1];
if ( (s[0] == monthString[0]) &&
(s[1] == monthString[1]) &&
(s[2] == monthString[2]) ) {
return(monthIndex);
} else if ( (toupper(s[0]) == monthString[0]) &&
(tolower(s[1]) == monthString[1]) &&
(tolower(s[2]) == monthString[2]) ) {
return monthIndex;
}
error_exit:
return(0);
} // make_month
#else
int
make_month(
TCHAR * s
)
{
int i;
for (i=0; i<12; i++)
if (!_strnicmp(s_rgchMonths[i], s, 3))
return i + 1;
return 0;
}
#endif
BOOL
SystemTimeToGMT(
IN const SYSTEMTIME & st,
OUT CHAR * pszBuff,
IN DWORD cbBuff
)
/*++
Converts the given system time to string representation
containing GMT Formatted String.
Arguments:
st System time that needs to be converted.
pstr pointer to string which will contain the GMT time on
successful return.
cbBuff size of pszBuff in bytes
Returns:
TRUE on success. FALSE on failure.
History:
MuraliK 3-Jan-1995
--*/
{
DBG_ASSERT( pszBuff != NULL);
if ( cbBuff < 40 ) {
SetLastError( ERROR_INSUFFICIENT_BUFFER );
return FALSE;
}
//
// Formats a string like: "Thu, 14 Jul 1994 15:26:05 GMT"
//
APPEND_PSZ( pszBuff, s_rgchDays[st.wDayOfWeek] ); // 0-based
*pszBuff++ = ',';
*pszBuff++ = ' ';
AppendTwoDigits( pszBuff, st.wDay );
*pszBuff++ = ' ';
APPEND_PSZ( pszBuff, s_rgchMonths[st.wMonth - 1] ); // 1-based
*pszBuff++ = ' ';
AppendYear( pszBuff, st.wYear );
*pszBuff++ = ' ';
AppendTwoDigits( pszBuff, st.wHour );
*pszBuff++ = ':';
AppendTwoDigits( pszBuff, st.wMinute );
*pszBuff++ = ':';
AppendTwoDigits( pszBuff, st.wSecond );
*pszBuff++ = ' ';
*pszBuff++ = 'G';
*pszBuff++ = 'M';
*pszBuff++ = 'T';
*pszBuff = '\0';
return ( TRUE);
} // SystemTimeToGMT()
BOOL
NtLargeIntegerTimeToLocalSystemTime(
IN const LARGE_INTEGER * pliTime,
OUT SYSTEMTIME * pst)
/*++
Converts the time returned by NTIO apis ( which is a LARGE_INTEGER) into
Win32 SystemTime in Local Time zone.
Arguments:
pliTime pointer to large integer containing the time in NT format.
pst pointer to SYSTEMTIME structure which contains the time
fields on successful conversion.
Returns:
TRUE on success and FALSE on failure.
History:
MuraliK 27-Apr-1995
Limitations:
This is an NT specific function !! Reason is: Win32 uses FILETIME
structure for times. However LARGE_INTEGER and FILETIME both use
similar structure with one difference that is one has a LONG while
other has a ULONG.
--*/
{
FILETIME ftLocal;
if ( pliTime == NULL || pst == NULL) {
SetLastError( ERROR_INVALID_PARAMETER);
return ( FALSE);
}
//
// Convert the given large integer to local file time and
// then convert that to SYSTEMTIME.
// structure, containing the time details.
// I dont like this cast ( assumes too much about time structures)
// but again suitable methods are not available.
//
return (FileTimeToLocalFileTime((FILETIME *) pliTime,
&ftLocal) &&
FileTimeToSystemTime(&ftLocal, pst)
);
} // NtLargeIntegerTimeToLocalSystemTime()
BOOL
SystemTimeToGMTEx(
IN const SYSTEMTIME & st,
OUT CHAR * pszBuff,
IN DWORD cbBuff,
IN DWORD csecOffset
)
/*++
Converts the given system time to string representation
containing GMT Formatted String.
Arguments:
st System time that needs to be converted.
pstr pointer to string which will contain the GMT time on
successful return.
cbBuff size of pszBuff in bytes
csecOffset The number of seconds to offset the specified system time
Returns:
TRUE on success. FALSE on failure.
History:
MuraliK 3-Jan-1995
--*/
{
SYSTEMTIME sttmp;
DWORD dwSeconds = 0;
ULARGE_INTEGER liTime;
FILETIME ft;
DBG_ASSERT( pszBuff != NULL);
//
// If an offset is specified, calculate that now
//
if (!SystemTimeToFileTime( &st, &ft )) {
return(FALSE);
}
liTime.HighPart = ft.dwHighDateTime;
liTime.LowPart = ft.dwLowDateTime;
//
// Nt Large integer times are stored in 100ns increments, so convert the
// second offset to 100ns increments then add it
//
liTime.QuadPart += ((ULONGLONG) csecOffset) * (ULONGLONG) FILETIME_1_SECOND;
ft.dwHighDateTime = liTime.HighPart;
ft.dwLowDateTime = liTime.LowPart;
FileTimeToSystemTime( &ft, &sttmp );
return SystemTimeToGMT( sttmp,
pszBuff,
cbBuff );
} // SystemTimeToGMTEx
BOOL
FileTimeToGMT(
IN const FILETIME & ft,
OUT CHAR * pszBuff,
IN DWORD cbBuff
)
/*++
Converts the given system time to string representation
containing GMT Formatted String.
Arguments:
ft File time that needs to be converted.
pstr pointer to string which will contain the GMT time on
successful return.
cbBuff size of pszBuff in bytes
Returns:
TRUE on success. FALSE on failure.
--*/
{
SYSTEMTIME st;
if (FileTimeToSystemTime(&ft, &st))
return SystemTimeToGMT(st, pszBuff, cbBuff);
else
return FALSE;
}
BOOL
FileTimeToGMTEx(
IN const FILETIME & ft,
OUT CHAR * pszBuff,
IN DWORD cbBuff,
IN DWORD csecOffset
)
/*++
Converts the given system time to string representation
containing GMT Formatted String.
Arguments:
ft File time that needs to be converted.
pstr pointer to string which will contain the GMT time on
successful return.
cbBuff size of pszBuff in bytes
csecOffset The number of seconds to offset the specified system time
Returns:
TRUE on success. FALSE on failure.
--*/
{
SYSTEMTIME sttmp;
DWORD dwSeconds = 0;
ULARGE_INTEGER liTime;
DBG_ASSERT( pszBuff != NULL);
liTime.HighPart = ft.dwHighDateTime;
liTime.LowPart = ft.dwLowDateTime;
//
// Nt Large integer times are stored in 100ns increments, so convert the
// second offset to 100ns increments then add it
//
liTime.QuadPart += ((ULONGLONG) csecOffset) * (ULONGLONG) FILETIME_1_SECOND;
FILETIME ft2 = ft;
ft2.dwHighDateTime = liTime.HighPart;
ft2.dwLowDateTime = liTime.LowPart;
FileTimeToSystemTime( &ft2, &sttmp );
return SystemTimeToGMT( sttmp,
pszBuff,
cbBuff );
}
BOOL
NtLargeIntegerTimeToSystemTime(
IN const LARGE_INTEGER & liTime,
OUT SYSTEMTIME * pst)
/*++
Converts the time returned by NTIO apis ( which is a LARGE_INTEGER) into
Win32 SystemTime in GMT
Arguments:
liTime large integer containing the time in NT format.
pst pointer to SYSTEMTIME structure which contains the time
fields on successful conversion.
Returns:
TRUE on success and FALSE on failure.
History:
MuraliK 3-Jan-1995
Limitations:
This is an NT specific function !! Reason is: Win32 uses FILETIME
structure for times. However LARGE_INTEGER and FILETIME both use
similar structure with one difference that is one has a LONG while
other has a ULONG. Will that make a difference ? God knows.
Or substitute whatever you want for God...
--*/
{
FILETIME ft;
if ( pst == NULL) {
SetLastError( ERROR_INVALID_PARAMETER);
return ( FALSE);
}
//
// convert li to filetime
//
ft.dwLowDateTime = liTime.LowPart;
ft.dwHighDateTime = liTime.HighPart;
//
// convert to system time
//
if (!FileTimeToSystemTime(&ft,pst)) {
return(FALSE);
}
return ( TRUE);
} // NtLargeIntegerTimeToSystemTime()
BOOL
NtSystemTimeToLargeInteger(
IN const SYSTEMTIME * pst,
OUT LARGE_INTEGER * pli
)
{
FILETIME ft;
//
// Convert to file time
//
if ( !SystemTimeToFileTime( pst, &ft ) ) {
return(FALSE);
}
//
// Convert file time to large integer
//
pli->LowPart = ft.dwLowDateTime;
pli->HighPart = ft.dwHighDateTime;
return(TRUE);
}
BOOL
StringTimeToFileTime(
IN const TCHAR * pszTime,
OUT LARGE_INTEGER * pliTime
)
/*++
Converts a string representation of a GMT time (three different
varieties) to an NT representation of a file time.
We handle the following variations:
Sun, 06 Nov 1994 08:49:37 GMT (RFC 822 updated by RFC 1123)
Sunday, 06-Nov-94 08:49:37 GMT (RFC 850)
Sun Nov 6 08:49:37 1994 (ANSI C's asctime() format
Arguments:
pszTime String representation of time field
pliTime large integer containing the time in NT format.
Returns:
TRUE on success and FALSE on failure.
History:
Johnl 24-Jan-1995 Modified from WWW library
--*/
{
TCHAR * s;
SYSTEMTIME st;
if (pszTime == NULL) {
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
st.wMilliseconds = 0;
if ((s = strchr(pszTime, ','))) {
DWORD len;
//
// Thursday, 10-Jun-93 01:29:59 GMT
// or: Thu, 10 Jan 1993 01:29:59 GMT */
//
s++;
while (*s && *s==' ') s++;
len = strlen(s);
if (len < 18) {
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
if ( *(s+2) == '-' ) { /* First format */
st.wDay = (WORD) atoi(s);
st.wMonth = (WORD) make_month(s+3);
st.wYear = (WORD) atoi(s+7);
st.wHour = (WORD) atoi(s+10);
st.wMinute = (WORD) atoi(s+13);
st.wSecond = (WORD) atoi(s+16);
} else { /* Second format */
if (len < 20) {
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
st.wDay = iis_2atoi(s);
st.wMonth = make_month(s+3);
st.wYear = iis_2atoi(s+7) * 100 + iis_2atoi(s+9);
st.wHour = iis_2atoi(s+12);
st.wMinute = iis_2atoi(s+15);
st.wSecond = iis_2atoi(s+18);
}
} else { /* Try the other format: Wed Jun 9 01:29:59 1993 GMT */
s = (TCHAR *) pszTime;
while (*s && *s==' ') s++;
if ((int)strlen(s) < 24) {
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
st.wDay = (WORD) atoi(s+8);
st.wMonth = (WORD) make_month(s+4);
st.wYear = (WORD) atoi(s+20);
st.wHour = (WORD) atoi(s+11);
st.wMinute = (WORD) atoi(s+14);
st.wSecond = (WORD) atoi(s+17);
}
//
// Adjust for dates with only two digits
//
if ( st.wYear < 1000 ) {
if ( st.wYear < 50 ) {
st.wYear += 2000;
} else {
st.wYear += 1900;
}
}
if ( !NtSystemTimeToLargeInteger( &st,pliTime )) {
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
return(TRUE);
}
/************************************************************
* Cached Date Time Formats
*
* Formatting Date and Time for
* HTTP headers & Logging Requests
* is a costly operation.
* Using default NT Formatting operations with wsprintf()
* consumes about 6000 instructions/transaction
*
* Following code addresses this issue by
* 1) Caching formatted date/time pair for all purposes
* 2) Caching is done at the granularity of seconds/minute
* If there is a match till seconds, we return entire
* formatted information.
* If there is a match till the minutes, then the seconds
* portion is over-written using a seconds-lookup-table.
*
* Murali R. Krishnan (MuraliK) 23-Feb-1996
************************************************************/
//
// The delimiter string is : <logDelimiterChar><blank>
// The delimiter char should be same as the one used for LOG_RECORD
// in the file: ilogcls.cxx
//
const TCHAR G_PSZ_LOG_DELIMITER[3] = TEXT(", ");
#ifdef ENABLE_AUX_COUNTERS
# define CdtCountAccesses() InterlockedIncrement( &m_nAccesses)
# define CdtCountMisses() InterlockedIncrement( &m_nMisses)
# else // ENABLE_AUX_COUNTERS
# define CdtCountAccesses() /* do nothing */
# define CdtCountMisses() /* do nothing */
# endif // ENABLE_AUX_COUNTERS
VOID
DATETIME_FORMAT_ENTRY::CopyFormattedData(
IN const SYSTEMTIME * pst,
OUT CHAR * pchDateTime) const
{
//
// copy the formatted date/time information
//
CopyMemory(pchDateTime,
m_rgchDateTime,
m_cbDateTime
);
if ( m_stDateTime.wSecond != pst->wSecond) {
//
// seconds do not match. update seconds portion alone
//
LPSTR pch = pchDateTime + m_cchOffsetSeconds;
*pch = g_rgchTwoDigits[pst->wSecond][0];
*(pch + 1) = g_rgchTwoDigits[pst->wSecond][1];
}
return;
}
BOOL
CDFTCache::CopyFormattedData(
IN const SYSTEMTIME * pst,
OUT CHAR * pchDateTime) const
{
// See <readmost.hxx> for an explanation of this routine
const LONG nSequence = _ReadSequence();
// Is the data being updated on another thread?
if (nSequence != UPDATING)
{
// The weird const_cast syntax is necessitated by the volatile
// attribute on m_tData (DATETIME_FORMAT_ENTRY).
LPCSTR pchDate = FormattedBuffer();
DWORD cbDateTime = DateTimeChars();
// Copy the string
CopyMemory(pchDateTime, pchDate, cbDateTime);
if (Seconds() != pst->wSecond) {
//
// seconds do not match. update seconds portion alone
//
LPSTR pch = pchDateTime + OffsetSeconds();
*pch = g_rgchTwoDigits[pst->wSecond][0];
*(pch + 1) = g_rgchTwoDigits[pst->wSecond][1];
}
// If the sequence number is unchanged, the read was valid.
const LONG nSequence2 = _ReadSequence();
return (nSequence == nSequence2);
}
return FALSE;
}
VOID
ASCLOG_DATETIME_CACHE::GenerateDateTimeString(
IN PDFT_ENTRY pdft,
IN const SYSTEMTIME * pst
)
/*++
Description:
This function generates the datetime formats for all predefined
sequences. It attempts to generate the formatted date/time
to the accuracy of a minute. If need be the seconds portion
is obtained by indexing an array.
This function should be called for a locked pdft entry,
and the caller should make sure that the structures can be accessed
freely for update
Arguments:
pst - pointer to system time for which the datetime format is required
Returns:
None
--*/
{
CHAR rgchTime[25];
CHAR * pchDateTime;
DWORD cchLen;
DBG_ASSERT( pdft != NULL && pst != NULL );
//
// Format date for Logging (dftLog)
// Format is:
// <Date><DelimiterString><Time><DelimiterString>
//
// We need to generate the date format again, only if it changes
//
pchDateTime = pdft->m_rgchDateTime;
if ( !SameDate( &pdft->m_stDateTime, pst) ) {
::GetDateFormat(LOCALE_SYSTEM_DEFAULT,
LOCALE_NOUSEROVERRIDE,
pst, NULL,
pchDateTime,
15);
lstrcat( pchDateTime, G_PSZ_LOG_DELIMITER);
//
// cache the date length for future use.
//
pdft->m_cchDateLen = lstrlen( pchDateTime);
}
cchLen = pdft->m_cchDateLen;
//
// format the time portion
//
::GetTimeFormat( LOCALE_SYSTEM_DEFAULT,
(LOCALE_NOUSEROVERRIDE |
TIME_FORCE24HOURFORMAT|
TIME_NOTIMEMARKER),
pst, NULL,
rgchTime, 15);
DBG_ASSERT(lstrlen(rgchTime) + lstrlen( G_PSZ_LOG_DELIMITER) <
sizeof(rgchTime));
lstrcat( rgchTime, G_PSZ_LOG_DELIMITER);
//
// append time to date generated
//
DBG_ASSERT( cchLen > 0); // range is fine
lstrcpy(pchDateTime + cchLen, rgchTime);
DBG_ASSERT( lstrlen( pchDateTime) < sizeof( pdft->m_rgchDateTime));
//
// Calculate the offset for seconds based on time format.
// the time is usually formatted as hh:mm:ss if wHour >= 10 Offset =6
// and is formatted as h:mm:ss if wHour < 10 Offset =5
//
pdft->m_cchOffsetSeconds = ( cchLen + 5 + ((pst->wHour < 10) ? 0 : 1));
//
// !!! for the german locale, it's always hh:mm:ss
//
if ( pdft->m_rgchDateTime[pdft->m_cchOffsetSeconds] == ':' ) {
pdft->m_cchOffsetSeconds++;
}
pdft->m_cbDateTime = lstrlen( pdft->m_rgchDateTime) + 1;
DBG_ASSERT(pdft->m_cbDateTime <= MAX_FORMATTED_DATETIME_LEN);
//
// store the valid time now
//
pdft->m_stDateTime = *pst;
return;
} // ASCLOG_DATETIME_CACHE::GenerateDateTimeString
VOID
EXTLOG_DATETIME_CACHE::GenerateDateTimeString(
IN PDFT_ENTRY pdft,
IN const SYSTEMTIME * pst
)
/*++
Description:
Used for W3C Extended Logging format.
This function generates the datetime formats for all predefined
sequences. It attempts to generate the formatted date/time
to the accuracy of a minute. If need be the seconds portion
is obtained by indexing an array.
This function should be called for a locked pdft entry,
and the caller should make sure that the structures can be accessed
freely for update
Arguments:
pst - pointer to system time for which the datetime format is required
Returns:
None
--*/
{
PCHAR pchDateTime;
DWORD cchLen;
DBG_ASSERT( pdft != NULL && pst != NULL );
//
// Format is:
// Date YYYY-MM-DD
// Time HH:MM:SS
//
pchDateTime = pdft->m_rgchDateTime;
if ( !SameDate( &pdft->m_stDateTime, pst) ) {
AppendYear( pchDateTime, pst->wYear );
*pchDateTime++ = '-';
AppendTwoDigits( pchDateTime, pst->wMonth );
*pchDateTime++ = '-';
AppendTwoDigits( pchDateTime, pst->wDay );
//
// cache the date length for future use.
//
pchDateTime++;
pdft->m_cchDateLen = lstrlen( pdft->m_rgchDateTime );
DBG_ASSERT( pdft->m_cchDateLen == 10 );
} else {
DBG_ASSERT( pdft->m_cchDateLen == 10 );
pchDateTime += (pdft->m_cchDateLen+1);
}
cchLen = pdft->m_cchDateLen;
//
// format the time portion
//
AppendTwoDigits( pchDateTime, pst->wHour );
*pchDateTime++ = ':';
AppendTwoDigits( pchDateTime, pst->wMinute );
*pchDateTime++ = ':';
AppendTwoDigits( pchDateTime, pst->wSecond );
pchDateTime++;
//
// Calculate the offset for seconds based on time format.
// YYYY-MM-DD HH:MM:SS
//
pdft->m_cchOffsetSeconds = cchLen + 7;
pdft->m_cbDateTime = DIFF(pchDateTime - (PCHAR)pdft->m_rgchDateTime);
DBG_ASSERT(pdft->m_cbDateTime <= MAX_FORMATTED_DATETIME_LEN);
//
// store the valid time now
//
pdft->m_stDateTime = *pst;
return;
} // EXTLOG_DATETIME_CACHE::GenerateDateTimeString
VOID
W3_DATETIME_CACHE::GenerateDateTimeString(
IN PDFT_ENTRY pdft,
IN const SYSTEMTIME * pst
)
/*++
Description:
This function generates the datetime formats for all predefined
sequences. It attempts to generate the formatted date/time
to the accuracy of a minute. If need be the seconds portion
is obtained by indexing an array.
This function should be called for a locked pdft entry,
and the caller should make sure that the structures can be accessed
freely for update
Arguments:
pst - pointer to system time for which the datetime format is required
Returns:
None
--*/
{
CHAR rgchTime[25];
PCHAR pchDateTime;
DWORD cchLen;
DBG_ASSERT( pdft != NULL && pst != NULL );
//
// Format date for Logging (dftGmt)
// Format is:
// Date: <date-time> GMT\r\n
//
pchDateTime = pdft->m_rgchDateTime;
static const char szDate[] = "Date: ";
static const char szCRLF[] = "\r\n";
CopyMemory( pchDateTime, szDate, sizeof(szDate) - 1 );
pchDateTime += sizeof(szDate) - 1;
if ( !::SystemTimeToGMT( *pst,
pchDateTime,
sizeof(pdft->m_rgchDateTime)
- sizeof( szDate) - sizeof(szCRLF) + 1 ) )
{
pdft->m_rgchDateTime[0] = '\0';
} else {
pchDateTime += lstrlen( pchDateTime );
pdft->m_cchOffsetSeconds =
DIFF(pchDateTime - pdft->m_rgchDateTime)
- 2 // minus 2 digits for seconds
- 4; // minus " GMT"
pdft->m_cchDateLen = pdft->m_cchOffsetSeconds
- 7; // minus " hh:mm:"
CopyMemory( pchDateTime, szCRLF, sizeof(szCRLF) );
}
pdft->m_cbDateTime = ( lstrlen( pdft->m_rgchDateTime ) + 1);
DBG_ASSERT(pdft->m_cbDateTime <= MAX_FORMATTED_DATETIME_LEN);
//
// store the valid time now
//
pdft->m_stDateTime = *pst;
return;
} // W3_DATETIME_CACHE::GenerateDateTimeString
CACHED_DATETIME_FORMATS::CACHED_DATETIME_FORMATS( VOID )
:
#if ENABLE_AUX_COUNTERS
m_nMisses ( 0),
m_nAccesses ( 0),
#endif // ENABLE_AUX_COUNTERS
m_idftCurrent ( 0)
{
DATETIME_FORMAT_ENTRY dft;
ZeroMemory( &dft, sizeof(dft));
for (int i = 0; i < CACHE_SIZE; i++)
{
m_rgDateTimes[i].Write(dft);
}
}
DWORD
CACHED_DATETIME_FORMATS::GetFormattedDateTime(
IN const SYSTEMTIME * pst,
OUT TCHAR * pchDateTime
)
/*++
This function obtains formatted string for date specified in *pst.
It uses a cache to do lookup for the formatted date and time.
If all entries fail, then it calls the Formatting functions to
generate a new format.
It has been experimentally determined that the cost of formatting is too
high and hence we resort to caching and this comprehensive lookup function.
Also this function is NOT a GENERAL PURPOSE DATE-FORMAT cacher.
We cache with the ASSUMPTION that the date format requests will be for
consecutive time intervals.
Arguments:
pst - pointer to SYSTEMTIME
pchDateTime - pointer to character buffer into which the formatted
date will be copied.
Returns:
Length of string (excluding the NULL terminator)
--*/
{
DBG_ASSERT( pst != NULL && pchDateTime != NULL);
CdtCountAccesses();
CDFTCache* pdft;
LONG i = m_idftCurrent + CACHE_SIZE; // modulo operation in loop
// => start at m_idftCurrent
// m_rgDateTimes is a circular buffer of CDFTCaches. The current entry
// is pointed to by m_idftCurrent. The second-most recent entry is at
// (m_idftCurrent - 1) % CACHE_SIZE. Etc.
for (int j = CACHE_SIZE ; --j >= 0; i--)
{
pdft = &m_rgDateTimes[i & CACHE_MASK];
if (pdft->IsHit(pst)
&& pdft->CopyFormattedData(pst, pchDateTime))
{
return pdft->DateTimeChars() - 1;
}
}
// Not found in cache? Then generate the time string and add it
DATETIME_FORMAT_ENTRY dft;
dft.m_stDateTime.wYear = 0; // invalid date
GenerateDateTimeString(&dft, pst);
i = InterlockedIncrement(const_cast<LONG*>(&m_idftCurrent));
pdft = &m_rgDateTimes[i & CACHE_MASK];
pdft->Write(dft);
CdtCountMisses();
//
// The date time format is valid. Copy formatted date time. It is
// assumed that the buffer has sufficient space for the formatted date
//
dft.CopyFormattedData(pst, pchDateTime);
return dft.m_cbDateTime - 1;
} // CACHED_DATETIME_FORMATS::GetFormattedDateTime()
DWORD
CACHED_DATETIME_FORMATS::GetFormattedCurrentDateTime(
OUT PCHAR pchDateTime
)
{
DBG_ASSERT(pchDateTime != NULL);
SYSTEMTIME st;
IISGetCurrentTimeAsSystemTime(&st);
return GetFormattedDateTime(&st, pchDateTime);
}