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.

259 lines
9.4 KiB

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