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

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name :
  4. datetime.cxx
  5. Abstract:
  6. This module exports common functions for date and time fields,
  7. Expanding into strings and manipulation.
  8. Author:
  9. Murali R. Krishnan ( MuraliK ) 3-Jan-1995
  10. --*/
  11. #include "precomp.hxx"
  12. #include <stdlib.h>
  13. #include <pudebug.h>
  14. # if !defined(dllexp)
  15. # define dllexp __declspec( dllexport)
  16. # endif
  17. #define DLL_IMPLEMENTATION
  18. #define IMPLEMENTATION_EXPORT
  19. #include <datetime.hxx>
  20. #include <inetsvcs.h>
  21. #include "date.hxx"
  22. class dllexp CDateTime
  23. {
  24. public:
  25. FILETIME_UINT64 m_ftu;
  26. SYSTEMTIME m_st;
  27. CDateTime()
  28. { /* do nothing */ }
  29. CDateTime(const SYSTEMTIME& rst)
  30. { SetTime(rst); }
  31. CDateTime(const FILETIME& rft)
  32. { SetTime(rft); }
  33. CDateTime(const FILETIME& rft, const SYSTEMTIME& rst)
  34. { m_ftu.ft = rft; m_st = rst; }
  35. BOOL
  36. GetCurrentTime()
  37. {
  38. GetSystemTimeAsFileTime(&m_ftu.ft);
  39. return FileTimeToSystemTime(&m_ftu.ft, &m_st);
  40. }
  41. BOOL
  42. SetTime(const SYSTEMTIME& rst)
  43. { m_st = rst; return SystemTimeToFileTime(&m_st, &m_ftu.ft); }
  44. BOOL
  45. SetTime(const FILETIME& rft)
  46. { m_ftu.ft = rft; return FileTimeToSystemTime(&m_ftu.ft, &m_st); }
  47. };
  48. static const CHAR g_rgchTwoDigits[100][2] =
  49. {
  50. { '0', '0' }, { '0', '1' }, { '0', '2' }, { '0', '3' }, { '0', '4' },
  51. { '0', '5' }, { '0', '6' }, { '0', '7' }, { '0', '8' }, { '0', '9' },
  52. { '1', '0' }, { '1', '1' }, { '1', '2' }, { '1', '3' }, { '1', '4' },
  53. { '1', '5' }, { '1', '6' }, { '1', '7' }, { '1', '8' }, { '1', '9' },
  54. { '2', '0' }, { '2', '1' }, { '2', '2' }, { '2', '3' }, { '2', '4' },
  55. { '2', '5' }, { '2', '6' }, { '2', '7' }, { '2', '8' }, { '2', '9' },
  56. { '3', '0' }, { '3', '1' }, { '3', '2' }, { '3', '3' }, { '3', '4' },
  57. { '3', '5' }, { '3', '6' }, { '3', '7' }, { '3', '8' }, { '3', '9' },
  58. { '4', '0' }, { '4', '1' }, { '4', '2' }, { '4', '3' }, { '4', '4' },
  59. { '4', '5' }, { '4', '6' }, { '4', '7' }, { '4', '8' }, { '4', '9' },
  60. { '5', '0' }, { '5', '1' }, { '5', '2' }, { '5', '3' }, { '5', '4' },
  61. { '5', '5' }, { '5', '6' }, { '5', '7' }, { '5', '8' }, { '5', '9' },
  62. { '6', '0' }, { '6', '1' }, { '6', '2' }, { '6', '3' }, { '6', '4' },
  63. { '6', '5' }, { '6', '6' }, { '6', '7' }, { '6', '8' }, { '6', '9' },
  64. { '7', '0' }, { '7', '1' }, { '7', '2' }, { '7', '3' }, { '7', '4' },
  65. { '7', '5' }, { '7', '6' }, { '7', '7' }, { '7', '8' }, { '7', '9' },
  66. { '8', '0' }, { '8', '1' }, { '8', '2' }, { '8', '3' }, { '8', '4' },
  67. { '8', '5' }, { '8', '6' }, { '8', '7' }, { '8', '8' }, { '8', '9' },
  68. { '9', '0' }, { '9', '1' }, { '9', '2' }, { '9', '3' }, { '9', '4' },
  69. { '9', '5' }, { '9', '6' }, { '9', '7' }, { '9', '8' }, { '9', '9' },
  70. };
  71. //
  72. // Constants
  73. //
  74. #define APPEND_STR(a,b) \
  75. {CopyMemory(a,b,sizeof(b)); a += sizeof(b)-sizeof(CHAR);}
  76. #define APPEND_PSZ( pszTail, psz ) \
  77. { DWORD cb = strlen( psz ); \
  78. CopyMemory( (pszTail), (psz), cb + 1 );\
  79. (pszTail) += cb; \
  80. }
  81. //
  82. // Makes a two-digit zero padded number (i.e., "23", or "05")
  83. //
  84. inline
  85. VOID
  86. AppendTwoDigits(
  87. CHAR*& rpszTail,
  88. DWORD Num
  89. )
  90. {
  91. if ( Num < 100 )
  92. {
  93. rpszTail[0] = g_rgchTwoDigits[Num][0];
  94. rpszTail[1] = g_rgchTwoDigits[Num][1];
  95. rpszTail[2] = '\0';
  96. rpszTail += 2;
  97. }
  98. else
  99. {
  100. DBG_ASSERT(!"Num >= 100");
  101. }
  102. }
  103. //
  104. // Years conversion
  105. //
  106. #define MAX_CACHED_YEARS 32
  107. static DWORD g_nMinYear = 0, g_nMaxYear = 0;
  108. static char g_aszYears[MAX_CACHED_YEARS][4+1];
  109. typedef CDataCache<CDateTime> CCacheTime;
  110. static CCacheTime g_ctCurrentTime;
  111. void
  112. InitializeDateTime()
  113. {
  114. SYSTEMTIME st;
  115. GetSystemTime(&st);
  116. g_nMinYear = st.wYear - MAX_CACHED_YEARS / 2;
  117. g_nMaxYear = g_nMinYear + MAX_CACHED_YEARS - 1;
  118. DBG_ASSERT(1000 <= g_nMinYear && g_nMaxYear <= 9999);
  119. for (DWORD i = g_nMinYear; i <= g_nMaxYear; i++)
  120. {
  121. _itoa( i, g_aszYears[i - g_nMinYear], 10 );
  122. }
  123. CDateTime dt(st);
  124. g_ctCurrentTime.Write(dt);
  125. }
  126. void
  127. TerminateDateTime()
  128. {
  129. // nothing to be done, at least for now
  130. }
  131. inline
  132. VOID
  133. AppendYear(
  134. CHAR* &rpszTail,
  135. DWORD dwYear
  136. )
  137. {
  138. DBG_ASSERT(g_nMinYear >= 1000);
  139. DWORD i = dwYear - g_nMinYear;
  140. if (i < MAX_CACHED_YEARS)
  141. {
  142. DBG_ASSERT(g_nMinYear <= dwYear && dwYear <= g_nMaxYear);
  143. const char* pszYear = g_aszYears[i];
  144. *rpszTail++ = *pszYear++;
  145. *rpszTail++ = *pszYear++;
  146. *rpszTail++ = *pszYear++;
  147. *rpszTail++ = *pszYear++;
  148. *rpszTail = '\0';
  149. }
  150. else
  151. {
  152. CHAR __ach[32];
  153. DBG_ASSERT( dwYear >= 1000 && dwYear <= 9999 );
  154. _itoa( dwYear, __ach, 10 );
  155. CopyMemory( rpszTail, __ach, 4+1 );
  156. rpszTail += 4;
  157. }
  158. }
  159. // Since ::GetSystemTime is relatively expensive (310 instructions) and
  160. // ::GetSystemTimeAsFileTime is pretty cheap (20 instructions), we cache
  161. // the SYSTEMTIME representation of the current time with an accuracy of
  162. // 1.0 seconds.
  163. BOOL
  164. IISGetCurrentTime(
  165. OUT FILETIME* pft,
  166. OUT SYSTEMTIME* pst)
  167. {
  168. BOOL fUpdatedCachedTime = FALSE;
  169. CDateTime dt;
  170. while (! g_ctCurrentTime.Read(dt))
  171. {
  172. // empty loop
  173. }
  174. FILETIME_UINT64 ftu;
  175. GetSystemTimeAsFileTime(&ftu.ft);
  176. if (ftu.u64 - dt.m_ftu.u64 >= FILETIME_1_SECOND)
  177. {
  178. #undef WT_INSTRUCTION_COUNTS
  179. #ifndef WT_INSTRUCTION_COUNTS
  180. fUpdatedCachedTime = TRUE;
  181. dt.SetTime(ftu.ft);
  182. g_ctCurrentTime.Write(dt);
  183. #endif
  184. }
  185. if (pft != NULL)
  186. *pft = dt.m_ftu.ft;
  187. if (pst != NULL)
  188. *pst = dt.m_st;
  189. return fUpdatedCachedTime;
  190. }
  191. /************************************************************
  192. * Data
  193. ************************************************************/
  194. static const TCHAR* s_rgchDays[] = {
  195. TEXT("Sun"), TEXT("Mon"), TEXT("Tue"), TEXT("Wed"),
  196. TEXT("Thu"), TEXT("Fri"), TEXT("Sat")
  197. };
  198. static const TCHAR* s_rgchMonths[] = {
  199. TEXT("Jan"), TEXT("Feb"), TEXT("Mar"), TEXT("Apr"),
  200. TEXT("May"), TEXT("Jun"), TEXT("Jul"), TEXT("Aug"),
  201. TEXT("Sep"), TEXT("Oct"), TEXT("Nov"), TEXT("Dec")
  202. };
  203. LPCTSTR
  204. DayOfWeek3CharNames(DWORD dwDayOfWeek)
  205. {
  206. return s_rgchDays[dwDayOfWeek];
  207. }
  208. LPCTSTR
  209. Month3CharNames(DWORD dwMonth)
  210. {
  211. return s_rgchMonths[dwMonth];
  212. }
  213. // Custom hash table for make_month() for mapping "Apr" to 4
  214. static const CHAR MonthIndexTable[64] = {
  215. -1,'A', 2, 12, -1, -1, -1, 8, // A to G
  216. -1, -1, -1, -1, 7, -1,'N', -1, // F to O
  217. 9, -1,'R', -1, 10, -1, 11, -1, // P to W
  218. -1, 5, -1, -1, -1, -1, -1, -1, // X to Z
  219. -1,'A', 2, 12, -1, -1, -1, 8, // a to g
  220. -1, -1, -1, -1, 7, -1,'N', -1, // f to o
  221. 9, -1,'R', -1, 10, -1, 11, -1, // p to w
  222. -1, 5, -1, -1, -1, -1, -1, -1 // x to z
  223. };
  224. static const BYTE TensDigit[10] = { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90 };
  225. /************************************************************
  226. * Functions
  227. ************************************************************/
  228. WORD
  229. iis_2atoi(
  230. PCHAR s
  231. )
  232. /*++
  233. Converts a 2 character string to integer
  234. Arguments:
  235. s String to convert
  236. Returns:
  237. numeric equivalent, 0 on failure.
  238. --*/
  239. {
  240. DWORD tens = s[0] - '0';
  241. DWORD ones = s[1] - '0';
  242. if ( (tens <= 9) && (ones <= 9) ) {
  243. return((WORD)(TensDigit[tens] + ones));
  244. }
  245. return(0);
  246. }
  247. #if 1
  248. WORD
  249. make_month(
  250. PCHAR s
  251. )
  252. {
  253. UCHAR monthIndex;
  254. UCHAR c;
  255. LPCTSTR monthString;
  256. //
  257. // use the third character as the index
  258. //
  259. c = (s[2] - 0x40) & 0x3F;
  260. monthIndex = MonthIndexTable[c];
  261. if ( monthIndex < 13 ) {
  262. goto verify;
  263. }
  264. //
  265. // ok, we need to look at the second character
  266. //
  267. if ( monthIndex == 'N' ) {
  268. //
  269. // we got an N which we need to resolve further
  270. //
  271. //
  272. // if s[1] is 'u' then Jun, if 'a' then Jan
  273. //
  274. if ( MonthIndexTable[(s[1]-0x40) & 0x3f] == 'A' ) {
  275. monthIndex = 1;
  276. } else {
  277. monthIndex = 6;
  278. }
  279. } else if ( monthIndex == 'R' ) {
  280. //
  281. // if s[1] is 'a' then March, if 'p' then April
  282. //
  283. if ( MonthIndexTable[(s[1]-0x40) & 0x3f] == 'A' ) {
  284. monthIndex = 3;
  285. } else {
  286. monthIndex = 4;
  287. }
  288. } else {
  289. goto error_exit;
  290. }
  291. verify:
  292. monthString = s_rgchMonths[monthIndex-1];
  293. if ( (s[0] == monthString[0]) &&
  294. (s[1] == monthString[1]) &&
  295. (s[2] == monthString[2]) ) {
  296. return(monthIndex);
  297. } else if ( (toupper(s[0]) == monthString[0]) &&
  298. (tolower(s[1]) == monthString[1]) &&
  299. (tolower(s[2]) == monthString[2]) ) {
  300. return monthIndex;
  301. }
  302. error_exit:
  303. return(0);
  304. } // make_month
  305. #else
  306. int
  307. make_month(
  308. TCHAR * s
  309. )
  310. {
  311. int i;
  312. for (i=0; i<12; i++)
  313. if (!_strnicmp(s_rgchMonths[i], s, 3))
  314. return i + 1;
  315. return 0;
  316. }
  317. #endif
  318. BOOL
  319. SystemTimeToGMT(
  320. IN const SYSTEMTIME & st,
  321. OUT CHAR * pszBuff,
  322. IN DWORD cbBuff
  323. )
  324. /*++
  325. Converts the given system time to string representation
  326. containing GMT Formatted String.
  327. Arguments:
  328. st System time that needs to be converted.
  329. pstr pointer to string which will contain the GMT time on
  330. successful return.
  331. cbBuff size of pszBuff in bytes
  332. Returns:
  333. TRUE on success. FALSE on failure.
  334. History:
  335. MuraliK 3-Jan-1995
  336. --*/
  337. {
  338. DBG_ASSERT( pszBuff != NULL);
  339. if ( cbBuff < 40 ) {
  340. SetLastError( ERROR_INSUFFICIENT_BUFFER );
  341. return FALSE;
  342. }
  343. //
  344. // Formats a string like: "Thu, 14 Jul 1994 15:26:05 GMT"
  345. //
  346. APPEND_PSZ( pszBuff, s_rgchDays[st.wDayOfWeek] ); // 0-based
  347. *pszBuff++ = ',';
  348. *pszBuff++ = ' ';
  349. AppendTwoDigits( pszBuff, st.wDay );
  350. *pszBuff++ = ' ';
  351. APPEND_PSZ( pszBuff, s_rgchMonths[st.wMonth - 1] ); // 1-based
  352. *pszBuff++ = ' ';
  353. AppendYear( pszBuff, st.wYear );
  354. *pszBuff++ = ' ';
  355. AppendTwoDigits( pszBuff, st.wHour );
  356. *pszBuff++ = ':';
  357. AppendTwoDigits( pszBuff, st.wMinute );
  358. *pszBuff++ = ':';
  359. AppendTwoDigits( pszBuff, st.wSecond );
  360. *pszBuff++ = ' ';
  361. *pszBuff++ = 'G';
  362. *pszBuff++ = 'M';
  363. *pszBuff++ = 'T';
  364. *pszBuff = '\0';
  365. return ( TRUE);
  366. } // SystemTimeToGMT()
  367. BOOL
  368. NtLargeIntegerTimeToLocalSystemTime(
  369. IN const LARGE_INTEGER * pliTime,
  370. OUT SYSTEMTIME * pst)
  371. /*++
  372. Converts the time returned by NTIO apis ( which is a LARGE_INTEGER) into
  373. Win32 SystemTime in Local Time zone.
  374. Arguments:
  375. pliTime pointer to large integer containing the time in NT format.
  376. pst pointer to SYSTEMTIME structure which contains the time
  377. fields on successful conversion.
  378. Returns:
  379. TRUE on success and FALSE on failure.
  380. History:
  381. MuraliK 27-Apr-1995
  382. Limitations:
  383. This is an NT specific function !! Reason is: Win32 uses FILETIME
  384. structure for times. However LARGE_INTEGER and FILETIME both use
  385. similar structure with one difference that is one has a LONG while
  386. other has a ULONG.
  387. --*/
  388. {
  389. FILETIME ftLocal;
  390. if ( pliTime == NULL || pst == NULL) {
  391. SetLastError( ERROR_INVALID_PARAMETER);
  392. return ( FALSE);
  393. }
  394. //
  395. // Convert the given large integer to local file time and
  396. // then convert that to SYSTEMTIME.
  397. // structure, containing the time details.
  398. // I dont like this cast ( assumes too much about time structures)
  399. // but again suitable methods are not available.
  400. //
  401. return (FileTimeToLocalFileTime((FILETIME *) pliTime,
  402. &ftLocal) &&
  403. FileTimeToSystemTime(&ftLocal, pst)
  404. );
  405. } // NtLargeIntegerTimeToLocalSystemTime()
  406. BOOL
  407. SystemTimeToGMTEx(
  408. IN const SYSTEMTIME & st,
  409. OUT CHAR * pszBuff,
  410. IN DWORD cbBuff,
  411. IN DWORD csecOffset
  412. )
  413. /*++
  414. Converts the given system time to string representation
  415. containing GMT Formatted String.
  416. Arguments:
  417. st System time that needs to be converted.
  418. pstr pointer to string which will contain the GMT time on
  419. successful return.
  420. cbBuff size of pszBuff in bytes
  421. csecOffset The number of seconds to offset the specified system time
  422. Returns:
  423. TRUE on success. FALSE on failure.
  424. History:
  425. MuraliK 3-Jan-1995
  426. --*/
  427. {
  428. SYSTEMTIME sttmp;
  429. DWORD dwSeconds = 0;
  430. ULARGE_INTEGER liTime;
  431. FILETIME ft;
  432. DBG_ASSERT( pszBuff != NULL);
  433. //
  434. // If an offset is specified, calculate that now
  435. //
  436. if (!SystemTimeToFileTime( &st, &ft )) {
  437. return(FALSE);
  438. }
  439. liTime.HighPart = ft.dwHighDateTime;
  440. liTime.LowPart = ft.dwLowDateTime;
  441. //
  442. // Nt Large integer times are stored in 100ns increments, so convert the
  443. // second offset to 100ns increments then add it
  444. //
  445. liTime.QuadPart += ((ULONGLONG) csecOffset) * (ULONGLONG) FILETIME_1_SECOND;
  446. ft.dwHighDateTime = liTime.HighPart;
  447. ft.dwLowDateTime = liTime.LowPart;
  448. FileTimeToSystemTime( &ft, &sttmp );
  449. return SystemTimeToGMT( sttmp,
  450. pszBuff,
  451. cbBuff );
  452. } // SystemTimeToGMTEx
  453. BOOL
  454. FileTimeToGMT(
  455. IN const FILETIME & ft,
  456. OUT CHAR * pszBuff,
  457. IN DWORD cbBuff
  458. )
  459. /*++
  460. Converts the given system time to string representation
  461. containing GMT Formatted String.
  462. Arguments:
  463. ft File time that needs to be converted.
  464. pstr pointer to string which will contain the GMT time on
  465. successful return.
  466. cbBuff size of pszBuff in bytes
  467. Returns:
  468. TRUE on success. FALSE on failure.
  469. --*/
  470. {
  471. SYSTEMTIME st;
  472. if (FileTimeToSystemTime(&ft, &st))
  473. return SystemTimeToGMT(st, pszBuff, cbBuff);
  474. else
  475. return FALSE;
  476. }
  477. BOOL
  478. FileTimeToGMTEx(
  479. IN const FILETIME & ft,
  480. OUT CHAR * pszBuff,
  481. IN DWORD cbBuff,
  482. IN DWORD csecOffset
  483. )
  484. /*++
  485. Converts the given system time to string representation
  486. containing GMT Formatted String.
  487. Arguments:
  488. ft File time that needs to be converted.
  489. pstr pointer to string which will contain the GMT time on
  490. successful return.
  491. cbBuff size of pszBuff in bytes
  492. csecOffset The number of seconds to offset the specified system time
  493. Returns:
  494. TRUE on success. FALSE on failure.
  495. --*/
  496. {
  497. SYSTEMTIME sttmp;
  498. DWORD dwSeconds = 0;
  499. ULARGE_INTEGER liTime;
  500. DBG_ASSERT( pszBuff != NULL);
  501. liTime.HighPart = ft.dwHighDateTime;
  502. liTime.LowPart = ft.dwLowDateTime;
  503. //
  504. // Nt Large integer times are stored in 100ns increments, so convert the
  505. // second offset to 100ns increments then add it
  506. //
  507. liTime.QuadPart += ((ULONGLONG) csecOffset) * (ULONGLONG) FILETIME_1_SECOND;
  508. FILETIME ft2 = ft;
  509. ft2.dwHighDateTime = liTime.HighPart;
  510. ft2.dwLowDateTime = liTime.LowPart;
  511. FileTimeToSystemTime( &ft2, &sttmp );
  512. return SystemTimeToGMT( sttmp,
  513. pszBuff,
  514. cbBuff );
  515. }
  516. BOOL
  517. NtLargeIntegerTimeToSystemTime(
  518. IN const LARGE_INTEGER & liTime,
  519. OUT SYSTEMTIME * pst)
  520. /*++
  521. Converts the time returned by NTIO apis ( which is a LARGE_INTEGER) into
  522. Win32 SystemTime in GMT
  523. Arguments:
  524. liTime large integer containing the time in NT format.
  525. pst pointer to SYSTEMTIME structure which contains the time
  526. fields on successful conversion.
  527. Returns:
  528. TRUE on success and FALSE on failure.
  529. History:
  530. MuraliK 3-Jan-1995
  531. Limitations:
  532. This is an NT specific function !! Reason is: Win32 uses FILETIME
  533. structure for times. However LARGE_INTEGER and FILETIME both use
  534. similar structure with one difference that is one has a LONG while
  535. other has a ULONG. Will that make a difference ? God knows.
  536. Or substitute whatever you want for God...
  537. --*/
  538. {
  539. FILETIME ft;
  540. if ( pst == NULL) {
  541. SetLastError( ERROR_INVALID_PARAMETER);
  542. return ( FALSE);
  543. }
  544. //
  545. // convert li to filetime
  546. //
  547. ft.dwLowDateTime = liTime.LowPart;
  548. ft.dwHighDateTime = liTime.HighPart;
  549. //
  550. // convert to system time
  551. //
  552. if (!FileTimeToSystemTime(&ft,pst)) {
  553. return(FALSE);
  554. }
  555. return ( TRUE);
  556. } // NtLargeIntegerTimeToSystemTime()
  557. BOOL
  558. NtSystemTimeToLargeInteger(
  559. IN const SYSTEMTIME * pst,
  560. OUT LARGE_INTEGER * pli
  561. )
  562. {
  563. FILETIME ft;
  564. //
  565. // Convert to file time
  566. //
  567. if ( !SystemTimeToFileTime( pst, &ft ) ) {
  568. return(FALSE);
  569. }
  570. //
  571. // Convert file time to large integer
  572. //
  573. pli->LowPart = ft.dwLowDateTime;
  574. pli->HighPart = ft.dwHighDateTime;
  575. return(TRUE);
  576. }
  577. BOOL
  578. StringTimeToFileTime(
  579. IN const TCHAR * pszTime,
  580. OUT LARGE_INTEGER * pliTime
  581. )
  582. /*++
  583. Converts a string representation of a GMT time (three different
  584. varieties) to an NT representation of a file time.
  585. We handle the following variations:
  586. Sun, 06 Nov 1994 08:49:37 GMT (RFC 822 updated by RFC 1123)
  587. Sunday, 06-Nov-94 08:49:37 GMT (RFC 850)
  588. Sun Nov 6 08:49:37 1994 (ANSI C's asctime() format
  589. Arguments:
  590. pszTime String representation of time field
  591. pliTime large integer containing the time in NT format.
  592. Returns:
  593. TRUE on success and FALSE on failure.
  594. History:
  595. Johnl 24-Jan-1995 Modified from WWW library
  596. --*/
  597. {
  598. TCHAR * s;
  599. SYSTEMTIME st;
  600. if (pszTime == NULL) {
  601. SetLastError( ERROR_INVALID_PARAMETER );
  602. return FALSE;
  603. }
  604. st.wMilliseconds = 0;
  605. if ((s = strchr(pszTime, ','))) {
  606. DWORD len;
  607. //
  608. // Thursday, 10-Jun-93 01:29:59 GMT
  609. // or: Thu, 10 Jan 1993 01:29:59 GMT */
  610. //
  611. s++;
  612. while (*s && *s==' ') s++;
  613. len = strlen(s);
  614. if (len < 18) {
  615. SetLastError( ERROR_INVALID_PARAMETER );
  616. return FALSE;
  617. }
  618. if ( *(s+2) == '-' ) { /* First format */
  619. st.wDay = (WORD) atoi(s);
  620. st.wMonth = (WORD) make_month(s+3);
  621. st.wYear = (WORD) atoi(s+7);
  622. st.wHour = (WORD) atoi(s+10);
  623. st.wMinute = (WORD) atoi(s+13);
  624. st.wSecond = (WORD) atoi(s+16);
  625. } else { /* Second format */
  626. if (len < 20) {
  627. SetLastError( ERROR_INVALID_PARAMETER );
  628. return FALSE;
  629. }
  630. st.wDay = iis_2atoi(s);
  631. st.wMonth = make_month(s+3);
  632. st.wYear = iis_2atoi(s+7) * 100 + iis_2atoi(s+9);
  633. st.wHour = iis_2atoi(s+12);
  634. st.wMinute = iis_2atoi(s+15);
  635. st.wSecond = iis_2atoi(s+18);
  636. }
  637. } else { /* Try the other format: Wed Jun 9 01:29:59 1993 GMT */
  638. s = (TCHAR *) pszTime;
  639. while (*s && *s==' ') s++;
  640. if ((int)strlen(s) < 24) {
  641. SetLastError( ERROR_INVALID_PARAMETER );
  642. return FALSE;
  643. }
  644. st.wDay = (WORD) atoi(s+8);
  645. st.wMonth = (WORD) make_month(s+4);
  646. st.wYear = (WORD) atoi(s+20);
  647. st.wHour = (WORD) atoi(s+11);
  648. st.wMinute = (WORD) atoi(s+14);
  649. st.wSecond = (WORD) atoi(s+17);
  650. }
  651. //
  652. // Adjust for dates with only two digits
  653. //
  654. if ( st.wYear < 1000 ) {
  655. if ( st.wYear < 50 ) {
  656. st.wYear += 2000;
  657. } else {
  658. st.wYear += 1900;
  659. }
  660. }
  661. if ( !NtSystemTimeToLargeInteger( &st,pliTime )) {
  662. SetLastError( ERROR_INVALID_PARAMETER );
  663. return FALSE;
  664. }
  665. return(TRUE);
  666. }
  667. /************************************************************
  668. * Cached Date Time Formats
  669. *
  670. * Formatting Date and Time for
  671. * HTTP headers & Logging Requests
  672. * is a costly operation.
  673. * Using default NT Formatting operations with wsprintf()
  674. * consumes about 6000 instructions/transaction
  675. *
  676. * Following code addresses this issue by
  677. * 1) Caching formatted date/time pair for all purposes
  678. * 2) Caching is done at the granularity of seconds/minute
  679. * If there is a match till seconds, we return entire
  680. * formatted information.
  681. * If there is a match till the minutes, then the seconds
  682. * portion is over-written using a seconds-lookup-table.
  683. *
  684. * Murali R. Krishnan (MuraliK) 23-Feb-1996
  685. ************************************************************/
  686. //
  687. // The delimiter string is : <logDelimiterChar><blank>
  688. // The delimiter char should be same as the one used for LOG_RECORD
  689. // in the file: ilogcls.cxx
  690. //
  691. const TCHAR G_PSZ_LOG_DELIMITER[3] = TEXT(", ");
  692. #ifdef ENABLE_AUX_COUNTERS
  693. # define CdtCountAccesses() InterlockedIncrement( &m_nAccesses)
  694. # define CdtCountMisses() InterlockedIncrement( &m_nMisses)
  695. # else // ENABLE_AUX_COUNTERS
  696. # define CdtCountAccesses() /* do nothing */
  697. # define CdtCountMisses() /* do nothing */
  698. # endif // ENABLE_AUX_COUNTERS
  699. VOID
  700. DATETIME_FORMAT_ENTRY::CopyFormattedData(
  701. IN const SYSTEMTIME * pst,
  702. OUT CHAR * pchDateTime) const
  703. {
  704. //
  705. // copy the formatted date/time information
  706. //
  707. CopyMemory(pchDateTime,
  708. m_rgchDateTime,
  709. m_cbDateTime
  710. );
  711. if ( m_stDateTime.wSecond != pst->wSecond) {
  712. //
  713. // seconds do not match. update seconds portion alone
  714. //
  715. LPSTR pch = pchDateTime + m_cchOffsetSeconds;
  716. *pch = g_rgchTwoDigits[pst->wSecond][0];
  717. *(pch + 1) = g_rgchTwoDigits[pst->wSecond][1];
  718. }
  719. return;
  720. }
  721. BOOL
  722. CDFTCache::CopyFormattedData(
  723. IN const SYSTEMTIME * pst,
  724. OUT CHAR * pchDateTime) const
  725. {
  726. // See <readmost.hxx> for an explanation of this routine
  727. const LONG nSequence = _ReadSequence();
  728. // Is the data being updated on another thread?
  729. if (nSequence != UPDATING)
  730. {
  731. // The weird const_cast syntax is necessitated by the volatile
  732. // attribute on m_tData (DATETIME_FORMAT_ENTRY).
  733. LPCSTR pchDate = FormattedBuffer();
  734. DWORD cbDateTime = DateTimeChars();
  735. // Copy the string
  736. CopyMemory(pchDateTime, pchDate, cbDateTime);
  737. if (Seconds() != pst->wSecond) {
  738. //
  739. // seconds do not match. update seconds portion alone
  740. //
  741. LPSTR pch = pchDateTime + OffsetSeconds();
  742. *pch = g_rgchTwoDigits[pst->wSecond][0];
  743. *(pch + 1) = g_rgchTwoDigits[pst->wSecond][1];
  744. }
  745. // If the sequence number is unchanged, the read was valid.
  746. const LONG nSequence2 = _ReadSequence();
  747. return (nSequence == nSequence2);
  748. }
  749. return FALSE;
  750. }
  751. VOID
  752. ASCLOG_DATETIME_CACHE::GenerateDateTimeString(
  753. IN PDFT_ENTRY pdft,
  754. IN const SYSTEMTIME * pst
  755. )
  756. /*++
  757. Description:
  758. This function generates the datetime formats for all predefined
  759. sequences. It attempts to generate the formatted date/time
  760. to the accuracy of a minute. If need be the seconds portion
  761. is obtained by indexing an array.
  762. This function should be called for a locked pdft entry,
  763. and the caller should make sure that the structures can be accessed
  764. freely for update
  765. Arguments:
  766. pst - pointer to system time for which the datetime format is required
  767. Returns:
  768. None
  769. --*/
  770. {
  771. CHAR rgchTime[25];
  772. CHAR * pchDateTime;
  773. DWORD cchLen;
  774. DBG_ASSERT( pdft != NULL && pst != NULL );
  775. //
  776. // Format date for Logging (dftLog)
  777. // Format is:
  778. // <Date><DelimiterString><Time><DelimiterString>
  779. //
  780. // We need to generate the date format again, only if it changes
  781. //
  782. pchDateTime = pdft->m_rgchDateTime;
  783. if ( !SameDate( &pdft->m_stDateTime, pst) ) {
  784. ::GetDateFormat(LOCALE_SYSTEM_DEFAULT,
  785. LOCALE_NOUSEROVERRIDE,
  786. pst, NULL,
  787. pchDateTime,
  788. 15);
  789. lstrcat( pchDateTime, G_PSZ_LOG_DELIMITER);
  790. //
  791. // cache the date length for future use.
  792. //
  793. pdft->m_cchDateLen = lstrlen( pchDateTime);
  794. }
  795. cchLen = pdft->m_cchDateLen;
  796. //
  797. // format the time portion
  798. //
  799. ::GetTimeFormat( LOCALE_SYSTEM_DEFAULT,
  800. (LOCALE_NOUSEROVERRIDE |
  801. TIME_FORCE24HOURFORMAT|
  802. TIME_NOTIMEMARKER),
  803. pst, NULL,
  804. rgchTime, 15);
  805. DBG_ASSERT(lstrlen(rgchTime) + lstrlen( G_PSZ_LOG_DELIMITER) <
  806. sizeof(rgchTime));
  807. lstrcat( rgchTime, G_PSZ_LOG_DELIMITER);
  808. //
  809. // append time to date generated
  810. //
  811. DBG_ASSERT( cchLen > 0); // range is fine
  812. lstrcpy(pchDateTime + cchLen, rgchTime);
  813. DBG_ASSERT( lstrlen( pchDateTime) < sizeof( pdft->m_rgchDateTime));
  814. //
  815. // Calculate the offset for seconds based on time format.
  816. // the time is usually formatted as hh:mm:ss if wHour >= 10 Offset =6
  817. // and is formatted as h:mm:ss if wHour < 10 Offset =5
  818. //
  819. pdft->m_cchOffsetSeconds = ( cchLen + 5 + ((pst->wHour < 10) ? 0 : 1));
  820. //
  821. // !!! for the german locale, it's always hh:mm:ss
  822. //
  823. if ( pdft->m_rgchDateTime[pdft->m_cchOffsetSeconds] == ':' ) {
  824. pdft->m_cchOffsetSeconds++;
  825. }
  826. pdft->m_cbDateTime = lstrlen( pdft->m_rgchDateTime) + 1;
  827. DBG_ASSERT(pdft->m_cbDateTime <= MAX_FORMATTED_DATETIME_LEN);
  828. //
  829. // store the valid time now
  830. //
  831. pdft->m_stDateTime = *pst;
  832. return;
  833. } // ASCLOG_DATETIME_CACHE::GenerateDateTimeString
  834. VOID
  835. EXTLOG_DATETIME_CACHE::GenerateDateTimeString(
  836. IN PDFT_ENTRY pdft,
  837. IN const SYSTEMTIME * pst
  838. )
  839. /*++
  840. Description:
  841. Used for W3C Extended Logging format.
  842. This function generates the datetime formats for all predefined
  843. sequences. It attempts to generate the formatted date/time
  844. to the accuracy of a minute. If need be the seconds portion
  845. is obtained by indexing an array.
  846. This function should be called for a locked pdft entry,
  847. and the caller should make sure that the structures can be accessed
  848. freely for update
  849. Arguments:
  850. pst - pointer to system time for which the datetime format is required
  851. Returns:
  852. None
  853. --*/
  854. {
  855. PCHAR pchDateTime;
  856. DWORD cchLen;
  857. DBG_ASSERT( pdft != NULL && pst != NULL );
  858. //
  859. // Format is:
  860. // Date YYYY-MM-DD
  861. // Time HH:MM:SS
  862. //
  863. pchDateTime = pdft->m_rgchDateTime;
  864. if ( !SameDate( &pdft->m_stDateTime, pst) ) {
  865. AppendYear( pchDateTime, pst->wYear );
  866. *pchDateTime++ = '-';
  867. AppendTwoDigits( pchDateTime, pst->wMonth );
  868. *pchDateTime++ = '-';
  869. AppendTwoDigits( pchDateTime, pst->wDay );
  870. //
  871. // cache the date length for future use.
  872. //
  873. pchDateTime++;
  874. pdft->m_cchDateLen = lstrlen( pdft->m_rgchDateTime );
  875. DBG_ASSERT( pdft->m_cchDateLen == 10 );
  876. } else {
  877. DBG_ASSERT( pdft->m_cchDateLen == 10 );
  878. pchDateTime += (pdft->m_cchDateLen+1);
  879. }
  880. cchLen = pdft->m_cchDateLen;
  881. //
  882. // format the time portion
  883. //
  884. AppendTwoDigits( pchDateTime, pst->wHour );
  885. *pchDateTime++ = ':';
  886. AppendTwoDigits( pchDateTime, pst->wMinute );
  887. *pchDateTime++ = ':';
  888. AppendTwoDigits( pchDateTime, pst->wSecond );
  889. pchDateTime++;
  890. //
  891. // Calculate the offset for seconds based on time format.
  892. // YYYY-MM-DD HH:MM:SS
  893. //
  894. pdft->m_cchOffsetSeconds = cchLen + 7;
  895. pdft->m_cbDateTime = DIFF(pchDateTime - (PCHAR)pdft->m_rgchDateTime);
  896. DBG_ASSERT(pdft->m_cbDateTime <= MAX_FORMATTED_DATETIME_LEN);
  897. //
  898. // store the valid time now
  899. //
  900. pdft->m_stDateTime = *pst;
  901. return;
  902. } // EXTLOG_DATETIME_CACHE::GenerateDateTimeString
  903. VOID
  904. W3_DATETIME_CACHE::GenerateDateTimeString(
  905. IN PDFT_ENTRY pdft,
  906. IN const SYSTEMTIME * pst
  907. )
  908. /*++
  909. Description:
  910. This function generates the datetime formats for all predefined
  911. sequences. It attempts to generate the formatted date/time
  912. to the accuracy of a minute. If need be the seconds portion
  913. is obtained by indexing an array.
  914. This function should be called for a locked pdft entry,
  915. and the caller should make sure that the structures can be accessed
  916. freely for update
  917. Arguments:
  918. pst - pointer to system time for which the datetime format is required
  919. Returns:
  920. None
  921. --*/
  922. {
  923. CHAR rgchTime[25];
  924. PCHAR pchDateTime;
  925. DWORD cchLen;
  926. DBG_ASSERT( pdft != NULL && pst != NULL );
  927. //
  928. // Format date for Logging (dftGmt)
  929. // Format is:
  930. // Date: <date-time> GMT\r\n
  931. //
  932. pchDateTime = pdft->m_rgchDateTime;
  933. static const char szDate[] = "Date: ";
  934. static const char szCRLF[] = "\r\n";
  935. CopyMemory( pchDateTime, szDate, sizeof(szDate) - 1 );
  936. pchDateTime += sizeof(szDate) - 1;
  937. if ( !::SystemTimeToGMT( *pst,
  938. pchDateTime,
  939. sizeof(pdft->m_rgchDateTime)
  940. - sizeof( szDate) - sizeof(szCRLF) + 1 ) )
  941. {
  942. pdft->m_rgchDateTime[0] = '\0';
  943. } else {
  944. pchDateTime += lstrlen( pchDateTime );
  945. pdft->m_cchOffsetSeconds =
  946. DIFF(pchDateTime - pdft->m_rgchDateTime)
  947. - 2 // minus 2 digits for seconds
  948. - 4; // minus " GMT"
  949. pdft->m_cchDateLen = pdft->m_cchOffsetSeconds
  950. - 7; // minus " hh:mm:"
  951. CopyMemory( pchDateTime, szCRLF, sizeof(szCRLF) );
  952. }
  953. pdft->m_cbDateTime = ( lstrlen( pdft->m_rgchDateTime ) + 1);
  954. DBG_ASSERT(pdft->m_cbDateTime <= MAX_FORMATTED_DATETIME_LEN);
  955. //
  956. // store the valid time now
  957. //
  958. pdft->m_stDateTime = *pst;
  959. return;
  960. } // W3_DATETIME_CACHE::GenerateDateTimeString
  961. CACHED_DATETIME_FORMATS::CACHED_DATETIME_FORMATS( VOID )
  962. :
  963. #if ENABLE_AUX_COUNTERS
  964. m_nMisses ( 0),
  965. m_nAccesses ( 0),
  966. #endif // ENABLE_AUX_COUNTERS
  967. m_idftCurrent ( 0)
  968. {
  969. DATETIME_FORMAT_ENTRY dft;
  970. ZeroMemory( &dft, sizeof(dft));
  971. for (int i = 0; i < CACHE_SIZE; i++)
  972. {
  973. m_rgDateTimes[i].Write(dft);
  974. }
  975. }
  976. DWORD
  977. CACHED_DATETIME_FORMATS::GetFormattedDateTime(
  978. IN const SYSTEMTIME * pst,
  979. OUT TCHAR * pchDateTime
  980. )
  981. /*++
  982. This function obtains formatted string for date specified in *pst.
  983. It uses a cache to do lookup for the formatted date and time.
  984. If all entries fail, then it calls the Formatting functions to
  985. generate a new format.
  986. It has been experimentally determined that the cost of formatting is too
  987. high and hence we resort to caching and this comprehensive lookup function.
  988. Also this function is NOT a GENERAL PURPOSE DATE-FORMAT cacher.
  989. We cache with the ASSUMPTION that the date format requests will be for
  990. consecutive time intervals.
  991. Arguments:
  992. pst - pointer to SYSTEMTIME
  993. pchDateTime - pointer to character buffer into which the formatted
  994. date will be copied.
  995. Returns:
  996. Length of string (excluding the NULL terminator)
  997. --*/
  998. {
  999. DBG_ASSERT( pst != NULL && pchDateTime != NULL);
  1000. CdtCountAccesses();
  1001. CDFTCache* pdft;
  1002. LONG i = m_idftCurrent + CACHE_SIZE; // modulo operation in loop
  1003. // => start at m_idftCurrent
  1004. // m_rgDateTimes is a circular buffer of CDFTCaches. The current entry
  1005. // is pointed to by m_idftCurrent. The second-most recent entry is at
  1006. // (m_idftCurrent - 1) % CACHE_SIZE. Etc.
  1007. for (int j = CACHE_SIZE ; --j >= 0; i--)
  1008. {
  1009. pdft = &m_rgDateTimes[i & CACHE_MASK];
  1010. if (pdft->IsHit(pst)
  1011. && pdft->CopyFormattedData(pst, pchDateTime))
  1012. {
  1013. return pdft->DateTimeChars() - 1;
  1014. }
  1015. }
  1016. // Not found in cache? Then generate the time string and add it
  1017. DATETIME_FORMAT_ENTRY dft;
  1018. dft.m_stDateTime.wYear = 0; // invalid date
  1019. GenerateDateTimeString(&dft, pst);
  1020. i = InterlockedIncrement(const_cast<LONG*>(&m_idftCurrent));
  1021. pdft = &m_rgDateTimes[i & CACHE_MASK];
  1022. pdft->Write(dft);
  1023. CdtCountMisses();
  1024. //
  1025. // The date time format is valid. Copy formatted date time. It is
  1026. // assumed that the buffer has sufficient space for the formatted date
  1027. //
  1028. dft.CopyFormattedData(pst, pchDateTime);
  1029. return dft.m_cbDateTime - 1;
  1030. } // CACHED_DATETIME_FORMATS::GetFormattedDateTime()
  1031. DWORD
  1032. CACHED_DATETIME_FORMATS::GetFormattedCurrentDateTime(
  1033. OUT PCHAR pchDateTime
  1034. )
  1035. {
  1036. DBG_ASSERT(pchDateTime != NULL);
  1037. SYSTEMTIME st;
  1038. IISGetCurrentTimeAsSystemTime(&st);
  1039. return GetFormattedDateTime(&st, pchDateTime);
  1040. }