DateTimeParser.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. //
  2. // DateTimeParser.cpp
  3. //
  4. // $Id: //poco/1.4/Foundation/src/DateTimeParser.cpp#5 $
  5. //
  6. // Library: Foundation
  7. // Package: DateTime
  8. // Module: DateTimeParser
  9. //
  10. // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
  11. // and Contributors.
  12. //
  13. // SPDX-License-Identifier: BSL-1.0
  14. //
  15. #include "Poco/DateTimeParser.h"
  16. #include "Poco/DateTimeFormat.h"
  17. #include "Poco/DateTime.h"
  18. #include "Poco/Exception.h"
  19. #include "Poco/Ascii.h"
  20. namespace Poco {
  21. #define SKIP_JUNK() \
  22. while (it != end && !Ascii::isDigit(*it)) ++it
  23. #define SKIP_DIGITS() \
  24. while (it != end && Ascii::isDigit(*it)) ++it
  25. #define PARSE_NUMBER(var) \
  26. while (it != end && Ascii::isDigit(*it)) var = var*10 + ((*it++) - '0')
  27. #define PARSE_NUMBER_N(var, n) \
  28. { int i = 0; while (i++ < n && it != end && Ascii::isDigit(*it)) var = var*10 + ((*it++) - '0'); }
  29. #define PARSE_FRACTIONAL_N(var, n) \
  30. { int i = 0; while (i < n && it != end && Ascii::isDigit(*it)) { var = var*10 + ((*it++) - '0'); i++; } while (i++ < n) var *= 10; }
  31. void DateTimeParser::parse(const std::string& fmt, const std::string& str, DateTime& dateTime, int& timeZoneDifferential)
  32. {
  33. if (fmt.empty() || str.empty())
  34. throw SyntaxException("Empty string.");
  35. int year = 0;
  36. int month = 0;
  37. int day = 0;
  38. int hour = 0;
  39. int minute = 0;
  40. int second = 0;
  41. int millis = 0;
  42. int micros = 0;
  43. int tzd = 0;
  44. std::string::const_iterator it = str.begin();
  45. std::string::const_iterator end = str.end();
  46. std::string::const_iterator itf = fmt.begin();
  47. std::string::const_iterator endf = fmt.end();
  48. while (itf != endf && it != end)
  49. {
  50. if (*itf == '%')
  51. {
  52. if (++itf != endf)
  53. {
  54. switch (*itf)
  55. {
  56. case 'w':
  57. case 'W':
  58. while (it != end && Ascii::isSpace(*it)) ++it;
  59. while (it != end && Ascii::isAlpha(*it)) ++it;
  60. break;
  61. case 'b':
  62. case 'B':
  63. month = parseMonth(it, end);
  64. break;
  65. case 'd':
  66. case 'e':
  67. case 'f':
  68. SKIP_JUNK();
  69. PARSE_NUMBER_N(day, 2);
  70. break;
  71. case 'm':
  72. case 'n':
  73. case 'o':
  74. SKIP_JUNK();
  75. PARSE_NUMBER_N(month, 2);
  76. break;
  77. case 'y':
  78. SKIP_JUNK();
  79. PARSE_NUMBER_N(year, 2);
  80. if (year >= 69)
  81. year += 1900;
  82. else
  83. year += 2000;
  84. break;
  85. case 'Y':
  86. SKIP_JUNK();
  87. PARSE_NUMBER_N(year, 4);
  88. break;
  89. case 'r':
  90. SKIP_JUNK();
  91. PARSE_NUMBER(year);
  92. if (year < 1000)
  93. {
  94. if (year >= 69)
  95. year += 1900;
  96. else
  97. year += 2000;
  98. }
  99. break;
  100. case 'H':
  101. case 'h':
  102. SKIP_JUNK();
  103. PARSE_NUMBER_N(hour, 2);
  104. break;
  105. case 'a':
  106. case 'A':
  107. hour = parseAMPM(it, end, hour);
  108. break;
  109. case 'M':
  110. SKIP_JUNK();
  111. PARSE_NUMBER_N(minute, 2);
  112. break;
  113. case 'S':
  114. SKIP_JUNK();
  115. PARSE_NUMBER_N(second, 2);
  116. break;
  117. case 's':
  118. SKIP_JUNK();
  119. PARSE_NUMBER_N(second, 2);
  120. if (it != end && (*it == '.' || *it == ','))
  121. {
  122. ++it;
  123. PARSE_FRACTIONAL_N(millis, 3);
  124. PARSE_FRACTIONAL_N(micros, 3);
  125. SKIP_DIGITS();
  126. }
  127. break;
  128. case 'i':
  129. SKIP_JUNK();
  130. PARSE_NUMBER_N(millis, 3);
  131. break;
  132. case 'c':
  133. SKIP_JUNK();
  134. PARSE_NUMBER_N(millis, 1);
  135. millis *= 100;
  136. break;
  137. case 'F':
  138. SKIP_JUNK();
  139. PARSE_FRACTIONAL_N(millis, 3);
  140. PARSE_FRACTIONAL_N(micros, 3);
  141. SKIP_DIGITS();
  142. break;
  143. case 'z':
  144. case 'Z':
  145. tzd = parseTZD(it, end);
  146. break;
  147. }
  148. ++itf;
  149. }
  150. }
  151. else ++itf;
  152. }
  153. if (month == 0) month = 1;
  154. if (day == 0) day = 1;
  155. if (DateTime::isValid(year, month, day, hour, minute, second, millis, micros))
  156. dateTime.assign(year, month, day, hour, minute, second, millis, micros);
  157. else
  158. throw SyntaxException("date/time component out of range");
  159. timeZoneDifferential = tzd;
  160. }
  161. DateTime DateTimeParser::parse(const std::string& fmt, const std::string& str, int& timeZoneDifferential)
  162. {
  163. DateTime result;
  164. parse(fmt, str, result, timeZoneDifferential);
  165. return result;
  166. }
  167. bool DateTimeParser::tryParse(const std::string& fmt, const std::string& str, DateTime& dateTime, int& timeZoneDifferential)
  168. {
  169. try
  170. {
  171. parse(fmt, str, dateTime, timeZoneDifferential);
  172. }
  173. catch (Exception&)
  174. {
  175. return false;
  176. }
  177. return true;
  178. }
  179. void DateTimeParser::parse(const std::string& str, DateTime& dateTime, int& timeZoneDifferential)
  180. {
  181. if (!tryParse(str, dateTime, timeZoneDifferential))
  182. throw SyntaxException("Unsupported or invalid date/time format");
  183. }
  184. DateTime DateTimeParser::parse(const std::string& str, int& timeZoneDifferential)
  185. {
  186. DateTime result;
  187. if (tryParse(str, result, timeZoneDifferential))
  188. return result;
  189. else
  190. throw SyntaxException("Unsupported or invalid date/time format");
  191. }
  192. bool DateTimeParser::tryParse(const std::string& str, DateTime& dateTime, int& timeZoneDifferential)
  193. {
  194. if (str.length() < 4) return false;
  195. if (str[3] == ',')
  196. return tryParse("%w, %e %b %r %H:%M:%S %Z", str, dateTime, timeZoneDifferential);
  197. else if (str[3] == ' ')
  198. return tryParse(DateTimeFormat::ASCTIME_FORMAT, str, dateTime, timeZoneDifferential);
  199. else if (str.find(',') < 10)
  200. return tryParse("%W, %e %b %r %H:%M:%S %Z", str, dateTime, timeZoneDifferential);
  201. else if (Ascii::isDigit(str[0]))
  202. {
  203. if (str.find(' ') != std::string::npos || str.length() == 10)
  204. return tryParse(DateTimeFormat::SORTABLE_FORMAT, str, dateTime, timeZoneDifferential);
  205. else if (str.find('.') != std::string::npos || str.find(',') != std::string::npos)
  206. return tryParse(DateTimeFormat::ISO8601_FRAC_FORMAT, str, dateTime, timeZoneDifferential);
  207. else
  208. return tryParse(DateTimeFormat::ISO8601_FORMAT, str, dateTime, timeZoneDifferential);
  209. }
  210. else return false;
  211. }
  212. int DateTimeParser::parseTZD(std::string::const_iterator& it, const std::string::const_iterator& end)
  213. {
  214. struct Zone
  215. {
  216. const char* designator;
  217. int timeZoneDifferential;
  218. };
  219. static Zone zones[] =
  220. {
  221. {"Z", 0},
  222. {"UT", 0},
  223. {"GMT", 0},
  224. {"BST", 1*3600},
  225. {"IST", 1*3600},
  226. {"WET", 0},
  227. {"WEST", 1*3600},
  228. {"CET", 1*3600},
  229. {"CEST", 2*3600},
  230. {"EET", 2*3600},
  231. {"EEST", 3*3600},
  232. {"MSK", 3*3600},
  233. {"MSD", 4*3600},
  234. {"NST", -3*3600-1800},
  235. {"NDT", -2*3600-1800},
  236. {"AST", -4*3600},
  237. {"ADT", -3*3600},
  238. {"EST", -5*3600},
  239. {"EDT", -4*3600},
  240. {"CST", -6*3600},
  241. {"CDT", -5*3600},
  242. {"MST", -7*3600},
  243. {"MDT", -6*3600},
  244. {"PST", -8*3600},
  245. {"PDT", -7*3600},
  246. {"AKST", -9*3600},
  247. {"AKDT", -8*3600},
  248. {"HST", -10*3600},
  249. {"AEST", 10*3600},
  250. {"AEDT", 11*3600},
  251. {"ACST", 9*3600+1800},
  252. {"ACDT", 10*3600+1800},
  253. {"AWST", 8*3600},
  254. {"AWDT", 9*3600}
  255. };
  256. int tzd = 0;
  257. while (it != end && Ascii::isSpace(*it)) ++it;
  258. if (it != end)
  259. {
  260. if (Ascii::isAlpha(*it))
  261. {
  262. std::string designator;
  263. designator += *it++;
  264. if (it != end && Ascii::isAlpha(*it)) designator += *it++;
  265. if (it != end && Ascii::isAlpha(*it)) designator += *it++;
  266. if (it != end && Ascii::isAlpha(*it)) designator += *it++;
  267. for (unsigned i = 0; i < sizeof(zones)/sizeof(Zone); ++i)
  268. {
  269. if (designator == zones[i].designator)
  270. {
  271. tzd = zones[i].timeZoneDifferential;
  272. break;
  273. }
  274. }
  275. }
  276. if (it != end && (*it == '+' || *it == '-'))
  277. {
  278. int sign = *it == '+' ? 1 : -1;
  279. ++it;
  280. int hours = 0;
  281. PARSE_NUMBER_N(hours, 2);
  282. if (it != end && *it == ':') ++it;
  283. int minutes = 0;
  284. PARSE_NUMBER_N(minutes, 2);
  285. tzd += sign*(hours*3600 + minutes*60);
  286. }
  287. }
  288. return tzd;
  289. }
  290. int DateTimeParser::parseMonth(std::string::const_iterator& it, const std::string::const_iterator& end)
  291. {
  292. std::string month;
  293. while (it != end && (Ascii::isSpace(*it) || Ascii::isPunct(*it))) ++it;
  294. bool isFirst = true;
  295. while (it != end && Ascii::isAlpha(*it))
  296. {
  297. char ch = (*it++);
  298. if (isFirst) { month += Ascii::toUpper(ch); isFirst = false; }
  299. else month += Ascii::toLower(ch);
  300. }
  301. if (month.length() < 3) throw SyntaxException("Month name must be at least three characters long", month);
  302. for (int i = 0; i < 12; ++i)
  303. {
  304. if (DateTimeFormat::MONTH_NAMES[i].find(month) == 0)
  305. return i + 1;
  306. }
  307. throw SyntaxException("Not a valid month name", month);
  308. }
  309. int DateTimeParser::parseDayOfWeek(std::string::const_iterator& it, const std::string::const_iterator& end)
  310. {
  311. std::string dow;
  312. while (it != end && (Ascii::isSpace(*it) || Ascii::isPunct(*it))) ++it;
  313. bool isFirst = true;
  314. while (it != end && Ascii::isAlpha(*it))
  315. {
  316. char ch = (*it++);
  317. if (isFirst) { dow += Ascii::toUpper(ch); isFirst = false; }
  318. else dow += Ascii::toLower(ch);
  319. }
  320. if (dow.length() < 3) throw SyntaxException("Weekday name must be at least three characters long", dow);
  321. for (int i = 0; i < 7; ++i)
  322. {
  323. if (DateTimeFormat::WEEKDAY_NAMES[i].find(dow) == 0)
  324. return i;
  325. }
  326. throw SyntaxException("Not a valid weekday name", dow);
  327. }
  328. int DateTimeParser::parseAMPM(std::string::const_iterator& it, const std::string::const_iterator& end, int hour)
  329. {
  330. std::string ampm;
  331. while (it != end && (Ascii::isSpace(*it) || Ascii::isPunct(*it))) ++it;
  332. while (it != end && Ascii::isAlpha(*it))
  333. {
  334. char ch = (*it++);
  335. ampm += Ascii::toUpper(ch);
  336. }
  337. if (ampm == "AM")
  338. {
  339. if (hour == 12)
  340. return 0;
  341. else
  342. return hour;
  343. }
  344. else if (ampm == "PM")
  345. {
  346. if (hour < 12)
  347. return hour + 12;
  348. else
  349. return hour;
  350. }
  351. else throw SyntaxException("Not a valid AM/PM designator", ampm);
  352. }
  353. } // namespace Poco