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