Leaked source code of windows server 2003
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.

268 lines
8.3 KiB

  1. #include <_apipch.h>
  2. #include "iso8601.h"
  3. // This code implements a parser & generater for the ISO 8601 date format.
  4. // This table defines different "types" of characters for use as the columns
  5. // of the state table
  6. unsigned char iso8601chartable[256] = {
  7. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  8. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  9. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0x82, 0,
  10. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0, 0, 0, 0, 0,
  11. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  12. 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0,
  13. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  14. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  15. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  16. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  17. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  18. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  19. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  20. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  21. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  22. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  23. };
  24. // State table
  25. // 0x80 bit = Error
  26. // 0x20 = Add character & advance to next field
  27. // 0x40 = Add character & advance to next field + skip one (for day of week)
  28. // 0x1f = Mask to determine next state #
  29. // Columns = input character type: unknown, number, "-", "T", ":", "Z"
  30. unsigned char iso8601StateTable[][6] =
  31. {
  32. 0x80, 0x01, 0x25, 0x80, 0x80, 0x80, // year
  33. 0x80, 0x02, 0x80, 0x80, 0x80, 0x80,
  34. 0x80, 0x03, 0x80, 0x80, 0x80, 0x80,
  35. 0x80, 0x24, 0x80, 0x80, 0x80, 0x80,
  36. 0x80, 0x06, 0x05, 0x85, 0x85, 0x05, //0x04 month
  37. 0x80, 0x06, 0x48, 0x80, 0x80, 0x80,
  38. 0x80, 0x47, 0x80, 0x80, 0x80, 0x80,
  39. 0x80, 0x09, 0x08, 0x88, 0x88, 0x08, //0x07 day
  40. 0x80, 0x09, 0x8b, 0x2b, 0x8b, 0x80,
  41. 0x80, 0x2a, 0x80, 0x80, 0x80, 0x80,
  42. 0x80, 0x0c, 0x8b, 0x0b, 0x8b, 0x08, //0x0a hour
  43. 0x80, 0x0c, 0x80, 0x80, 0x80, 0x80,
  44. 0x80, 0x2d, 0x80, 0x80, 0x80, 0x80,
  45. 0x80, 0x0f, 0x8e, 0x8e, 0x0e, 0x08, //0x0d min
  46. 0x80, 0x0f, 0x80, 0x80, 0x80, 0x80,
  47. 0x80, 0x30, 0x80, 0x80, 0x80, 0x80,
  48. 0x80, 0x12, 0x91, 0x91, 0x11, 0x08, //0x10 sec
  49. 0x80, 0x12, 0x80, 0x80, 0x80, 0x80,
  50. 0x80, 0x30, 0x80, 0x80, 0x80, 0x80,
  51. };
  52. HRESULT iso8601ToFileTime(char *pszisoDate, FILETIME *pftTime, BOOL fLenient, BOOL fPartial)
  53. {
  54. SYSTEMTIME stTime;
  55. HRESULT hr;
  56. hr = iso8601ToSysTime(pszisoDate, &stTime, fLenient, fPartial);
  57. if (SUCCEEDED(hr))
  58. {
  59. if (SystemTimeToFileTime( &stTime, pftTime))
  60. return S_OK;
  61. else
  62. return E_FAIL;
  63. }
  64. return hr;
  65. }
  66. // Convert a character string formatted as iso8601 into a SYSTEMTIME structure
  67. // Supports both basic & extended forms of iso8601.
  68. // isoDate: Input string. It can be null or space terminated.
  69. // pSysTime: Output SYSTEMTIME structure
  70. // fLenient: true for normal operation. "false" if you want to detect incorrectly
  71. // formatted iso8601. Will still return the "best guess" value.
  72. // fPartial: Set to true if you will accept partial results. Note that this just fills
  73. // in zeros where data is missing, which strictly speaking can't be distinguished
  74. // from real zeros in this implementation. An improvement would have a second
  75. // structure to fill in with validity bits.
  76. HRESULT iso8601ToSysTime(char *pszisoDate, SYSTEMTIME *pSysTime, BOOL fLenient, BOOL fPartial)
  77. {
  78. HRESULT hr = S_OK;
  79. WORD *dateWords = (WORD *) pSysTime;
  80. WORD *endWord = dateWords + 7; // To detect the end of the date
  81. int state = 0;
  82. int pos = 0;
  83. unsigned char action;
  84. if (NULL == pszisoDate || NULL == pSysTime)
  85. {
  86. if (NULL != pSysTime)
  87. ZeroMemory(pSysTime, sizeof(SYSTEMTIME));
  88. return E_INVALIDARG;
  89. }
  90. *dateWords = 0;
  91. // Main state machine loop. Loop until a space or null.
  92. while(*pszisoDate && *pszisoDate != ' ')
  93. {
  94. char code = iso8601chartable[*pszisoDate];
  95. if(code & 0x80)
  96. {
  97. if(!fLenient)
  98. hr = E_FAIL; // Illegal character only when lenient
  99. code = code & 0x7f;
  100. }
  101. action = iso8601StateTable[state][code];
  102. state = action&0x1f; // Calculate the next state
  103. if(code == 1) // The character code 1 is always a number which gets accumulated
  104. *dateWords = *dateWords * 10 + *pszisoDate - '0';
  105. switch(action >> 5)
  106. {
  107. case 0x1:
  108. if(!fPartial && !*dateWords)
  109. hr = E_FAIL; // Only partial, error
  110. if(dateWords == endWord) // Prevent an overflow
  111. return S_OK;
  112. dateWords++;
  113. *dateWords = 0;
  114. break;
  115. case 0x2: // Finish piece & advance twice (past day of week)
  116. if(!fPartial && !*dateWords)
  117. hr = E_FAIL; // Only partial, error
  118. // We don't need to check for an overflow here since the state machine
  119. // only calls this to skip "dayofweek" in the SYSTEMTIME structure.
  120. // We could do dateWords+=2 instead of the following if leaving random
  121. // values in dayofweek is acceptable.
  122. dateWords++;
  123. *dateWords = 0;
  124. dateWords++;
  125. *dateWords = 0;
  126. break;
  127. }
  128. if((action & 0x80) && !fLenient)
  129. hr = E_FAIL;
  130. pszisoDate++;
  131. }
  132. // Zero out the rest of the SYSTEMTIME structure
  133. while(dateWords < endWord)
  134. *(++dateWords) = 0;
  135. return hr;
  136. }
  137. // The function toExtended accepts a FILETIME and converts it into the ISO8601 extended
  138. // form, placeing it in the character buffer 'buf'. The buffer 'buf' must have room for
  139. // a minimum of 40 characters to support the longest forms of 8601 (currently only 21 are used).
  140. HRESULT FileTimeToiso8601(FILETIME *pftTime, char *pszBuf)
  141. {
  142. SYSTEMTIME stTime;
  143. if (NULL == pftTime)
  144. return E_INVALIDARG;
  145. if (FileTimeToSystemTime( pftTime, &stTime))
  146. {
  147. return SysTimeToiso8601(&stTime, pszBuf);
  148. }
  149. else
  150. return E_FAIL;
  151. }
  152. // The function toExtended accepts a SYSTEMTIME and converts it into the ISO8601 extended
  153. // form, placeing it in the character buffer 'buf'. The buffer 'buf' must have room for
  154. // a minimum of 40 characters to support the longest forms of 8601 (currently only 21 are used).
  155. HRESULT SysTimeToiso8601(SYSTEMTIME *pstTime, char *pszBuf)
  156. {
  157. if (NULL == pstTime || NULL == pszBuf)
  158. {
  159. if (NULL != pstTime)
  160. ZeroMemory(pstTime, sizeof(SYSTEMTIME));
  161. return E_INVALIDARG;
  162. }
  163. pszBuf[0] = pstTime->wYear / 1000 + '0';
  164. pszBuf[1] = ((pstTime->wYear / 100) % 10) + '0';
  165. pszBuf[2] = ((pstTime->wYear / 10) % 10) + '0';
  166. pszBuf[3] = ((pstTime->wYear) % 10) + '0';
  167. pszBuf[4] = '.';
  168. pszBuf[5] = pstTime->wMonth / 10 + '0';
  169. pszBuf[6] = (pstTime->wMonth % 10) + '0';
  170. pszBuf[7] = '.';
  171. pszBuf[8] = pstTime->wDay / 10 + '0';
  172. pszBuf[9] = (pstTime->wDay % 10) + '0';
  173. pszBuf[10] = 'T';
  174. pszBuf[11] = pstTime->wHour / 10 + '0';
  175. pszBuf[12] = (pstTime->wHour % 10) + '0';
  176. pszBuf[13] = ':';
  177. pszBuf[14] = pstTime->wMinute / 10 + '0';
  178. pszBuf[15] = (pstTime->wMinute % 10) + '0';
  179. pszBuf[16] = ':';
  180. pszBuf[17] = pstTime->wSecond / 10 + '0';
  181. pszBuf[18] = (pstTime->wSecond % 10) + '0';
  182. pszBuf[19] = 'Z';
  183. pszBuf[20] = 0;
  184. return S_OK;
  185. }
  186. #ifdef STANDALONETEST8601
  187. // This code does some simple tests.
  188. int main(int argc, char **argv)
  189. {
  190. char *isoDate;
  191. SYSTEMTIME sysTime;
  192. char outBuf[256];
  193. HRESULT hr;
  194. isoDate = "1997.01.01T14:23:53Z";
  195. hr = iso8601::toSysTime(isoDate, &sysTime, FALSE);
  196. if(hr != S_OK)
  197. printf("error.\n");
  198. iso8601::toExtended(&sysTime, outBuf);
  199. printf("%s\n", outBuf);
  200. isoDate = "19970101T142353Z";
  201. hr = iso8601::toSysTime(isoDate, &sysTime, FALSE);
  202. if(hr != S_OK)
  203. printf("error.\n");
  204. iso8601::toExtended(&sysTime, outBuf);
  205. printf("%s\n", outBuf);
  206. isoDate = "1997:01.01T14:23:53Z";
  207. hr = iso8601::toSysTime(isoDate, &sysTime, FALSE);
  208. if(hr != S_OK)
  209. printf("error (correct).\n");
  210. iso8601::toExtended(&sysTime, outBuf);
  211. printf("%s\n", outBuf);
  212. isoDate = ".01.01T14:23:53Z";
  213. hr = iso8601::toSysTime(isoDate, &sysTime, FALSE);
  214. if(hr != S_OK)
  215. printf("error.\n");
  216. iso8601::toExtended(&sysTime, outBuf);
  217. printf("%s\n", outBuf);
  218. isoDate = "..01T14:23:53Z";
  219. hr = iso8601::toSysTime(isoDate, &sysTime, FALSE);
  220. if(hr != S_OK)
  221. printf("error.\n");
  222. iso8601::toExtended(&sysTime, outBuf);
  223. printf("%s\n", outBuf);
  224. isoDate = "..T14:23:53Z";
  225. hr = iso8601::toSysTime(isoDate, &sysTime, FALSE);
  226. if(hr != S_OK)
  227. printf("error.\n");
  228. iso8601::toExtended(&sysTime, outBuf);
  229. printf("%s\n", outBuf);
  230. return 0;
  231. }
  232. #endif // STANDALONETEST8601