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