rfctime.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. /*
  2. * Copyright (C) 2007 by egnite Software GmbH. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * 3. Neither the name of the copyright holders nor the names of
  14. * contributors may be used to endorse or promote products derived
  15. * from this software without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  18. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  19. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  20. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  21. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  22. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  23. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  24. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  25. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  26. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  27. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  28. * SUCH DAMAGE.
  29. *
  30. * For additional information see http://www.ethernut.de/
  31. */
  32. /*!
  33. * \file pro/rfctime.c
  34. * \brief RFC date and time conversion.
  35. *
  36. * \verbatim
  37. *
  38. * $Log$
  39. * Revision 1.2 2009/01/04 21:06:53 olereinhardt
  40. * 2009-01-04 Ole Reinhardt <ole.reinhardt@thermotemp.de>
  41. *
  42. * * pro/rfctime.c: Fixed time parsing in RfcTimeParse
  43. * Thanks to Michael Fischer!
  44. *
  45. * Revision 1.1 2008/02/15 16:46:09 haraldkipp
  46. * First release.
  47. *
  48. *
  49. * \endverbatim
  50. */
  51. #include <stdio.h>
  52. #include <string.h>
  53. #include <ctype.h>
  54. #include <pro/rfctime.h>
  55. static char rfc1123_buf[32];
  56. static char *wkdays[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
  57. static char *months[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
  58. /*!
  59. * \brief Skip spaces and tabs.
  60. *
  61. * \param str Pointer to the string containing the leading spaces.
  62. *
  63. * \return Pointer into the string after the last space character.
  64. */
  65. static char *skip_spaces(const char *str)
  66. {
  67. while (*str == ' ' || *str == '\t')
  68. str++;
  69. return (char *)str;
  70. }
  71. /*!
  72. * \brief Parse digits.
  73. *
  74. * Number base is 10.
  75. *
  76. * \param str Pointer to the string containing the digits.
  77. * \param year Points to the variable that will receive the binary value.
  78. *
  79. * \return Pointer into the string after the parsed characters.
  80. */
  81. static char *parse_digits(const char *str, int *val)
  82. {
  83. *val = 0;
  84. while (isdigit((unsigned char)*str)) {
  85. *val *= 10;
  86. *val += *str++ - '0';
  87. }
  88. return (char *)str;
  89. }
  90. /*!
  91. * \brief Parse string containing the year.
  92. *
  93. * Works with years including and excluding the century. If the
  94. * resulting value is lower than 70, the 21st century is assumed.
  95. * Values
  96. *
  97. * \param str Pointer to the time string.
  98. * \param year Points to the variable that will receive the years since 1900.
  99. *
  100. * \return Pointer into the string after the parsed characters.
  101. */
  102. char *TimeParseYear(const char *str, int *year)
  103. {
  104. str = parse_digits(str, year);
  105. if (*year < 70) {
  106. *year += 100;
  107. }
  108. else if (*year > 1900) {
  109. *year -= 1900;
  110. }
  111. return (char *)str;
  112. }
  113. /*!
  114. * \brief Parse a string for the name of a month.
  115. *
  116. * Does basic comparision only, no range checking etc. For example, if
  117. * the string starts with capital letter 'D', December is assumed without
  118. * further checking the rest. It will work for abbreviated as well as
  119. * full names.
  120. *
  121. * \param str Pointer to the time string. Must start with a capital letter
  122. * followed by lower case characters.
  123. * \param month Points to the variable that will receive the month (0..11).
  124. *
  125. * \return Pointer into the string after the parsed characters.
  126. */
  127. char *TimeParseMonth(const char *str, int *month)
  128. {
  129. if (*str == 'A') {
  130. if (*++str == 'p' || *str == 'P') {
  131. /* April */
  132. *month = 3;
  133. }
  134. else {
  135. /* August */
  136. *month = 7;
  137. }
  138. }
  139. else if (*str == 'D') {
  140. /* December */
  141. *month = 11;
  142. }
  143. else if (*str == 'F') {
  144. /* February */
  145. *month = 1;
  146. }
  147. else if (*str == 'J') {
  148. if (*++str == 'a' || *str == 'A') {
  149. /* January */
  150. *month = 0;
  151. }
  152. else if (*str && (*++str == 'l' || *str == 'L')) {
  153. /* July */
  154. *month = 6;
  155. }
  156. else {
  157. /* June */
  158. *month = 5;
  159. }
  160. }
  161. else if (*str == 'M') {
  162. if (*++str == 'a' && (*++str == 'r' || *str == 'R')) {
  163. /* March */
  164. *month = 2;
  165. }
  166. else {
  167. /* May */
  168. *month = 4;
  169. }
  170. }
  171. else if (*str == 'N') {
  172. /* November */
  173. *month = 10;
  174. }
  175. else if (*str == 'O') {
  176. /* October */
  177. *month = 9;
  178. }
  179. else {
  180. /* September */
  181. *month = 8;
  182. }
  183. while (isalpha((unsigned char)*str)) {
  184. str++;
  185. }
  186. return (char *)str;
  187. }
  188. /*!
  189. * \brief Parse date string.
  190. *
  191. * Parses a string of the format day 'x' month-name 'x' year, where
  192. * 'x' is any non-alphanumeric character.
  193. *
  194. * \param str Pointer to the date string.
  195. * \param mday Points to the variable that will receive the day (1..31).
  196. * \param mon Points to the variable that will receive the month (0..11).
  197. * \param year Points to the variable that will receive the years since 1900.
  198. *
  199. * \return Pointer into the string after the parsed characters.
  200. */
  201. char *TimeParseDmy(const char *str, int *mday, int *mon, int *year)
  202. {
  203. str = parse_digits(str, mday);
  204. while (*str && !isalpha((unsigned char)*str)) {
  205. str++;
  206. }
  207. str = TimeParseMonth(str, mon);
  208. while (*str && !isdigit((unsigned char)*str)) {
  209. str++;
  210. }
  211. str = TimeParseYear(str, year);
  212. return (char *)str;
  213. }
  214. /*!
  215. * \brief Parse time of day string.
  216. *
  217. * Parses a string of the format hour ':' minute ':' second.
  218. *
  219. * \param str Pointer to the time string.
  220. * \param hour Points to the variable that will receive the hour (0..23).
  221. * \param min Points to the variable that will receive the minute (0..59).
  222. * \param sec Points to the variable that will receive the second (0..59).
  223. *
  224. * \return Pointer into the string after the parsed characters.
  225. */
  226. char *TimeParseHms(const char *str, int *hour, int *min, int *sec)
  227. {
  228. str = parse_digits(str, hour);
  229. if (*str == ':') {
  230. str = parse_digits(str + 1, min);
  231. if (*str == ':') {
  232. str = parse_digits(str + 1, sec);
  233. }
  234. else {
  235. *sec = 0;
  236. }
  237. }
  238. else {
  239. *min = 0;
  240. }
  241. return (char *)str;
  242. }
  243. /*!
  244. * \brief Parse RFC date and time string.
  245. *
  246. * This routine accepts RFC 850, RFC 1123 and asctime time formats.
  247. *
  248. * \param str Pointer to the date and time string.
  249. *
  250. * \return Number of seconds since epoch or -1 in case of any error.
  251. */
  252. time_t RfcTimeParse(const char *str)
  253. {
  254. struct _tm dts = { 0, 0, 0, 1, 0, 0, 0, 0, 0 };
  255. /* Skip leading whitespace. */
  256. str = skip_spaces(str);
  257. /* Skip weekday, optional in RFC 822. */
  258. if (isalpha((unsigned char)*str)) {
  259. while (*str && *str != ' ' && *str != '\t')
  260. str++;
  261. str = skip_spaces(str);
  262. }
  263. if (isalpha((unsigned char)*str)) {
  264. /* asctime format 'Fri Feb 2 2007 07:30:05'. */
  265. str = TimeParseMonth(str, &dts.tm_mon);
  266. str = skip_spaces(str);
  267. str = parse_digits(str, &dts.tm_mday);
  268. str = skip_spaces(str);
  269. str = TimeParseYear(str, &dts.tm_year);
  270. str = skip_spaces(str);
  271. str = TimeParseHms(str, &dts.tm_hour, &dts.tm_min, &dts.tm_sec);
  272. }
  273. else if (*str) {
  274. /* RFC 850 'Friday, 02-Feb-2007 07:30:05 GMT'. */
  275. /* RFC 1123 'Fri, 02 Feb 2007 07:30:05 GMT'. */
  276. str = TimeParseDmy(str, &dts.tm_mday, &dts.tm_mon, &dts.tm_year);
  277. str = skip_spaces(str);
  278. str = TimeParseHms(str, &dts.tm_hour, &dts.tm_min, &dts.tm_sec);
  279. }
  280. str = skip_spaces(str);
  281. if (strcmp(str, "GMT") == 0) {
  282. return mktime(&dts);
  283. }
  284. return _mkgmtime(&dts);
  285. }
  286. /*!
  287. * \brief Create RFC 1123 date and time string.
  288. *
  289. * \param tm Pointer to the date and time structure.
  290. *
  291. * \return Pointer to a static buffer containing the string.
  292. */
  293. char *Rfc1123TimeString(struct _tm *tm)
  294. {
  295. sprintf(rfc1123_buf, "%s, %02d %s %04d %02d:%02d:%02d",
  296. wkdays[tm->tm_wday],
  297. tm->tm_mday, months[tm->tm_mon], tm->tm_year + 1900,
  298. tm->tm_hour, tm->tm_min, tm->tm_sec);
  299. return rfc1123_buf;
  300. }