Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

990 lines
23 KiB

/*++
Copyright (c) 1994 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
Project:
Internet Services Common DLL
Functions Exported:
SystemTimeToGMT()
NtLargeIntegerTimeToSystemTime()
Revision History:
MuraliK 23-Feb-1996 Added IslFormatDate()
--*/
/************************************************************
* Include Headers
************************************************************/
# include <tcpdllp.hxx>
# include "mainsupp.hxx"
/************************************************************
* Data
************************************************************/
static TCHAR * s_rgchDays[] = {
TEXT("Sun"),
TEXT("Mon"),
TEXT("Tue"),
TEXT("Wed"),
TEXT("Thu"),
TEXT("Fri"),
TEXT("Sat") };
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") };
/************************************************************
* Functions
************************************************************/
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;
}
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"
//
::wsprintf( pszBuff,
TEXT( "%s, %02d %s %04d %02d:%02d:%02d GMT"),
s_rgchDays[st.wDayOfWeek],
st.wDay,
s_rgchMonths[st.wMonth - 1],
st.wYear,
st.wHour,
st.wMinute,
st.wSecond);
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) 10000000;
ft.dwHighDateTime = liTime.HighPart;
ft.dwLowDateTime = liTime.LowPart;
FileTimeToSystemTime( &ft, &sttmp );
return SystemTimeToGMT( sttmp,
pszBuff,
cbBuff );
} // SystemTimeToGMTEx
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) {
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
st.wMilliseconds = 0;
if ((s = strchr(pszTime, ','))) { /* Thursday, 10-Jun-93 01:29:59 GMT */
s++; /* or: Thu, 10 Jan 1993 01:29:59 GMT */
while (*s && *s==' ') s++;
if (strchr(s,'-')) { /* First format */
if ((int)strlen(s) < 18) {
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
st.wDay = atoi(s);
st.wMonth = make_month(s+3);
st.wYear = atoi(s+7);
st.wHour = atoi(s+10);
st.wMinute = atoi(s+13);
st.wSecond = atoi(s+16);
} else { /* Second format */
if ((int)strlen(s) < 20) {
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
st.wDay = atoi(s);
st.wMonth = make_month(s+3);
st.wYear = atoi(s+7);
st.wHour = atoi(s+12);
st.wMinute = atoi(s+15);
st.wSecond = atoi(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 = atoi(s+8);
st.wMonth = make_month(s+4);
st.wYear = atoi(s+22);
st.wHour = atoi(s+11);
st.wMinute = atoi(s+14);
st.wSecond = 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
************************************************************/
# define ENABLE_AUX_COUNTERS ( 1)
//
// Seconds lookup table
//
static TCHAR g_rgchSeconds[60][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'
}; // g_rgchSeconds
//
// 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(", ");
# define MAX_NUM_CACHED_DATETIME_FORMATS (10) // maintain 10 minute history
# define MAX_FORMATTED_DATETIME_LEN (50)
struct DATETIME_FORMAT_ENTRY {
SYSTEMTIME stDateTime;
int cchOffsetSeconds[dftMax];
DWORD cbDateTime[dftMax];
int _cchDateLen[dftMax];
TCHAR rgchDateTime[dftMax][ MAX_FORMATTED_DATETIME_LEN];
VOID CopyFormattedData(IN const SYSTEMTIME * pst,
IN DATETIME_FORMAT_TYPE dft,
OUT TCHAR * pchDateTime)
{
// copy the formatted date/time information
memcpy(pchDateTime,
rgchDateTime[dft],
cbDateTime[dft]
);
if ( stDateTime.wSecond != pst->wSecond) {
// seconds do not match. update seconds portion alone
LPTSTR pch = pchDateTime + cchOffsetSeconds[dft];
DBG_ASSERT( pst->wSecond < 60);
*pch = g_rgchSeconds[pst->wSecond][0];
*(pch + 1) = g_rgchSeconds[pst->wSecond][1];
}
return;
}
BOOL IsHit( IN const SYSTEMTIME * pst)
{
// Ignore seconds & milli-seconds during comparison
return ( memcmp( &stDateTime, pst,
(sizeof(SYSTEMTIME) - 2* sizeof(WORD)))
== 0);
}
VOID
GenerateDateTimeFormats(IN const SYSTEMTIME * pst);
}; // struct DATETIME_FORMAT_ENTRY
typedef DATETIME_FORMAT_ENTRY * PDFT_ENTRY;
class CACHED_DATETIME_FORMATS {
public:
CACHED_DATETIME_FORMATS(VOID)
:
# if ENABLE_AUX_COUNTERS
m_nMisses ( 0),
m_nAccesses ( 0),
# endif // ENABLE_AUX_COUNTERS
m_pdftCurrent ( m_rgDateTimes)
{
InitializeCriticalSection( &m_csLock);
memset( m_rgDateTimes, 0, sizeof(m_rgDateTimes));
}
~CACHED_DATETIME_FORMATS(VOID)
{ DeleteCriticalSection( &m_csLock); }
VOID Lock(VOID)
{ EnterCriticalSection( &m_csLock); }
VOID Unlock(VOID)
{ LeaveCriticalSection( &m_csLock); }
DWORD
GetFormattedDateTime(IN const SYSTEMTIME * pst,
IN DATETIME_FORMAT_TYPE dft,
OUT TCHAR * pchDateTime);
private:
// m_pdftCurrent points into m_rgDateTimes array
DATETIME_FORMAT_ENTRY * m_pdftCurrent;
# if ENABLE_AUX_COUNTERS
public:
LONG m_nMisses;
LONG m_nAccesses;
private:
# endif // ENABLE_AUX_COUNTERS
DATETIME_FORMAT_ENTRY m_rgDateTimes[ MAX_NUM_CACHED_DATETIME_FORMATS];
CRITICAL_SECTION m_csLock;
}; // class CACHED_DATETIME_FORMATS
CACHED_DATETIME_FORMATS * g_pCachedDft;
#ifdef ENABLE_AUX_COUNTERS
# define CdtCountAccesses() InterlockedIncrement( &g_pCachedDft->m_nAccesses)
# define CdtCountMisses() InterlockedIncrement( &g_pCachedDft->m_nMisses)
# else // ENABLE_AUX_COUNTERS
# define CdtCountAccesses() /* do nothing */
# define CdtCountMisses() /* do nothing */
# endif // ENABLE_AUX_COUNTERS
VOID
DATETIME_FORMAT_ENTRY::GenerateDateTimeFormats(
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
--*/
{
TCHAR rgchTime[25];
TCHAR * pchDateTime;
DWORD cchLen;
//
// Format date for Logging (dftLog)
// Format is:
// <Date><DelimiterString><Time><DelimiterString>
//
// We need to generate the date format again, only if it changes
//
pchDateTime = rgchDateTime[dftLog];
if ( memcmp( &stDateTime, pst, 4 * sizeof(WORD)) != 0) {
DBG_REQUIRE( ::GetDateFormat(LOCALE_SYSTEM_DEFAULT,
LOCALE_NOUSEROVERRIDE,
pst, NULL,
pchDateTime,
15)
!= 0
);
lstrcat( pchDateTime, G_PSZ_LOG_DELIMITER);
// cache the date length for future use.
_cchDateLen[dftLog] = lstrlen( pchDateTime);
DBG_ASSERT( pchDateTime == rgchDateTime[dftLog]);
}
cchLen = _cchDateLen[dftLog];
// format the time portion
DBG_REQUIRE( ::GetTimeFormat( LOCALE_SYSTEM_DEFAULT,
(LOCALE_NOUSEROVERRIDE |
TIME_FORCE24HOURFORMAT|
TIME_NOTIMEMARKER),
pst, NULL,
rgchTime, 15)
!= 0);
DBG_ASSERT(lstrlen(rgchTime) + lstrlen( G_PSZ_LOG_DELIMITER) <
sizeof(rgchTime));
//
// Calculate offset in seconds by assuming that the seconds will
// always be the last 2 digits of the time string -- RonaldM
//
int cchSecondsOffset = lstrlen(rgchTime) - 2;
lstrcat( rgchTime, G_PSZ_LOG_DELIMITER);
// append time to date generated
DBG_ASSERT( cchLen > 0); // range is fine
lstrcpy(pchDateTime + cchLen, rgchTime);
/*
//
// 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
//
cchOffsetSeconds[dftLog] = ( cchLen + 5 + ((pst->wHour < 10) ? 0 : 1));
Note: Bogus assumption because of leading zero conditions, see note above
-- RonaldM
*/
cchOffsetSeconds[dftLog] = cchLen + cchSecondsOffset;
DBG_ASSERT( lstrlen( pchDateTime) < sizeof( rgchDateTime[dftLog]));
cbDateTime[dftLog] = (lstrlen( rgchDateTime[dftLog]) + 1) * sizeof(TCHAR);
//
// Format date for Logging (dftGmt)
// Format is:
// Date: <date-time> GMT\r\n
//
pchDateTime = rgchDateTime[dftGmt];
memcpy( pchDateTime, TEXT("Date: "), sizeof(TEXT("Date: ")) - sizeof(TCHAR) );
pchDateTime += sizeof(TEXT("Date: ")) - sizeof(TCHAR);
if ( !::SystemTimeToGMT( *pst,
pchDateTime,
sizeof(rgchDateTime[dftGmt])
- sizeof( TEXT("Date: " "\r\n")) ) )
{
rgchDateTime[dftGmt][0] = '\0';
}
else
{
pchDateTime += lstrlen( pchDateTime );
cchOffsetSeconds[dftGmt] = pchDateTime
- rgchDateTime[dftGmt]
- 2 // minus 2 digits for seconds
- 4; // minus " GMT"
memcpy( pchDateTime, TEXT("\r\n"), sizeof(TEXT("\r\n")) );
}
cbDateTime[dftGmt] =
( lstrlen( rgchDateTime[dftGmt] ) + 1) * sizeof(TCHAR);
DBG_ASSERT( dftMax == 2); // there are only 2 date formats to be done now
// store the valid time now
memcpy( &stDateTime, pst, sizeof(*pst));
return;
} // CACHED_DATETIME_FORMATS::GenerateDateTimeFormats()
DWORD
CACHED_DATETIME_FORMATS::GetFormattedDateTime(IN const SYSTEMTIME * pst,
IN DATETIME_FORMAT_TYPE dft,
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
dft - enumerated value indicating the type of format request
pchDateTime - pointer to character buffer into which the formatted
date will be copied.
Returns:
TRUE on success. FALSE if there is any error.
--*/
{
PDFT_ENTRY pdft;
DWORD cbFmt;
DBG_ASSERT( pst != NULL && pchDateTime != NULL);
CdtCountAccesses();
//
// 1. Fast lookup to retrieve the formatted date for current item
//
// Since dates are stored in sequential order, we look in
// the linear fashion, scanning backward first and then scanning forward
// TBD: This code needs to be improved based on proximity of search.
//
Lock();
for ( pdft = m_pdftCurrent;
pdft >= m_rgDateTimes;
pdft--
) {
if ( pdft->IsHit(pst)) {
//
// The date time format is valid. Copy formatted date time.
// It is assumed that the buffer has sufficient space for
// the formatted date
//
pdft->CopyFormattedData(pst, dft, pchDateTime);
cbFmt = pdft->cbDateTime[dft];
Unlock();
return (cbFmt);
}
} // for ( backward scan)
// forward scan
for ( pdft = m_pdftCurrent + 1;
pdft < m_rgDateTimes + MAX_NUM_CACHED_DATETIME_FORMATS;
pdft++
) {
if ( pdft->IsHit(pst)) {
//
// The date time format is valid. Copy formatted date time.
// It is assumed that the buffer has sufficient space for
// the formatted date
//
pdft->CopyFormattedData(pst, dft, pchDateTime);
cbFmt = pdft->cbDateTime[dft];
Unlock();
return (cbFmt);
}
} // for ( forward scan)
//
// 3. Even an exhaustive search missed the date format.
// It is time to get into dirty part of thework
// Generate the date formats anew and cache them.
//
pdft = m_pdftCurrent;
// circular shift the current pointer
pdft = (((pdft - m_rgDateTimes) == (MAX_NUM_CACHED_DATETIME_FORMATS - 1)) ?
m_rgDateTimes : pdft + 1);
pdft->GenerateDateTimeFormats( pst);
// change the current pointer atomically to new one
m_pdftCurrent = pdft;
// copy off the formatted date into current buffer
pdft->CopyFormattedData(pst, dft, pchDateTime);
cbFmt = pdft->cbDateTime[dft];
Unlock();
CdtCountMisses();
return ( cbFmt );
} // CACHED_DATETIME_FORMATS::GetFormattedDateTime()
DWORD
IslInitDateTimesCache( VOID)
{
g_pCachedDft = new CACHED_DATETIME_FORMATS();
return (( g_pCachedDft == NULL) ? ERROR_NOT_ENOUGH_MEMORY: NULL);
} // IslInitDateTimesCache()
VOID
IslCleanupDateTimesCache( VOID)
{
delete g_pCachedDft;
g_pCachedDft = NULL;
} // IslCleanupDateTimesCache()
DWORD
IslFormatDateTime(IN const SYSTEMTIME * pst,
IN DATETIME_FORMAT_TYPE dft,
OUT TCHAR * pchDateTime
)
/*++
IslFormatDateTime()
This function formats the date/time given into a string.
It should be used only for cacheable dates/times (with temporal locality).
The function maintains cached entries and attempts to pull the formatted
entries from cache to avoid penalty of regenerating formatted data.
This is not a General purpose function.
It is a function for specific purpose.
--*/
{
//
// 1. Obtain the formatted date from global cache
// return the strlen() of the data
//
return g_pCachedDft->GetFormattedDateTime(pst, dft, pchDateTime)
- 1;
} // IslFormatDateTime()
/************************ End of File ***********************/