1 /* Parse a time duration and return a seconds count
2 Copyright (C) 2008 Free Software Foundation, Inc.
3 Written by Bruce Korb <bkorb@gnu.org>, 2008.
5 This program 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 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
28 #include "parse-duration.h"
38 #define cch_t char const
51 #define SEC_PER_MIN 60
52 #define SEC_PER_HR (SEC_PER_MIN * 60)
53 #define SEC_PER_DAY (SEC_PER_HR * 24)
54 #define SEC_PER_WEEK (SEC_PER_DAY * 7)
55 #define SEC_PER_MONTH (SEC_PER_DAY * 30)
56 #define SEC_PER_YEAR (SEC_PER_DAY * 365)
58 #define TIME_MAX 0x7FFFFFFF
60 static unsigned long inline
61 str_const_to_ul (cch_t * str, cch_t ** ppz, int base)
63 return strtoul (str, (char **)ppz, base);
67 str_const_to_l (cch_t * str, cch_t ** ppz, int base)
69 return strtol (str, (char **)ppz, base);
73 scale_n_add (time_t base, time_t val, int scale)
82 if (val > TIME_MAX / scale)
89 if (base > TIME_MAX - val)
99 parse_hr_min_sec (time_t start, cch_t * pz)
105 /* For as long as our scanner pointer points to a colon *AND*
106 we've not looped before, then keep looping. (two iterations max) */
107 while ((*pz == ':') && (lpct++ <= 1))
109 unsigned long v = str_const_to_ul (pz+1, &pz, 10);
114 start = scale_n_add (v, start, 60);
120 /* allow for trailing spaces */
121 while (isspace ((unsigned char)*pz)) pz++;
132 parse_scaled_value (time_t base, cch_t ** ppz, cch_t * endp, int scale)
137 if (base == BAD_TIME)
141 val = str_const_to_ul (pz, &pz, 10);
144 while (isspace ((unsigned char)*pz)) pz++;
152 return scale_n_add (base, val, scale);
156 parse_year_month_day (cch_t * pz, cch_t * ps)
160 res = parse_scaled_value (0, &pz, ps, SEC_PER_YEAR);
162 ps = strchr (++pz, '-');
168 res = parse_scaled_value (res, &pz, ps, SEC_PER_MONTH);
171 ps = pz + strlen (pz);
172 return parse_scaled_value (res, &pz, ps, SEC_PER_DAY);
176 parse_yearmonthday (cch_t * in_pz)
182 if (strlen (in_pz) != 8)
188 memcpy (buf, in_pz, 4);
191 res = parse_scaled_value (0, &pz, buf + 4, SEC_PER_YEAR);
193 memcpy (buf, in_pz + 4, 2);
196 res = parse_scaled_value (res, &pz, buf + 2, SEC_PER_MONTH);
198 memcpy (buf, in_pz + 6, 2);
201 return parse_scaled_value (res, &pz, buf + 2, SEC_PER_DAY);
205 parse_YMWD (cch_t * pz)
208 cch_t * ps = strchr (pz, 'Y');
211 res = parse_scaled_value (0, &pz, ps, SEC_PER_YEAR);
215 ps = strchr (pz, 'M');
218 res = parse_scaled_value (res, &pz, ps, SEC_PER_MONTH);
222 ps = strchr (pz, 'W');
225 res = parse_scaled_value (res, &pz, ps, SEC_PER_WEEK);
229 ps = strchr (pz, 'D');
232 res = parse_scaled_value (res, &pz, ps, SEC_PER_DAY);
236 while (isspace ((unsigned char)*pz)) pz++;
247 parse_hour_minute_second (cch_t * pz, cch_t * ps)
251 res = parse_scaled_value (0, &pz, ps, SEC_PER_HR);
253 ps = strchr (++pz, ':');
260 res = parse_scaled_value (res, &pz, ps, SEC_PER_MIN);
263 ps = pz + strlen (pz);
264 return parse_scaled_value (res, &pz, ps, 1);
268 parse_hourminutesecond (cch_t * in_pz)
274 if (strlen (in_pz) != 6)
280 memcpy (buf, in_pz, 2);
283 res = parse_scaled_value (0, &pz, buf + 2, SEC_PER_HR);
285 memcpy (buf, in_pz + 2, 2);
288 res = parse_scaled_value (res, &pz, buf + 2, SEC_PER_MIN);
290 memcpy (buf, in_pz + 4, 2);
293 return parse_scaled_value (res, &pz, buf + 2, 1);
297 parse_HMS (cch_t * pz)
300 cch_t * ps = strchr (pz, 'H');
303 res = parse_scaled_value (0, &pz, ps, SEC_PER_HR);
307 ps = strchr (pz, 'M');
310 res = parse_scaled_value (res, &pz, ps, SEC_PER_MIN);
314 ps = strchr (pz, 'S');
317 res = parse_scaled_value (res, &pz, ps, 1);
321 while (isspace ((unsigned char)*pz)) pz++;
332 parse_time (cch_t * pz)
340 ps = strchr (pz, ':');
343 res = parse_hour_minute_second (pz, ps);
347 * Try for a 'H', 'M' or 'S' suffix
349 else if (ps = strpbrk (pz, "HMS"),
352 /* Its a YYYYMMDD format: */
353 res = parse_hourminutesecond (pz);
357 res = parse_HMS (pz);
365 /* trim leading white space */
366 while (isspace ((unsigned char)*pz)) pz++;
368 /* trim trailing white space */
370 char * pe = pz + strlen (pz);
371 while ((pe > pz) && isspace ((unsigned char)pe[-1])) pe--;
379 * Parse the year/months/days of a time period
382 parse_period (cch_t * in_pz)
384 char * pz = xstrdup (in_pz);
385 char * pT = strchr (pz, 'T');
400 ps = strchr (pz, '-');
403 res = parse_year_month_day (pz, ps);
407 * Try for a 'Y', 'M' or 'D' suffix
409 else if (ps = strpbrk (pz, "YMWD"),
412 /* Its a YYYYMMDD format: */
413 res = parse_yearmonthday (pz);
417 res = parse_YMWD (pz);
419 if ((errno == 0) && (pT != NULL))
421 time_t val = parse_time (pT);
422 res = scale_n_add (res, val, 1);
430 parse_non_iso8601(cch_t * pz)
432 whats_done_t whatd_we_do = NOTHING_IS_DONE;
440 val = str_const_to_l (pz, &pz, 10);
444 /* IF we find a colon, then we're going to have a seconds value.
445 We will not loop here any more. We cannot already have parsed
446 a minute value and if we've parsed an hour value, then the result
447 value has to be less than an hour. */
450 if (whatd_we_do >= MINUTE_IS_DONE)
453 val = parse_hr_min_sec (val, pz);
455 if ((whatd_we_do == HOUR_IS_DONE) && (val >= SEC_PER_HR))
458 return scale_n_add (res, val, 1);
464 /* Skip over white space following the number we just parsed. */
465 while (isspace ((unsigned char)*pz)) pz++;
469 default: goto bad_time;
471 return scale_n_add (res, val, 1);
474 if (whatd_we_do >= YEAR_IS_DONE)
477 whatd_we_do = YEAR_IS_DONE;
481 if (whatd_we_do >= MONTH_IS_DONE)
483 mult = SEC_PER_MONTH;
484 whatd_we_do = MONTH_IS_DONE;
488 if (whatd_we_do >= WEEK_IS_DONE)
491 whatd_we_do = WEEK_IS_DONE;
495 if (whatd_we_do >= DAY_IS_DONE)
498 whatd_we_do = DAY_IS_DONE;
502 if (whatd_we_do >= HOUR_IS_DONE)
505 whatd_we_do = HOUR_IS_DONE;
509 if (whatd_we_do >= MINUTE_IS_DONE)
512 whatd_we_do = MINUTE_IS_DONE;
517 whatd_we_do = SECOND_IS_DONE;
521 res = scale_n_add (res, val, mult);
523 while (isspace ((unsigned char)*++pz)) ;
527 if (! isdigit ((unsigned char)*pz))
531 } while (whatd_we_do < SECOND_IS_DONE);
539 parse_duration (char const * pz)
543 while (isspace ((unsigned char)*pz)) pz++;
548 res = parse_period (pz + 1);
549 if ((errno != 0) || (res == BAD_TIME))
556 res = parse_time (pz + 1);
557 if ((errno != 0) || (res == BAD_TIME))
562 if (! isdigit ((unsigned char)*pz))
565 res = parse_non_iso8601 (pz);
566 if ((errno == 0) && (res != BAD_TIME))
571 fprintf (stderr, _("Invalid time duration: %s\n"), pz);
580 * c-file-style: "gnu"
581 * indent-tabs-mode: nil
583 * end of parse-duration.c */