rfctime.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  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. * $Id$
  38. * \endverbatim
  39. */
  40. #include <stdio.h>
  41. #include <string.h>
  42. #include <ctype.h>
  43. #include <pro/rfctime.h>
  44. static char rfc1123_buf[32];
  45. static char *wkdays[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
  46. static char *months[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
  47. /*!
  48. * \brief Skip spaces and tabs.
  49. *
  50. * \param str Pointer to the string containing the leading spaces.
  51. *
  52. * \return Pointer into the string after the last space character.
  53. */
  54. static char *skip_spaces(const char *str)
  55. {
  56. while (*str == ' ' || *str == '\t')
  57. str++;
  58. return (char *)str;
  59. }
  60. /*!
  61. * \brief Parse digits.
  62. *
  63. * Number base is 10.
  64. *
  65. * \param str Pointer to the string containing the digits.
  66. * \param year Points to the variable that will receive the binary value.
  67. *
  68. * \return Pointer into the string after the parsed characters.
  69. */
  70. static char *parse_digits(const char *str, int *val)
  71. {
  72. *val = 0;
  73. while (isdigit((unsigned char)*str)) {
  74. *val *= 10;
  75. *val += *str++ - '0';
  76. }
  77. return (char *)str;
  78. }
  79. /*!
  80. * \brief Parse string containing the year.
  81. *
  82. * Works with years including and excluding the century. If the
  83. * resulting value is lower than 70, the 21st century is assumed.
  84. * Values
  85. *
  86. * \param str Pointer to the time string.
  87. * \param year Points to the variable that will receive the years since 1900.
  88. *
  89. * \return Pointer into the string after the parsed characters.
  90. */
  91. char *TimeParseYear(const char *str, int *year)
  92. {
  93. str = parse_digits(str, year);
  94. if (*year < 70) {
  95. *year += 100;
  96. }
  97. else if (*year > 1900) {
  98. *year -= 1900;
  99. }
  100. return (char *)str;
  101. }
  102. /*!
  103. * \brief Parse a string for the name of a month.
  104. *
  105. * Does basic comparision only, no range checking etc. For example, if
  106. * the string starts with capital letter 'D', December is assumed without
  107. * further checking the rest. It will work for abbreviated as well as
  108. * full names.
  109. *
  110. * \param str Pointer to the time string. Must start with a capital letter
  111. * followed by lower case characters.
  112. * \param month Points to the variable that will receive the month (0..11).
  113. *
  114. * \return Pointer into the string after the parsed characters.
  115. */
  116. char *TimeParseMonth(const char *str, int *month)
  117. {
  118. if (*str == 'A') {
  119. if (*++str == 'p' || *str == 'P') {
  120. /* April */
  121. *month = 3;
  122. }
  123. else {
  124. /* August */
  125. *month = 7;
  126. }
  127. }
  128. else if (*str == 'D') {
  129. /* December */
  130. *month = 11;
  131. }
  132. else if (*str == 'F') {
  133. /* February */
  134. *month = 1;
  135. }
  136. else if (*str == 'J') {
  137. if (*++str == 'a' || *str == 'A') {
  138. /* January */
  139. *month = 0;
  140. }
  141. else if (*str && (*++str == 'l' || *str == 'L')) {
  142. /* July */
  143. *month = 6;
  144. }
  145. else {
  146. /* June */
  147. *month = 5;
  148. }
  149. }
  150. else if (*str == 'M') {
  151. if (*++str == 'a' && (*++str == 'r' || *str == 'R')) {
  152. /* March */
  153. *month = 2;
  154. }
  155. else {
  156. /* May */
  157. *month = 4;
  158. }
  159. }
  160. else if (*str == 'N') {
  161. /* November */
  162. *month = 10;
  163. }
  164. else if (*str == 'O') {
  165. /* October */
  166. *month = 9;
  167. }
  168. else {
  169. /* September */
  170. *month = 8;
  171. }
  172. while (isalpha((unsigned char)*str)) {
  173. str++;
  174. }
  175. return (char *)str;
  176. }
  177. /*!
  178. * \brief Parse date string.
  179. *
  180. * Parses a string of the format day 'x' month-name 'x' year, where
  181. * 'x' is any non-alphanumeric character.
  182. *
  183. * \param str Pointer to the date string.
  184. * \param mday Points to the variable that will receive the day (1..31).
  185. * \param mon Points to the variable that will receive the month (0..11).
  186. * \param year Points to the variable that will receive the years since 1900.
  187. *
  188. * \return Pointer into the string after the parsed characters.
  189. */
  190. char *TimeParseDmy(const char *str, int *mday, int *mon, int *year)
  191. {
  192. str = parse_digits(str, mday);
  193. while (*str && !isalpha((unsigned char)*str)) {
  194. str++;
  195. }
  196. str = TimeParseMonth(str, mon);
  197. while (*str && !isdigit((unsigned char)*str)) {
  198. str++;
  199. }
  200. str = TimeParseYear(str, year);
  201. return (char *)str;
  202. }
  203. /*!
  204. * \brief Parse time of day string.
  205. *
  206. * Parses a string of the format hour ':' minute ':' second.
  207. *
  208. * \param str Pointer to the time string.
  209. * \param hour Points to the variable that will receive the hour (0..23).
  210. * \param min Points to the variable that will receive the minute (0..59).
  211. * \param sec Points to the variable that will receive the second (0..59).
  212. *
  213. * \return Pointer into the string after the parsed characters.
  214. */
  215. char *TimeParseHms(const char *str, int *hour, int *min, int *sec)
  216. {
  217. str = parse_digits(str, hour);
  218. if (*str == ':') {
  219. str = parse_digits(str + 1, min);
  220. if (*str == ':') {
  221. str = parse_digits(str + 1, sec);
  222. }
  223. else {
  224. *sec = 0;
  225. }
  226. }
  227. else {
  228. *min = 0;
  229. }
  230. return (char *)str;
  231. }
  232. /*!
  233. * \brief Parse RFC date and time string.
  234. *
  235. * This routine accepts RFC 850, RFC 1123 and asctime time formats.
  236. *
  237. * \param str Pointer to the date and time string.
  238. *
  239. * \return Number of seconds since epoch or -1 in case of any error.
  240. */
  241. time_t RfcTimeParse(const char *str)
  242. {
  243. struct tm dts = { 0, 0, 0, 1, 0, 0, 0, 0, 0 };
  244. /* Skip leading whitespace. */
  245. str = skip_spaces(str);
  246. /* Skip weekday, optional in RFC 822. */
  247. if (isalpha((unsigned char)*str)) {
  248. while (*str && *str != ' ' && *str != '\t')
  249. str++;
  250. str = skip_spaces(str);
  251. }
  252. if (isalpha((unsigned char)*str)) {
  253. /* asctime format 'Fri Feb 2 2007 07:30:05'. */
  254. str = TimeParseMonth(str, &dts.tm_mon);
  255. str = skip_spaces(str);
  256. str = parse_digits(str, &dts.tm_mday);
  257. str = skip_spaces(str);
  258. str = TimeParseYear(str, &dts.tm_year);
  259. str = skip_spaces(str);
  260. str = TimeParseHms(str, &dts.tm_hour, &dts.tm_min, &dts.tm_sec);
  261. }
  262. else if (*str) {
  263. /* RFC 850 'Friday, 02-Feb-2007 07:30:05 GMT'. */
  264. /* RFC 1123 'Fri, 02 Feb 2007 07:30:05 GMT'. */
  265. str = TimeParseDmy(str, &dts.tm_mday, &dts.tm_mon, &dts.tm_year);
  266. str = skip_spaces(str);
  267. str = TimeParseHms(str, &dts.tm_hour, &dts.tm_min, &dts.tm_sec);
  268. }
  269. str = skip_spaces(str);
  270. if (strcmp(str, "GMT") == 0) {
  271. return _mkgmtime(&dts);
  272. }
  273. return mktime(&dts);
  274. }
  275. /*!
  276. * \brief Create RFC 1123 date and time string.
  277. *
  278. * \param tm Pointer to the date and time structure.
  279. *
  280. * \return Pointer to a static buffer containing the string.
  281. */
  282. char *Rfc1123TimeString(struct tm *tm)
  283. {
  284. sprintf(rfc1123_buf, "%s, %02d %s %04d %02d:%02d:%02d",
  285. wkdays[tm->tm_wday],
  286. tm->tm_mday, months[tm->tm_mon], tm->tm_year + 1900,
  287. tm->tm_hour, tm->tm_min, tm->tm_sec);
  288. return rfc1123_buf;
  289. }