PageRenderTime 29ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/srclib/apr-util/misc/apr_date.c

https://bitbucket.org/jonasteuwen/apache2nginx
C | 637 lines | 362 code | 117 blank | 158 comment | 144 complexity | 9aa4f1a292ac097e2e4a4858c906dff5 MD5 | raw file
  1. /* Licensed to the Apache Software Foundation (ASF) under one or more
  2. * contributor license agreements. See the NOTICE file distributed with
  3. * this work for additional information regarding copyright ownership.
  4. * The ASF licenses this file to You under the Apache License, Version 2.0
  5. * (the "License"); you may not use this file except in compliance with
  6. * the License. You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /*
  17. * apr_date.c: date parsing utility routines
  18. * These routines are (hopefully) platform independent.
  19. *
  20. * 27 Oct 1996 Roy Fielding
  21. * Extracted (with many modifications) from mod_proxy.c and
  22. * tested with over 50,000 randomly chosen valid date strings
  23. * and several hundred variations of invalid date strings.
  24. *
  25. */
  26. #include "apr.h"
  27. #include "apr_lib.h"
  28. #define APR_WANT_STRFUNC
  29. #include "apr_want.h"
  30. #if APR_HAVE_STDLIB_H
  31. #include <stdlib.h>
  32. #endif
  33. #if APR_HAVE_CTYPE_H
  34. #include <ctype.h>
  35. #endif
  36. #include "apr_date.h"
  37. /*
  38. * Compare a string to a mask
  39. * Mask characters (arbitrary maximum is 256 characters, just in case):
  40. * @ - uppercase letter
  41. * $ - lowercase letter
  42. * & - hex digit
  43. * # - digit
  44. * ~ - digit or space
  45. * * - swallow remaining characters
  46. * <x> - exact match for any other character
  47. */
  48. APU_DECLARE(int) apr_date_checkmask(const char *data, const char *mask)
  49. {
  50. int i;
  51. char d;
  52. for (i = 0; i < 256; i++) {
  53. d = data[i];
  54. switch (mask[i]) {
  55. case '\0':
  56. return (d == '\0');
  57. case '*':
  58. return 1;
  59. case '@':
  60. if (!apr_isupper(d))
  61. return 0;
  62. break;
  63. case '$':
  64. if (!apr_islower(d))
  65. return 0;
  66. break;
  67. case '#':
  68. if (!apr_isdigit(d))
  69. return 0;
  70. break;
  71. case '&':
  72. if (!apr_isxdigit(d))
  73. return 0;
  74. break;
  75. case '~':
  76. if ((d != ' ') && !apr_isdigit(d))
  77. return 0;
  78. break;
  79. default:
  80. if (mask[i] != d)
  81. return 0;
  82. break;
  83. }
  84. }
  85. return 0; /* We only get here if mask is corrupted (exceeds 256) */
  86. }
  87. /*
  88. * Parses an HTTP date in one of three standard forms:
  89. *
  90. * Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
  91. * Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
  92. * Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
  93. *
  94. * and returns the apr_time_t number of microseconds since 1 Jan 1970 GMT,
  95. * or APR_DATE_BAD if this would be out of range or if the date is invalid.
  96. *
  97. * The restricted HTTP syntax is
  98. *
  99. * HTTP-date = rfc1123-date | rfc850-date | asctime-date
  100. *
  101. * rfc1123-date = wkday "," SP date1 SP time SP "GMT"
  102. * rfc850-date = weekday "," SP date2 SP time SP "GMT"
  103. * asctime-date = wkday SP date3 SP time SP 4DIGIT
  104. *
  105. * date1 = 2DIGIT SP month SP 4DIGIT
  106. * ; day month year (e.g., 02 Jun 1982)
  107. * date2 = 2DIGIT "-" month "-" 2DIGIT
  108. * ; day-month-year (e.g., 02-Jun-82)
  109. * date3 = month SP ( 2DIGIT | ( SP 1DIGIT ))
  110. * ; month day (e.g., Jun 2)
  111. *
  112. * time = 2DIGIT ":" 2DIGIT ":" 2DIGIT
  113. * ; 00:00:00 - 23:59:59
  114. *
  115. * wkday = "Mon" | "Tue" | "Wed"
  116. * | "Thu" | "Fri" | "Sat" | "Sun"
  117. *
  118. * weekday = "Monday" | "Tuesday" | "Wednesday"
  119. * | "Thursday" | "Friday" | "Saturday" | "Sunday"
  120. *
  121. * month = "Jan" | "Feb" | "Mar" | "Apr"
  122. * | "May" | "Jun" | "Jul" | "Aug"
  123. * | "Sep" | "Oct" | "Nov" | "Dec"
  124. *
  125. * However, for the sake of robustness (and Netscapeness), we ignore the
  126. * weekday and anything after the time field (including the timezone).
  127. *
  128. * This routine is intended to be very fast; 10x faster than using sscanf.
  129. *
  130. * Originally from Andrew Daviel <andrew@vancouver-webpages.com>, 29 Jul 96
  131. * but many changes since then.
  132. *
  133. */
  134. APU_DECLARE(apr_time_t) apr_date_parse_http(const char *date)
  135. {
  136. apr_time_exp_t ds;
  137. apr_time_t result;
  138. int mint, mon;
  139. const char *monstr, *timstr;
  140. static const int months[12] =
  141. {
  142. ('J' << 16) | ('a' << 8) | 'n', ('F' << 16) | ('e' << 8) | 'b',
  143. ('M' << 16) | ('a' << 8) | 'r', ('A' << 16) | ('p' << 8) | 'r',
  144. ('M' << 16) | ('a' << 8) | 'y', ('J' << 16) | ('u' << 8) | 'n',
  145. ('J' << 16) | ('u' << 8) | 'l', ('A' << 16) | ('u' << 8) | 'g',
  146. ('S' << 16) | ('e' << 8) | 'p', ('O' << 16) | ('c' << 8) | 't',
  147. ('N' << 16) | ('o' << 8) | 'v', ('D' << 16) | ('e' << 8) | 'c'};
  148. if (!date)
  149. return APR_DATE_BAD;
  150. while (*date && apr_isspace(*date)) /* Find first non-whitespace char */
  151. ++date;
  152. if (*date == '\0')
  153. return APR_DATE_BAD;
  154. if ((date = strchr(date, ' ')) == NULL) /* Find space after weekday */
  155. return APR_DATE_BAD;
  156. ++date; /* Now pointing to first char after space, which should be */
  157. /* start of the actual date information for all 4 formats. */
  158. if (apr_date_checkmask(date, "## @$$ #### ##:##:## *")) {
  159. /* RFC 1123 format with two days */
  160. ds.tm_year = ((date[7] - '0') * 10 + (date[8] - '0') - 19) * 100;
  161. if (ds.tm_year < 0)
  162. return APR_DATE_BAD;
  163. ds.tm_year += ((date[9] - '0') * 10) + (date[10] - '0');
  164. ds.tm_mday = ((date[0] - '0') * 10) + (date[1] - '0');
  165. monstr = date + 3;
  166. timstr = date + 12;
  167. }
  168. else if (apr_date_checkmask(date, "##-@$$-## ##:##:## *")) {
  169. /* RFC 850 format */
  170. ds.tm_year = ((date[7] - '0') * 10) + (date[8] - '0');
  171. if (ds.tm_year < 70)
  172. ds.tm_year += 100;
  173. ds.tm_mday = ((date[0] - '0') * 10) + (date[1] - '0');
  174. monstr = date + 3;
  175. timstr = date + 10;
  176. }
  177. else if (apr_date_checkmask(date, "@$$ ~# ##:##:## ####*")) {
  178. /* asctime format */
  179. ds.tm_year = ((date[16] - '0') * 10 + (date[17] - '0') - 19) * 100;
  180. if (ds.tm_year < 0)
  181. return APR_DATE_BAD;
  182. ds.tm_year += ((date[18] - '0') * 10) + (date[19] - '0');
  183. if (date[4] == ' ')
  184. ds.tm_mday = 0;
  185. else
  186. ds.tm_mday = (date[4] - '0') * 10;
  187. ds.tm_mday += (date[5] - '0');
  188. monstr = date;
  189. timstr = date + 7;
  190. }
  191. else if (apr_date_checkmask(date, "# @$$ #### ##:##:## *")) {
  192. /* RFC 1123 format with one day */
  193. ds.tm_year = ((date[6] - '0') * 10 + (date[7] - '0') - 19) * 100;
  194. if (ds.tm_year < 0)
  195. return APR_DATE_BAD;
  196. ds.tm_year += ((date[8] - '0') * 10) + (date[9] - '0');
  197. ds.tm_mday = (date[0] - '0');
  198. monstr = date + 2;
  199. timstr = date + 11;
  200. }
  201. else
  202. return APR_DATE_BAD;
  203. if (ds.tm_mday <= 0 || ds.tm_mday > 31)
  204. return APR_DATE_BAD;
  205. ds.tm_hour = ((timstr[0] - '0') * 10) + (timstr[1] - '0');
  206. ds.tm_min = ((timstr[3] - '0') * 10) + (timstr[4] - '0');
  207. ds.tm_sec = ((timstr[6] - '0') * 10) + (timstr[7] - '0');
  208. if ((ds.tm_hour > 23) || (ds.tm_min > 59) || (ds.tm_sec > 61))
  209. return APR_DATE_BAD;
  210. mint = (monstr[0] << 16) | (monstr[1] << 8) | monstr[2];
  211. for (mon = 0; mon < 12; mon++)
  212. if (mint == months[mon])
  213. break;
  214. if (mon == 12)
  215. return APR_DATE_BAD;
  216. if ((ds.tm_mday == 31) && (mon == 3 || mon == 5 || mon == 8 || mon == 10))
  217. return APR_DATE_BAD;
  218. /* February gets special check for leapyear */
  219. if ((mon == 1) &&
  220. ((ds.tm_mday > 29) ||
  221. ((ds.tm_mday == 29)
  222. && ((ds.tm_year & 3)
  223. || (((ds.tm_year % 100) == 0)
  224. && (((ds.tm_year % 400) != 100)))))))
  225. return APR_DATE_BAD;
  226. ds.tm_mon = mon;
  227. /* ap_mplode_time uses tm_usec and tm_gmtoff fields, but they haven't
  228. * been set yet.
  229. * It should be safe to just zero out these values.
  230. * tm_usec is the number of microseconds into the second. HTTP only
  231. * cares about second granularity.
  232. * tm_gmtoff is the number of seconds off of GMT the time is. By
  233. * definition all times going through this function are in GMT, so this
  234. * is zero.
  235. */
  236. ds.tm_usec = 0;
  237. ds.tm_gmtoff = 0;
  238. if (apr_time_exp_get(&result, &ds) != APR_SUCCESS)
  239. return APR_DATE_BAD;
  240. return result;
  241. }
  242. /*
  243. * Parses a string resembling an RFC 822 date. This is meant to be
  244. * leinent in its parsing of dates. Hence, this will parse a wider
  245. * range of dates than apr_date_parse_http.
  246. *
  247. * The prominent mailer (or poster, if mailer is unknown) that has
  248. * been seen in the wild is included for the unknown formats.
  249. *
  250. * Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
  251. * Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
  252. * Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
  253. * Sun, 6 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
  254. * Sun, 06 Nov 94 08:49:37 GMT ; RFC 822
  255. * Sun, 6 Nov 94 08:49:37 GMT ; RFC 822
  256. * Sun, 06 Nov 94 08:49 GMT ; Unknown [drtr@ast.cam.ac.uk]
  257. * Sun, 6 Nov 94 08:49 GMT ; Unknown [drtr@ast.cam.ac.uk]
  258. * Sun, 06 Nov 94 8:49:37 GMT ; Unknown [Elm 70.85]
  259. * Sun, 6 Nov 94 8:49:37 GMT ; Unknown [Elm 70.85]
  260. * Mon, 7 Jan 2002 07:21:22 GMT ; Unknown [Postfix]
  261. * Sun, 06-Nov-1994 08:49:37 GMT ; RFC 850 with four digit years
  262. *
  263. */
  264. #define TIMEPARSE(ds,hr10,hr1,min10,min1,sec10,sec1) \
  265. { \
  266. ds.tm_hour = ((hr10 - '0') * 10) + (hr1 - '0'); \
  267. ds.tm_min = ((min10 - '0') * 10) + (min1 - '0'); \
  268. ds.tm_sec = ((sec10 - '0') * 10) + (sec1 - '0'); \
  269. }
  270. #define TIMEPARSE_STD(ds,timstr) \
  271. { \
  272. TIMEPARSE(ds, timstr[0],timstr[1], \
  273. timstr[3],timstr[4], \
  274. timstr[6],timstr[7]); \
  275. }
  276. APU_DECLARE(apr_time_t) apr_date_parse_rfc(const char *date)
  277. {
  278. apr_time_exp_t ds;
  279. apr_time_t result;
  280. int mint, mon;
  281. const char *monstr, *timstr, *gmtstr;
  282. static const int months[12] =
  283. {
  284. ('J' << 16) | ('a' << 8) | 'n', ('F' << 16) | ('e' << 8) | 'b',
  285. ('M' << 16) | ('a' << 8) | 'r', ('A' << 16) | ('p' << 8) | 'r',
  286. ('M' << 16) | ('a' << 8) | 'y', ('J' << 16) | ('u' << 8) | 'n',
  287. ('J' << 16) | ('u' << 8) | 'l', ('A' << 16) | ('u' << 8) | 'g',
  288. ('S' << 16) | ('e' << 8) | 'p', ('O' << 16) | ('c' << 8) | 't',
  289. ('N' << 16) | ('o' << 8) | 'v', ('D' << 16) | ('e' << 8) | 'c' };
  290. if (!date)
  291. return APR_DATE_BAD;
  292. /* Not all dates have text days at the beginning. */
  293. if (!apr_isdigit(date[0]))
  294. {
  295. while (*date && apr_isspace(*date)) /* Find first non-whitespace char */
  296. ++date;
  297. if (*date == '\0')
  298. return APR_DATE_BAD;
  299. if ((date = strchr(date, ' ')) == NULL) /* Find space after weekday */
  300. return APR_DATE_BAD;
  301. ++date; /* Now pointing to first char after space, which should be */ }
  302. /* start of the actual date information for all 11 formats. */
  303. if (apr_date_checkmask(date, "## @$$ #### ##:##:## *")) { /* RFC 1123 format */
  304. ds.tm_year = ((date[7] - '0') * 10 + (date[8] - '0') - 19) * 100;
  305. if (ds.tm_year < 0)
  306. return APR_DATE_BAD;
  307. ds.tm_year += ((date[9] - '0') * 10) + (date[10] - '0');
  308. ds.tm_mday = ((date[0] - '0') * 10) + (date[1] - '0');
  309. monstr = date + 3;
  310. timstr = date + 12;
  311. gmtstr = date + 21;
  312. TIMEPARSE_STD(ds, timstr);
  313. }
  314. else if (apr_date_checkmask(date, "##-@$$-## ##:##:## *")) {/* RFC 850 format */
  315. ds.tm_year = ((date[7] - '0') * 10) + (date[8] - '0');
  316. if (ds.tm_year < 70)
  317. ds.tm_year += 100;
  318. ds.tm_mday = ((date[0] - '0') * 10) + (date[1] - '0');
  319. monstr = date + 3;
  320. timstr = date + 10;
  321. gmtstr = date + 19;
  322. TIMEPARSE_STD(ds, timstr);
  323. }
  324. else if (apr_date_checkmask(date, "@$$ ~# ##:##:## ####*")) {
  325. /* asctime format */
  326. ds.tm_year = ((date[16] - '0') * 10 + (date[17] - '0') - 19) * 100;
  327. if (ds.tm_year < 0)
  328. return APR_DATE_BAD;
  329. ds.tm_year += ((date[18] - '0') * 10) + (date[19] - '0');
  330. if (date[4] == ' ')
  331. ds.tm_mday = 0;
  332. else
  333. ds.tm_mday = (date[4] - '0') * 10;
  334. ds.tm_mday += (date[5] - '0');
  335. monstr = date;
  336. timstr = date + 7;
  337. gmtstr = NULL;
  338. TIMEPARSE_STD(ds, timstr);
  339. }
  340. else if (apr_date_checkmask(date, "# @$$ #### ##:##:## *")) {
  341. /* RFC 1123 format*/
  342. ds.tm_year = ((date[6] - '0') * 10 + (date[7] - '0') - 19) * 100;
  343. if (ds.tm_year < 0)
  344. return APR_DATE_BAD;
  345. ds.tm_year += ((date[8] - '0') * 10) + (date[9] - '0');
  346. ds.tm_mday = (date[0] - '0');
  347. monstr = date + 2;
  348. timstr = date + 11;
  349. gmtstr = date + 20;
  350. TIMEPARSE_STD(ds, timstr);
  351. }
  352. else if (apr_date_checkmask(date, "## @$$ ## ##:##:## *")) {
  353. /* This is the old RFC 1123 date format - many many years ago, people
  354. * used two-digit years. Oh, how foolish.
  355. *
  356. * Two-digit day, two-digit year version. */
  357. ds.tm_year = ((date[7] - '0') * 10) + (date[8] - '0');
  358. if (ds.tm_year < 70)
  359. ds.tm_year += 100;
  360. ds.tm_mday = ((date[0] - '0') * 10) + (date[1] - '0');
  361. monstr = date + 3;
  362. timstr = date + 10;
  363. gmtstr = date + 19;
  364. TIMEPARSE_STD(ds, timstr);
  365. }
  366. else if (apr_date_checkmask(date, " # @$$ ## ##:##:## *")) {
  367. /* This is the old RFC 1123 date format - many many years ago, people
  368. * used two-digit years. Oh, how foolish.
  369. *
  370. * Space + one-digit day, two-digit year version.*/
  371. ds.tm_year = ((date[7] - '0') * 10) + (date[8] - '0');
  372. if (ds.tm_year < 70)
  373. ds.tm_year += 100;
  374. ds.tm_mday = (date[1] - '0');
  375. monstr = date + 3;
  376. timstr = date + 10;
  377. gmtstr = date + 19;
  378. TIMEPARSE_STD(ds, timstr);
  379. }
  380. else if (apr_date_checkmask(date, "# @$$ ## ##:##:## *")) {
  381. /* This is the old RFC 1123 date format - many many years ago, people
  382. * used two-digit years. Oh, how foolish.
  383. *
  384. * One-digit day, two-digit year version. */
  385. ds.tm_year = ((date[6] - '0') * 10) + (date[7] - '0');
  386. if (ds.tm_year < 70)
  387. ds.tm_year += 100;
  388. ds.tm_mday = (date[0] - '0');
  389. monstr = date + 2;
  390. timstr = date + 9;
  391. gmtstr = date + 18;
  392. TIMEPARSE_STD(ds, timstr);
  393. }
  394. else if (apr_date_checkmask(date, "## @$$ ## ##:## *")) {
  395. /* Loser format. This is quite bogus. */
  396. ds.tm_year = ((date[7] - '0') * 10) + (date[8] - '0');
  397. if (ds.tm_year < 70)
  398. ds.tm_year += 100;
  399. ds.tm_mday = ((date[0] - '0') * 10) + (date[1] - '0');
  400. monstr = date + 3;
  401. timstr = date + 10;
  402. gmtstr = NULL;
  403. TIMEPARSE(ds, timstr[0],timstr[1], timstr[3],timstr[4], '0','0');
  404. }
  405. else if (apr_date_checkmask(date, "# @$$ ## ##:## *")) {
  406. /* Loser format. This is quite bogus. */
  407. ds.tm_year = ((date[6] - '0') * 10) + (date[7] - '0');
  408. if (ds.tm_year < 70)
  409. ds.tm_year += 100;
  410. ds.tm_mday = (date[0] - '0');
  411. monstr = date + 2;
  412. timstr = date + 9;
  413. gmtstr = NULL;
  414. TIMEPARSE(ds, timstr[0],timstr[1], timstr[3],timstr[4], '0','0');
  415. }
  416. else if (apr_date_checkmask(date, "## @$$ ## #:##:## *")) {
  417. /* Loser format. This is quite bogus. */
  418. ds.tm_year = ((date[7] - '0') * 10) + (date[8] - '0');
  419. if (ds.tm_year < 70)
  420. ds.tm_year += 100;
  421. ds.tm_mday = ((date[0] - '0') * 10) + (date[1] - '0');
  422. monstr = date + 3;
  423. timstr = date + 9;
  424. gmtstr = date + 18;
  425. TIMEPARSE(ds, '0',timstr[1], timstr[3],timstr[4], timstr[6],timstr[7]);
  426. }
  427. else if (apr_date_checkmask(date, "# @$$ ## #:##:## *")) {
  428. /* Loser format. This is quite bogus. */
  429. ds.tm_year = ((date[6] - '0') * 10) + (date[7] - '0');
  430. if (ds.tm_year < 70)
  431. ds.tm_year += 100;
  432. ds.tm_mday = (date[0] - '0');
  433. monstr = date + 2;
  434. timstr = date + 8;
  435. gmtstr = date + 17;
  436. TIMEPARSE(ds, '0',timstr[1], timstr[3],timstr[4], timstr[6],timstr[7]);
  437. }
  438. else if (apr_date_checkmask(date, " # @$$ #### ##:##:## *")) {
  439. /* RFC 1123 format with a space instead of a leading zero. */
  440. ds.tm_year = ((date[7] - '0') * 10 + (date[8] - '0') - 19) * 100;
  441. if (ds.tm_year < 0)
  442. return APR_DATE_BAD;
  443. ds.tm_year += ((date[9] - '0') * 10) + (date[10] - '0');
  444. ds.tm_mday = (date[1] - '0');
  445. monstr = date + 3;
  446. timstr = date + 12;
  447. gmtstr = date + 21;
  448. TIMEPARSE_STD(ds, timstr);
  449. }
  450. else if (apr_date_checkmask(date, "##-@$$-#### ##:##:## *")) {
  451. /* RFC 1123 with dashes instead of spaces between date/month/year
  452. * This also looks like RFC 850 with four digit years.
  453. */
  454. ds.tm_year = ((date[7] - '0') * 10 + (date[8] - '0') - 19) * 100;
  455. if (ds.tm_year < 0)
  456. return APR_DATE_BAD;
  457. ds.tm_year += ((date[9] - '0') * 10) + (date[10] - '0');
  458. ds.tm_mday = ((date[0] - '0') * 10) + (date[1] - '0');
  459. monstr = date + 3;
  460. timstr = date + 12;
  461. gmtstr = date + 21;
  462. TIMEPARSE_STD(ds, timstr);
  463. }
  464. else
  465. return APR_DATE_BAD;
  466. if (ds.tm_mday <= 0 || ds.tm_mday > 31)
  467. return APR_DATE_BAD;
  468. if ((ds.tm_hour > 23) || (ds.tm_min > 59) || (ds.tm_sec > 61))
  469. return APR_DATE_BAD;
  470. mint = (monstr[0] << 16) | (monstr[1] << 8) | monstr[2];
  471. for (mon = 0; mon < 12; mon++)
  472. if (mint == months[mon])
  473. break;
  474. if (mon == 12)
  475. return APR_DATE_BAD;
  476. if ((ds.tm_mday == 31) && (mon == 3 || mon == 5 || mon == 8 || mon == 10))
  477. return APR_DATE_BAD;
  478. /* February gets special check for leapyear */
  479. if ((mon == 1) &&
  480. ((ds.tm_mday > 29)
  481. || ((ds.tm_mday == 29)
  482. && ((ds.tm_year & 3)
  483. || (((ds.tm_year % 100) == 0)
  484. && (((ds.tm_year % 400) != 100)))))))
  485. return APR_DATE_BAD;
  486. ds.tm_mon = mon;
  487. /* tm_gmtoff is the number of seconds off of GMT the time is.
  488. *
  489. * We only currently support: [+-]ZZZZ where Z is the offset in
  490. * hours from GMT.
  491. *
  492. * If there is any confusion, tm_gmtoff will remain 0.
  493. */
  494. ds.tm_gmtoff = 0;
  495. /* Do we have a timezone ? */
  496. if (gmtstr) {
  497. int offset;
  498. switch (*gmtstr) {
  499. case '-':
  500. offset = atoi(gmtstr+1);
  501. ds.tm_gmtoff -= (offset / 100) * 60 * 60;
  502. ds.tm_gmtoff -= (offset % 100) * 60;
  503. break;
  504. case '+':
  505. offset = atoi(gmtstr+1);
  506. ds.tm_gmtoff += (offset / 100) * 60 * 60;
  507. ds.tm_gmtoff += (offset % 100) * 60;
  508. break;
  509. }
  510. }
  511. /* apr_time_exp_get uses tm_usec field, but it hasn't been set yet.
  512. * It should be safe to just zero out this value.
  513. * tm_usec is the number of microseconds into the second. HTTP only
  514. * cares about second granularity.
  515. */
  516. ds.tm_usec = 0;
  517. if (apr_time_exp_gmt_get(&result, &ds) != APR_SUCCESS)
  518. return APR_DATE_BAD;
  519. return result;
  520. }