apr_date.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  1. /* Copyright 2000-2005 The Apache Software Foundation or its licensors, as
  2. * applicable.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * 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 months 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 + 20;
  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. ds.tm_year = ((date[7] - '0') * 10) + (date[8] - '0');
  356. if (ds.tm_year < 70)
  357. ds.tm_year += 100;
  358. ds.tm_mday = ((date[0] - '0') * 10) + (date[1] - '0');
  359. monstr = date + 3;
  360. timstr = date + 10;
  361. gmtstr = date + 19;
  362. TIMEPARSE_STD(ds, timstr);
  363. }
  364. else if (apr_date_checkmask(date, "# @$$ ## ##:##:## *")) {
  365. /* This is the old RFC 1123 date format - many many years ago, people
  366. * used two-digit years. Oh, how foolish. */
  367. ds.tm_year = ((date[6] - '0') * 10) + (date[7] - '0');
  368. if (ds.tm_year < 70)
  369. ds.tm_year += 100;
  370. ds.tm_mday = (date[0] - '0');
  371. monstr = date + 2;
  372. timstr = date + 9;
  373. gmtstr = date + 18;
  374. TIMEPARSE_STD(ds, timstr);
  375. }
  376. else if (apr_date_checkmask(date, "## @$$ ## ##:## *")) {
  377. /* Loser format. This is quite bogus. */
  378. ds.tm_year = ((date[7] - '0') * 10) + (date[8] - '0');
  379. if (ds.tm_year < 70)
  380. ds.tm_year += 100;
  381. ds.tm_mday = ((date[0] - '0') * 10) + (date[1] - '0');
  382. monstr = date + 3;
  383. timstr = date + 10;
  384. gmtstr = NULL;
  385. TIMEPARSE(ds, timstr[0],timstr[1], timstr[3],timstr[4], '0','0');
  386. }
  387. else if (apr_date_checkmask(date, "# @$$ ## ##:## *")) {
  388. /* Loser format. This is quite bogus. */
  389. ds.tm_year = ((date[6] - '0') * 10) + (date[7] - '0');
  390. if (ds.tm_year < 70)
  391. ds.tm_year += 100;
  392. ds.tm_mday = (date[0] - '0');
  393. monstr = date + 2;
  394. timstr = date + 9;
  395. gmtstr = NULL;
  396. TIMEPARSE(ds, timstr[0],timstr[1], timstr[3],timstr[4], '0','0');
  397. }
  398. else if (apr_date_checkmask(date, "## @$$ ## #:##:## *")) {
  399. /* Loser format. This is quite bogus. */
  400. ds.tm_year = ((date[7] - '0') * 10) + (date[8] - '0');
  401. if (ds.tm_year < 70)
  402. ds.tm_year += 100;
  403. ds.tm_mday = ((date[0] - '0') * 10) + (date[1] - '0');
  404. monstr = date + 3;
  405. timstr = date + 9;
  406. gmtstr = date + 18;
  407. TIMEPARSE(ds, '0',timstr[1], timstr[3],timstr[4], timstr[6],timstr[7]);
  408. }
  409. else if (apr_date_checkmask(date, "# @$$ ## #:##:## *")) {
  410. /* Loser format. This is quite bogus. */
  411. ds.tm_year = ((date[6] - '0') * 10) + (date[7] - '0');
  412. if (ds.tm_year < 70)
  413. ds.tm_year += 100;
  414. ds.tm_mday = (date[0] - '0');
  415. monstr = date + 2;
  416. timstr = date + 8;
  417. gmtstr = date + 17;
  418. TIMEPARSE(ds, '0',timstr[1], timstr[3],timstr[4], timstr[6],timstr[7]);
  419. }
  420. else if (apr_date_checkmask(date, " # @$$ #### ##:##:## *")) {
  421. /* RFC 1123 format with a space instead of a leading zero. */
  422. ds.tm_year = ((date[7] - '0') * 10 + (date[8] - '0') - 19) * 100;
  423. if (ds.tm_year < 0)
  424. return APR_DATE_BAD;
  425. ds.tm_year += ((date[9] - '0') * 10) + (date[10] - '0');
  426. ds.tm_mday = (date[1] - '0');
  427. monstr = date + 3;
  428. timstr = date + 12;
  429. gmtstr = date + 20;
  430. TIMEPARSE_STD(ds, timstr);
  431. }
  432. else if (apr_date_checkmask(date, "##-@$$-#### ##:##:## *")) {
  433. /* RFC 1123 with dashes instead of spaces between date/month/year
  434. * This also looks like RFC 850 with four digit years.
  435. */
  436. ds.tm_year = ((date[7] - '0') * 10 + (date[8] - '0') - 19) * 100;
  437. if (ds.tm_year < 0)
  438. return APR_DATE_BAD;
  439. ds.tm_year += ((date[9] - '0') * 10) + (date[10] - '0');
  440. ds.tm_mday = ((date[0] - '0') * 10) + (date[1] - '0');
  441. monstr = date + 3;
  442. timstr = date + 12;
  443. gmtstr = date + 21;
  444. TIMEPARSE_STD(ds, timstr);
  445. }
  446. else
  447. return APR_DATE_BAD;
  448. if (ds.tm_mday <= 0 || ds.tm_mday > 31)
  449. return APR_DATE_BAD;
  450. if ((ds.tm_hour > 23) || (ds.tm_min > 59) || (ds.tm_sec > 61))
  451. return APR_DATE_BAD;
  452. mint = (monstr[0] << 16) | (monstr[1] << 8) | monstr[2];
  453. for (mon = 0; mon < 12; mon++)
  454. if (mint == months[mon])
  455. break;
  456. if (mon == 12)
  457. return APR_DATE_BAD;
  458. if ((ds.tm_mday == 31) && (mon == 3 || mon == 5 || mon == 8 || mon == 10))
  459. return APR_DATE_BAD;
  460. /* February gets special check for leapyear */
  461. if ((mon == 1) &&
  462. ((ds.tm_mday > 29)
  463. || ((ds.tm_mday == 29)
  464. && ((ds.tm_year & 3)
  465. || (((ds.tm_year % 100) == 0)
  466. && (((ds.tm_year % 400) != 100)))))))
  467. return APR_DATE_BAD;
  468. ds.tm_mon = mon;
  469. /* tm_gmtoff is the number of seconds off of GMT the time is.
  470. *
  471. * We only currently support: [+-]ZZZZ where Z is the offset in
  472. * hours from GMT.
  473. *
  474. * If there is any confusion, tm_gmtoff will remain 0.
  475. */
  476. ds.tm_gmtoff = 0;
  477. if (gmtstr && *gmtstr != '\0') {
  478. /* Do we have a GMT? */
  479. if (*(++gmtstr) != '\0') {
  480. int offset;
  481. switch (*(gmtstr++)) {
  482. case '-':
  483. offset = atoi(gmtstr);
  484. ds.tm_gmtoff -= (offset / 100) * 60 * 60;
  485. ds.tm_gmtoff -= (offset % 100) * 60;
  486. break;
  487. case '+':
  488. offset = atoi(gmtstr);
  489. ds.tm_gmtoff += (offset / 100) * 60 * 60;
  490. ds.tm_gmtoff += (offset % 100) * 60;
  491. break;
  492. }
  493. }
  494. }
  495. /* apr_time_exp_get uses tm_usec field, but it hasn't been set yet.
  496. * It should be safe to just zero out this value.
  497. * tm_usec is the number of microseconds into the second. HTTP only
  498. * cares about second granularity.
  499. */
  500. ds.tm_usec = 0;
  501. if (apr_time_exp_gmt_get(&result, &ds) != APR_SUCCESS)
  502. return APR_DATE_BAD;
  503. return result;
  504. }