partime.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742
  1. /* Parse a string, yielding a struct partime that describes it. */
  2. /* Copyright 1993, 1994, 1995, 1997 Paul Eggert
  3. Distributed under license by the Free Software Foundation, Inc.
  4. This file is part of RCS.
  5. RCS is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2, or (at your option)
  8. any later version.
  9. RCS is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with RCS; see the file COPYING.
  15. If not, write to the Free Software Foundation,
  16. 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17. Report problems and direct all questions to:
  18. rcs-bugs@cs.purdue.edu
  19. */
  20. #if has_conf_h
  21. # include <conf.h>
  22. #else
  23. # if HAVE_CONFIG_H
  24. # include <config.h>
  25. # else
  26. # ifndef __STDC__
  27. # define const
  28. # endif
  29. # endif
  30. # if HAVE_LIMITS_H
  31. # include <limits.h>
  32. # endif
  33. # ifndef LONG_MIN
  34. # define LONG_MIN (-1-2147483647L)
  35. # endif
  36. # if STDC_HEADERS
  37. # include <stdlib.h>
  38. # endif
  39. # include <time.h>
  40. # ifdef __STDC__
  41. # define P(x) x
  42. # else
  43. # define P(x) ()
  44. # endif
  45. #endif
  46. #include <ctype.h>
  47. #if STDC_HEADERS
  48. # define CTYPE_DOMAIN(c) 1
  49. #else
  50. # define CTYPE_DOMAIN(c) ((unsigned) (c) <= 0177)
  51. #endif
  52. #define ISALNUM(c) (CTYPE_DOMAIN (c) && isalnum (c))
  53. #define ISALPHA(c) (CTYPE_DOMAIN (c) && isalpha (c))
  54. #define ISSPACE(c) (CTYPE_DOMAIN (c) && isspace (c))
  55. #define ISUPPER(c) (CTYPE_DOMAIN (c) && isupper (c))
  56. #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
  57. #include <partime.h>
  58. char const partimeId[] =
  59. "$Id: partime.c,v 5.16 1997/05/19 06:33:53 eggert Exp $";
  60. /* Lookup tables for names of months, weekdays, time zones. */
  61. #define NAME_LENGTH_MAXIMUM 4
  62. struct name_val
  63. {
  64. char name[NAME_LENGTH_MAXIMUM];
  65. int val;
  66. };
  67. static char const *parse_decimal P ((char const *, int, int, int, int, int *, int *));
  68. static char const *parse_fixed P ((char const *, int, int *));
  69. static char const *parse_pattern_letter P ((char const *, int, struct partime *));
  70. static char const *parse_prefix P ((char const *, struct partime *, int *));
  71. static char const *parse_ranged P ((char const *, int, int, int, int *));
  72. static int lookup P ((char const *, struct name_val const[]));
  73. static int merge_partime P ((struct partime *, struct partime const *));
  74. static void undefine P ((struct partime *));
  75. static struct name_val const month_names[] =
  76. {
  77. {"jan", 0},
  78. {"feb", 1},
  79. {"mar", 2},
  80. {"apr", 3},
  81. {"may", 4},
  82. {"jun", 5},
  83. {"jul", 6},
  84. {"aug", 7},
  85. {"sep", 8},
  86. {"oct", 9},
  87. {"nov", 10},
  88. {"dec", 11},
  89. {"", TM_UNDEFINED}
  90. };
  91. static struct name_val const weekday_names[] =
  92. {
  93. {"sun", 0},
  94. {"mon", 1},
  95. {"tue", 2},
  96. {"wed", 3},
  97. {"thu", 4},
  98. {"fri", 5},
  99. {"sat", 6},
  100. {"", TM_UNDEFINED}
  101. };
  102. #define hr60nonnegative(t) ((t)/100 * 60 + (t)%100)
  103. #define hr60(t) ((t)<0 ? -hr60nonnegative(-(t)) : hr60nonnegative(t))
  104. #define zs(t,s) {s, hr60(t)}
  105. #define zd(t,s,d) zs(t, s), zs((t)+100, d)
  106. static struct name_val const zone_names[] =
  107. {
  108. zs (-1000, "hst"), /* Hawaii */
  109. zd (-1000, "hast", "hadt"), /* Hawaii-Aleutian */
  110. zd (- 900, "akst", "akdt"), /* Alaska */
  111. zd (- 800, "pst" , "pdt" ), /* Pacific */
  112. zd (- 700, "mst" , "mdt" ), /* Mountain */
  113. zd (- 600, "cst" , "cdt" ), /* Central */
  114. zd (- 500, "est" , "edt" ), /* Eastern */
  115. zd (- 400, "ast" , "adt" ), /* Atlantic */
  116. zd (- 330, "nst" , "ndt" ), /* Newfoundland */
  117. zs ( 000, "utc" ), /* Coordinated Universal */
  118. zs ( 000, "uct" ), /* " */
  119. zs ( 000, "cut" ), /* " */
  120. zs ( 000, "ut"), /* Universal */
  121. zs ( 000, "z"), /* Zulu (required by ISO 8601) */
  122. zd ( 000, "gmt" , "bst" ), /* Greenwich Mean, British Summer */
  123. zd ( 000, "wet" , "west"), /* Western European */
  124. zd ( 100, "cet" , "cest"), /* Central European */
  125. zd ( 100, "met" , "mest"), /* Middle European (bug in old tz versions) */
  126. zd ( 100, "mez" , "mesz"), /* Mittel-Europaeische Zeit */
  127. zd ( 200, "eet" , "eest"), /* Eastern European */
  128. zs ( 530, "ist" ), /* India */
  129. zd ( 900, "jst" , "jdt" ), /* Japan */
  130. zd ( 900, "kst" , "kdt" ), /* Korea */
  131. zd ( 1200, "nzst", "nzdt"), /* New Zealand */
  132. {"lt", 1},
  133. #if 0
  134. /* The following names are duplicates or are not well attested.
  135. There are lots more where these came from. */
  136. zs (-1100, "sst" ), /* Samoan */
  137. zd (- 900, "yst" , "ydt" ), /* Yukon - name is no longer used */
  138. zd (- 500, "ast" , "adt" ), /* Acre */
  139. zd (- 400, "wst" , "wdt" ), /* Western Brazil */
  140. zd (- 400, "cst" , "cdt" ), /* Chile */
  141. zd (- 200, "fst" , "fdt" ), /* Fernando de Noronha */
  142. zs ( 000, "wat" ), /* West African */
  143. zs ( 100, "cat" ), /* Central African */
  144. zs ( 200, "sat" ), /* South African */
  145. zd ( 200, "ist" , "idt" ), /* Israel */
  146. zs ( 300, "eat" ), /* East African */
  147. zd ( 300, "msk" , "msd" ), /* Moscow */
  148. zd ( 330, "ist" , "idt" ), /* Iran */
  149. zs ( 800, "hkt" ), /* Hong Kong */
  150. zs ( 800, "sgt" ), /* Singapore */
  151. zd ( 800, "cst" , "cdt" ), /* China */
  152. zd ( 800, "wst" , "wst" ), /* Western Australia */
  153. zd ( 930, "cst" , "cst" ), /* Central Australia */
  154. zs ( 1000, "gst" ), /* Guam */
  155. zd ( 1000, "est" , "est" ), /* Eastern Australia */
  156. #endif
  157. {"", -1}
  158. };
  159. /* Look for a prefix of S in TABLE, returning val for first matching entry. */
  160. static int
  161. lookup (s, table)
  162. char const *s;
  163. struct name_val const table[];
  164. {
  165. int j;
  166. char buf[NAME_LENGTH_MAXIMUM];
  167. for (j = 0; j < NAME_LENGTH_MAXIMUM; j++)
  168. {
  169. unsigned char c = *s++;
  170. if (! ISALPHA (c))
  171. {
  172. buf[j] = '\0';
  173. break;
  174. }
  175. buf[j] = ISUPPER (c) ? tolower (c) : c;
  176. }
  177. for (;; table++)
  178. for (j = 0; ; j++)
  179. if (j == NAME_LENGTH_MAXIMUM || ! table[0].name[j])
  180. return table[0].val;
  181. else if (buf[j] != table[0].name[j])
  182. break;
  183. }
  184. /* Set *T to ``undefined'' values. */
  185. static void
  186. undefine (t)
  187. struct partime *t;
  188. {
  189. t->tm.tm_sec = t->tm.tm_min = t->tm.tm_hour = t->tm.tm_mday = t->tm.tm_mon
  190. = t->tm.tm_year = t->tm.tm_wday = t->tm.tm_yday
  191. = t->ymodulus = t->yweek
  192. = TM_UNDEFINED;
  193. t->zone = TM_UNDEFINED_ZONE;
  194. }
  195. /* Array of patterns to look for in a date string.
  196. Order is important: we look for the first matching pattern
  197. whose values do not contradict values that we already know about.
  198. See `parse_pattern_letter' below for the meaning of the pattern codes. */
  199. static char const *const patterns[] =
  200. {
  201. /* These traditional patterns must come first,
  202. to prevent an ISO 8601 format from misinterpreting their prefixes. */
  203. "E_n_y", "x", /* RFC 822 */
  204. "E_n", "n_E", "n", "t:m:s_A", "t:m_A", "t_A", /* traditional */
  205. "y/N/D$", /* traditional RCS */
  206. /* ISO 8601:1988 formats, generalized a bit. */
  207. "y-N-D$", "4ND$", "Y-N$",
  208. "RND$", "-R=N$", "-R$", "--N=D$", "N=DT",
  209. "--N$", "---D$", "DT",
  210. "Y-d$", "4d$", "R=d$", "-d$", "dT",
  211. "y-W-X", "yWX", "y=W",
  212. "-r-W-X", "r-W-XT", "-rWX", "rWXT", "-W=X", "W=XT", "-W",
  213. "-w-X", "w-XT", "---X$", "XT", "4$",
  214. "T",
  215. "h:m:s$", "hms$", "h:m$", "hm$", "h$", "-m:s$", "-ms$", "-m$", "--s$",
  216. "Y", "Z",
  217. 0
  218. };
  219. /* Parse an initial prefix of STR, setting *T accordingly.
  220. Return the first character after the prefix, or 0 if it couldn't be parsed.
  221. Start with pattern *PI; if success, set *PI to the next pattern to try.
  222. Set *PI to -1 if we know there are no more patterns to try;
  223. if *PI is initially negative, give up immediately. */
  224. static char const *
  225. parse_prefix (str, t, pi)
  226. char const *str;
  227. struct partime *t;
  228. int *pi;
  229. {
  230. int i = *pi;
  231. char const *pat;
  232. unsigned char c;
  233. if (i < 0)
  234. return 0;
  235. /* Remove initial noise. */
  236. while (! ISALNUM (c = *str) && c != '-' && c != '+')
  237. {
  238. if (! c)
  239. {
  240. undefine (t);
  241. *pi = -1;
  242. return str;
  243. }
  244. str++;
  245. }
  246. /* Try a pattern until one succeeds. */
  247. while ((pat = patterns[i++]) != 0)
  248. {
  249. char const *s = str;
  250. undefine (t);
  251. do
  252. {
  253. if (! (c = *pat++))
  254. {
  255. *pi = i;
  256. return s;
  257. }
  258. }
  259. while ((s = parse_pattern_letter (s, c, t)) != 0);
  260. }
  261. return 0;
  262. }
  263. /* Parse an initial prefix of S of length DIGITS; it must be a number.
  264. Store the parsed number into *RES.
  265. Return the first character after the prefix, or 0 if it wasn't parsed. */
  266. static char const *
  267. parse_fixed (s, digits, res)
  268. char const *s;
  269. int digits, *res;
  270. {
  271. int n = 0;
  272. char const *lim = s + digits;
  273. while (s < lim)
  274. {
  275. unsigned d = *s++ - '0';
  276. if (9 < d)
  277. return 0;
  278. n = 10 * n + d;
  279. }
  280. *res = n;
  281. return s;
  282. }
  283. /* Parse an initial prefix of S of length DIGITS;
  284. it must be a number in the range LO through HI.
  285. Store the parsed number into *RES.
  286. Return the first character after the prefix, or 0 if it wasn't parsed. */
  287. static char const *
  288. parse_ranged (s, digits, lo, hi, res)
  289. char const *s;
  290. int digits, lo, hi, *res;
  291. {
  292. s = parse_fixed (s, digits, res);
  293. return s && lo <= *res && *res <= hi ? s : 0;
  294. }
  295. /* Parse an initial prefix of S of length DIGITS;
  296. it must be a number in the range LO through HI
  297. and it may be followed by a fraction to be computed using RESOLUTION.
  298. Store the parsed number into *RES; store the fraction times RESOLUTION,
  299. rounded to the nearest integer, into *FRES.
  300. Return the first character after the prefix, or 0 if it wasn't parsed. */
  301. static char const *
  302. parse_decimal (s, digits, lo, hi, resolution, res, fres)
  303. char const *s;
  304. int digits, lo, hi, resolution, *res, *fres;
  305. {
  306. s = parse_fixed (s, digits, res);
  307. if (s && lo <= *res && *res <= hi)
  308. {
  309. int f = 0;
  310. if ((s[0] == ',' || s[0] == '.') && ISDIGIT (s[1]))
  311. {
  312. char const *s1 = ++s;
  313. int num10 = 0, denom10 = 10, product;
  314. while (ISDIGIT (*++s))
  315. {
  316. int d = denom10 * 10;
  317. if (d / 10 != denom10)
  318. return 0; /* overflow */
  319. denom10 = d;
  320. }
  321. s = parse_fixed (s1, (int) (s - s1), &num10);
  322. product = num10 * resolution;
  323. f = (product + (denom10 >> 1)) / denom10;
  324. f -= f & (product % denom10 == denom10 >> 1); /* round to even */
  325. if (f < 0 || product/resolution != num10)
  326. return 0; /* overflow */
  327. }
  328. *fres = f;
  329. return s;
  330. }
  331. return 0;
  332. }
  333. /* Parse an initial prefix of S; it must denote a time zone.
  334. Set *ZONE to the number of seconds east of GMT,
  335. or to TM_LOCAL_ZONE if it is the local time zone.
  336. Return the first character after the prefix, or 0 if it wasn't parsed. */
  337. char *
  338. parzone (s, zone)
  339. char const *s;
  340. long *zone;
  341. {
  342. char sign;
  343. int hh, mm, ss;
  344. int minutesEastOfUTC;
  345. long offset, z;
  346. /* The formats are LT, n, n DST, nDST, no, o
  347. where n is a time zone name
  348. and o is a time zone offset of the form [-+]hh[:mm[:ss]]. */
  349. switch (*s)
  350. {
  351. case '-':
  352. case '+':
  353. z = 0;
  354. break;
  355. default:
  356. minutesEastOfUTC = lookup (s, zone_names);
  357. if (minutesEastOfUTC == -1)
  358. return 0;
  359. /* Don't bother to check rest of spelling. */
  360. while (ISALPHA ((unsigned char) *s))
  361. s++;
  362. /* Don't modify LT. */
  363. if (minutesEastOfUTC == 1)
  364. {
  365. *zone = TM_LOCAL_ZONE;
  366. return (char *) s;
  367. }
  368. z = minutesEastOfUTC * 60L;
  369. /* Look for trailing " DST". */
  370. if ((s[-1] == 'T' || s[-1] == 't')
  371. && (s[-2] == 'S' || s[-2] == 's')
  372. && (s[-3] == 'D' || s[-3] == 'd'))
  373. goto trailing_dst;
  374. while (ISSPACE ((unsigned char) *s))
  375. s++;
  376. if ((s[0] == 'D' || s[0] == 'd')
  377. && (s[1] == 'S' || s[1] == 's')
  378. && (s[2] == 'T' || s[2] == 't'))
  379. {
  380. s += 3;
  381. trailing_dst:
  382. *zone = z + 60*60;
  383. return (char *) s;
  384. }
  385. switch (*s)
  386. {
  387. case '-':
  388. case '+':
  389. break;
  390. default:
  391. *zone = z;
  392. return (char *) s;
  393. }
  394. break;
  395. }
  396. sign = *s++;
  397. if (! (s = parse_ranged (s, 2, 0, 23, &hh)))
  398. return 0;
  399. mm = ss = 0;
  400. if (*s == ':')
  401. s++;
  402. if (ISDIGIT (*s))
  403. {
  404. if (! (s = parse_ranged (s, 2, 0, 59, &mm)))
  405. return 0;
  406. if (*s == ':' && s[-3] == ':' && ISDIGIT (s[1])
  407. && ! (s = parse_ranged (s + 1, 2, 0, 59, &ss)))
  408. return 0;
  409. }
  410. if (ISDIGIT (*s))
  411. return 0;
  412. offset = (hh * 60 + mm) * 60L + ss;
  413. *zone = z + (sign == '-' ? -offset : offset);
  414. /* ?? Are fractions allowed here? If so, they're not implemented. */
  415. return (char *) s;
  416. }
  417. /* Parse an initial prefix of S, matching the pattern whose code is C.
  418. Set *T accordingly.
  419. Return the first character after the prefix, or 0 if it wasn't parsed. */
  420. static char const *
  421. parse_pattern_letter (s, c, t)
  422. char const *s;
  423. int c;
  424. struct partime *t;
  425. {
  426. switch (c)
  427. {
  428. case '$': /* The next character must be a non-digit. */
  429. if (ISDIGIT (*s))
  430. return 0;
  431. break;
  432. case '-':
  433. case '/':
  434. case ':':
  435. /* These characters stand for themselves. */
  436. if (*s++ != c)
  437. return 0;
  438. break;
  439. case '4': /* 4-digit year */
  440. s = parse_fixed (s, 4, &t->tm.tm_year);
  441. break;
  442. case '=': /* optional '-' */
  443. s += *s == '-';
  444. break;
  445. case 'A': /* AM or PM */
  446. /* This matches the regular expression [AaPp][Mm]?.
  447. It must not be followed by a letter or digit;
  448. otherwise it would match prefixes of strings like "PST". */
  449. switch (*s++)
  450. {
  451. case 'A':
  452. case 'a':
  453. if (t->tm.tm_hour == 12)
  454. t->tm.tm_hour = 0;
  455. break;
  456. case 'P':
  457. case 'p':
  458. if (t->tm.tm_hour != 12)
  459. t->tm.tm_hour += 12;
  460. break;
  461. default:
  462. return 0;
  463. }
  464. switch (*s)
  465. {
  466. case 'M':
  467. case 'm':
  468. s++;
  469. break;
  470. }
  471. if (ISALNUM ((unsigned char) *s))
  472. return 0;
  473. break;
  474. case 'D': /* day of month [01-31] */
  475. s = parse_ranged (s, 2, 1, 31, &t->tm.tm_mday);
  476. break;
  477. case 'd': /* day of year [001-366] */
  478. s = parse_ranged (s, 3, 1, 366, &t->tm.tm_yday);
  479. t->tm.tm_yday--;
  480. break;
  481. case 'E': /* extended day of month [1-9, 01-31] */
  482. s = parse_ranged (s, (ISDIGIT (s[0]) && ISDIGIT (s[1])) + 1, 1, 31,
  483. &t->tm.tm_mday);
  484. break;
  485. case 'h': /* hour [00-23 followed by optional fraction] */
  486. {
  487. int frac;
  488. s = parse_decimal (s, 2, 0, 23, 60 * 60, &t->tm.tm_hour, &frac);
  489. t->tm.tm_min = frac / 60;
  490. t->tm.tm_sec = frac % 60;
  491. }
  492. break;
  493. case 'm': /* minute [00-59 followed by optional fraction] */
  494. s = parse_decimal (s, 2, 0, 59, 60, &t->tm.tm_min, &t->tm.tm_sec);
  495. break;
  496. case 'n': /* month name [e.g. "Jan"] */
  497. if (! TM_DEFINED (t->tm.tm_mon = lookup (s, month_names)))
  498. return 0;
  499. /* Don't bother to check rest of spelling. */
  500. while (ISALPHA ((unsigned char) *s))
  501. s++;
  502. break;
  503. case 'N': /* month [01-12] */
  504. s = parse_ranged (s, 2, 1, 12, &t->tm.tm_mon);
  505. t->tm.tm_mon--;
  506. break;
  507. case 'r': /* year % 10 (remainder in origin-0 decade) [0-9] */
  508. s = parse_fixed (s, 1, &t->tm.tm_year);
  509. t->ymodulus = 10;
  510. break;
  511. case_R:
  512. case 'R': /* year % 100 (remainder in origin-0 century) [00-99] */
  513. s = parse_fixed (s, 2, &t->tm.tm_year);
  514. t->ymodulus = 100;
  515. break;
  516. case 's': /* second [00-60 followed by optional fraction] */
  517. {
  518. int frac;
  519. s = parse_decimal (s, 2, 0, 60, 1, &t->tm.tm_sec, &frac);
  520. t->tm.tm_sec += frac;
  521. }
  522. break;
  523. case 'T': /* 'T' or 't' */
  524. switch (*s++)
  525. {
  526. case 'T':
  527. case 't':
  528. break;
  529. default:
  530. return 0;
  531. }
  532. break;
  533. case 't': /* traditional hour [1-9 or 01-12] */
  534. s = parse_ranged (s, (ISDIGIT (s[0]) && ISDIGIT (s[1])) + 1, 1, 12,
  535. &t->tm.tm_hour);
  536. break;
  537. case 'w': /* 'W' or 'w' only (stands for current week) */
  538. switch (*s++)
  539. {
  540. case 'W':
  541. case 'w':
  542. break;
  543. default:
  544. return 0;
  545. }
  546. break;
  547. case 'W': /* 'W' or 'w', followed by a week of year [00-53] */
  548. switch (*s++)
  549. {
  550. case 'W':
  551. case 'w':
  552. break;
  553. default:
  554. return 0;
  555. }
  556. s = parse_ranged (s, 2, 0, 53, &t->yweek);
  557. break;
  558. case 'X': /* weekday (1=Mon ... 7=Sun) [1-7] */
  559. s = parse_ranged (s, 1, 1, 7, &t->tm.tm_wday);
  560. t->tm.tm_wday--;
  561. break;
  562. case 'x': /* weekday name [e.g. "Sun"] */
  563. if (! TM_DEFINED (t->tm.tm_wday = lookup (s, weekday_names)))
  564. return 0;
  565. /* Don't bother to check rest of spelling. */
  566. while (ISALPHA ((unsigned char) *s))
  567. s++;
  568. break;
  569. case 'y': /* either R or Y */
  570. if (ISDIGIT (s[0]) && ISDIGIT (s[1]) && ! ISDIGIT (s[2]))
  571. goto case_R;
  572. /* fall into */
  573. case 'Y': /* year in full [4 or more digits] */
  574. {
  575. int len = 0;
  576. while (ISDIGIT (s[len]))
  577. len++;
  578. if (len < 4)
  579. return 0;
  580. s = parse_fixed (s, len, &t->tm.tm_year);
  581. }
  582. break;
  583. case 'Z': /* time zone */
  584. s = parzone (s, &t->zone);
  585. break;
  586. case '_': /* possibly empty sequence of non-alphanumerics */
  587. while (! ISALNUM ((unsigned char) *s) && *s)
  588. s++;
  589. break;
  590. default: /* bad pattern */
  591. return 0;
  592. }
  593. return s;
  594. }
  595. /* If there is no conflict, merge into *T the additional information in *U
  596. and return 0. Otherwise do nothing and return -1. */
  597. static int
  598. merge_partime (t, u)
  599. struct partime *t;
  600. struct partime const *u;
  601. {
  602. # define conflict(a,b) ((a) != (b) && TM_DEFINED (a) && TM_DEFINED (b))
  603. if (conflict (t->tm.tm_sec, u->tm.tm_sec)
  604. || conflict (t->tm.tm_min, u->tm.tm_min)
  605. || conflict (t->tm.tm_hour, u->tm.tm_hour)
  606. || conflict (t->tm.tm_mday, u->tm.tm_mday)
  607. || conflict (t->tm.tm_mon, u->tm.tm_mon)
  608. || conflict (t->tm.tm_year, u->tm.tm_year)
  609. || conflict (t->tm.tm_wday, u->tm.tm_yday)
  610. || conflict (t->ymodulus, u->ymodulus)
  611. || conflict (t->yweek, u->yweek)
  612. || (t->zone != u->zone
  613. && t->zone != TM_UNDEFINED_ZONE
  614. && u->zone != TM_UNDEFINED_ZONE))
  615. return -1;
  616. # undef conflict
  617. # define merge_(a,b) if (TM_DEFINED (b)) (a) = (b);
  618. merge_ (t->tm.tm_sec, u->tm.tm_sec)
  619. merge_ (t->tm.tm_min, u->tm.tm_min)
  620. merge_ (t->tm.tm_hour, u->tm.tm_hour)
  621. merge_ (t->tm.tm_mday, u->tm.tm_mday)
  622. merge_ (t->tm.tm_mon, u->tm.tm_mon)
  623. merge_ (t->tm.tm_year, u->tm.tm_year)
  624. merge_ (t->tm.tm_wday, u->tm.tm_yday)
  625. merge_ (t->ymodulus, u->ymodulus)
  626. merge_ (t->yweek, u->yweek)
  627. # undef merge_
  628. if (u->zone != TM_UNDEFINED_ZONE)
  629. t->zone = u->zone;
  630. return 0;
  631. }
  632. /* Parse a date/time prefix of S, putting the parsed result into *T.
  633. Return the first character after the prefix.
  634. The prefix may contain no useful information;
  635. in that case, *T will contain only undefined values. */
  636. char *
  637. partime (s, t)
  638. char const *s;
  639. struct partime *t;
  640. {
  641. struct partime p;
  642. undefine (t);
  643. while (*s)
  644. {
  645. int i = 0;
  646. char const *s1;
  647. do
  648. {
  649. if (! (s1 = parse_prefix (s, &p, &i)))
  650. return (char *) s;
  651. }
  652. while (merge_partime (t, &p) != 0);
  653. s = s1;
  654. }
  655. return (char *) s;
  656. }