]> git.notmuchmail.org Git - notmuch/blob - parse-time-string/parse-time-string.c
perf-test: add argument parsing for performance tests
[notmuch] / parse-time-string / parse-time-string.c
1 /*
2  * parse time string - user friendly date and time parser
3  * Copyright © 2012 Jani Nikula
4  *
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 2 of the License, or
8  * (at your option) any later version.
9  *
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.
14  *
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/>.
17  *
18  * Author: Jani Nikula <jani@nikula.org>
19  */
20
21 #include <assert.h>
22 #include <ctype.h>
23 #include <errno.h>
24 #include <limits.h>
25 #include <stdio.h>
26 #include <stdarg.h>
27 #include <stdbool.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <strings.h>
31 #include <time.h>
32 #include <sys/time.h>
33 #include <sys/types.h>
34
35 #include "parse-time-string.h"
36
37 /*
38  * IMPLEMENTATION DETAILS
39  *
40  * At a high level, the parsing is done in two phases: 1) actual
41  * parsing of the input string and storing the parsed data into
42  * 'struct state', and 2) processing of the data in 'struct state'
43  * according to current time (or provided reference time) and
44  * rounding. This is evident in the main entry point function
45  * parse_time_string().
46  *
47  * 1) The parsing phase - parse_input()
48  *
49  * Parsing is greedy and happens from left to right. The parsing is as
50  * unambiguous as possible; only unambiguous date/time formats are
51  * accepted. Redundant or contradictory absolute date/time in the
52  * input (e.g. date specified multiple times/ways) is not
53  * accepted. Relative date/time on the other hand just accumulates if
54  * present multiple times (e.g. "5 days 5 days" just turns into 10
55  * days).
56  *
57  * Parsing decisions are made on the input format, not value. For
58  * example, "20/5/2005" fails because the recognized format here is
59  * MM/D/YYYY, even though the values would suggest DD/M/YYYY.
60  *
61  * Parsing is mostly stateless in the sense that parsing decisions are
62  * not made based on the values of previously parsed data, or whether
63  * certain data is present in the first place. (There are a few
64  * exceptions to the latter part, though, such as parsing of time zone
65  * that would otherwise look like plain time.)
66  *
67  * When the parser encounters a number that is not greedily parsed as
68  * part of a format, the interpretation is postponed until the next
69  * token is parsed. The parser for the next token may consume the
70  * previously postponed number. For example, when parsing "20 May" the
71  * meaning of "20" is not known until "May" is parsed. If the parser
72  * for the next token does not consume the postponed number, the
73  * number is handled as a "lone" number before parser for the next
74  * token finishes.
75  *
76  * 2) The processing phase - create_output()
77  *
78  * Once the parser in phase 1 has finished, 'struct state' contains
79  * all the information from the input string, and it's no longer
80  * needed. Since the parser does not even handle the concept of "now",
81  * the processing initializes the fields referring to the current
82  * date/time.
83  *
84  * If requested, the result is rounded towards past or future. The
85  * idea behind rounding is to support parsing date/time ranges in an
86  * obvious way. For example, for a range defined as two dates (without
87  * time), one would typically want to have an inclusive range from the
88  * beginning of start date to the end of the end date. The caller
89  * would use rounding towards past in the start date, and towards
90  * future in the end date.
91  *
92  * The absolute date and time is shifted by the relative date and
93  * time, and time zone adjustments are made. Daylight saving time
94  * (DST) is specifically *not* handled at all.
95  *
96  * Finally, the result is stored to time_t.
97  */
98
99 #define unused(x) x __attribute__ ((unused))
100
101 /* XXX: Redefine these to add i18n support. The keyword table uses
102  * N_() to mark strings to be translated; they are accessed
103  * dynamically using _(). */
104 #define _(s) (s)        /* i18n: define as gettext (s) */
105 #define N_(s) (s)       /* i18n: define as gettext_noop (s) */
106
107 #define ARRAY_SIZE(a) (sizeof (a) / sizeof (a[0]))
108
109 /*
110  * Field indices in the tm and set arrays of struct state.
111  *
112  * NOTE: There's some code that depends on the ordering of this enum.
113  */
114 enum field {
115     /* Keep SEC...YEAR in this order. */
116     TM_ABS_SEC,         /* seconds */
117     TM_ABS_MIN,         /* minutes */
118     TM_ABS_HOUR,        /* hours */
119     TM_ABS_MDAY,        /* day of the month */
120     TM_ABS_MON,         /* month */
121     TM_ABS_YEAR,        /* year */
122
123     TM_WDAY,            /* day of the week. special: may be relative */
124     TM_ABS_ISDST,       /* daylight saving time */
125
126     TM_AMPM,            /* am vs. pm */
127     TM_TZ,              /* timezone in minutes */
128
129     /* Keep SEC...YEAR in this order. */
130     TM_REL_SEC,         /* seconds relative to absolute or reference time */
131     TM_REL_MIN,         /* minutes ... */
132     TM_REL_HOUR,        /* hours ... */
133     TM_REL_DAY,         /* days ... */
134     TM_REL_MON,         /* months ... */
135     TM_REL_YEAR,        /* years ... */
136     TM_REL_WEEK,        /* weeks ... */
137
138     TM_NONE,            /* not a field */
139
140     TM_SIZE = TM_NONE,
141     TM_FIRST_ABS = TM_ABS_SEC,
142     TM_FIRST_REL = TM_REL_SEC,
143 };
144
145 /* Values for the set array of struct state. */
146 enum field_set {
147     FIELD_UNSET,        /* The field has not been touched by parser. */
148     FIELD_SET,          /* The field has been set by parser. */
149     FIELD_NOW,          /* The field will be set to reference time. */
150 };
151
152 static enum field
153 next_abs_field (enum field field)
154 {
155     /* NOTE: Depends on the enum ordering. */
156     return field < TM_ABS_YEAR ? field + 1 : TM_NONE;
157 }
158
159 static enum field
160 abs_to_rel_field (enum field field)
161 {
162     assert (field <= TM_ABS_YEAR);
163
164     /* NOTE: Depends on the enum ordering. */
165     return field + (TM_FIRST_REL - TM_FIRST_ABS);
166 }
167
168 /* Get the smallest acceptable value for field. */
169 static int
170 get_field_epoch_value (enum field field)
171 {
172     if (field == TM_ABS_MDAY || field == TM_ABS_MON)
173         return 1;
174     else if (field == TM_ABS_YEAR)
175         return 1970;
176     else
177         return 0;
178 }
179
180 /* The parsing state. */
181 struct state {
182     int tm[TM_SIZE];                    /* parsed date and time */
183     enum field_set set[TM_SIZE];        /* set status of tm */
184
185     enum field last_field;      /* Previously set field. */
186     char delim;
187
188     int postponed_length;       /* Number of digits in postponed value. */
189     int postponed_value;
190     char postponed_delim;       /* The delimiter preceding postponed number. */
191 };
192
193 /*
194  * Helpers for postponed numbers.
195  *
196  * postponed_length is the number of digits in postponed value. 0
197  * means there is no postponed number. -1 means there is a postponed
198  * number, but it comes from a keyword, and it doesn't have digits.
199  */
200 static int
201 get_postponed_length (struct state *state)
202 {
203     return state->postponed_length;
204 }
205
206 /*
207  * Consume a previously postponed number. Return true if a number was
208  * in fact postponed, false otherwise. Store the postponed number's
209  * value in *v, length in the input string in *n (or -1 if the number
210  * was written out and parsed as a keyword), and the preceding
211  * delimiter to *d. If a number was not postponed, *v, *n and *d are
212  * unchanged.
213  */
214 static bool
215 consume_postponed_number (struct state *state, int *v, int *n, char *d)
216 {
217     if (!state->postponed_length)
218         return false;
219
220     if (n)
221         *n = state->postponed_length;
222
223     if (v)
224         *v = state->postponed_value;
225
226     if (d)
227         *d = state->postponed_delim;
228
229     state->postponed_length = 0;
230     state->postponed_value = 0;
231     state->postponed_delim = 0;
232
233     return true;
234 }
235
236 static int parse_postponed_number (struct state *state, enum field next_field);
237
238 /*
239  * Postpone a number to be handled later. If one exists already,
240  * handle it first. n may be -1 to indicate a keyword that has no
241  * number length.
242  */
243 static int
244 set_postponed_number (struct state *state, int v, int n)
245 {
246     int r;
247     char d = state->delim;
248
249     /* Parse a previously postponed number, if any. */
250     r = parse_postponed_number (state, TM_NONE);
251     if (r)
252         return r;
253
254     state->postponed_length = n;
255     state->postponed_value = v;
256     state->postponed_delim = d;
257
258     return 0;
259 }
260
261 static void
262 set_delim (struct state *state, char delim)
263 {
264     state->delim = delim;
265 }
266
267 static void
268 unset_delim (struct state *state)
269 {
270     state->delim = 0;
271 }
272
273 /*
274  * Field set/get/mod helpers.
275  */
276
277 /* Return true if field has been set. */
278 static bool
279 is_field_set (struct state *state, enum field field)
280 {
281     assert (field < ARRAY_SIZE (state->tm));
282
283     return state->set[field] != FIELD_UNSET;
284 }
285
286 static void
287 unset_field (struct state *state, enum field field)
288 {
289     assert (field < ARRAY_SIZE (state->tm));
290
291     state->set[field] = FIELD_UNSET;
292     state->tm[field] = 0;
293 }
294
295 /*
296  * Set field to value. A field can only be set once to ensure the
297  * input does not contain redundant and potentially conflicting data.
298  */
299 static int
300 set_field (struct state *state, enum field field, int value)
301 {
302     int r;
303
304     /* Fields can only be set once. */
305     if (is_field_set (state, field))
306         return -PARSE_TIME_ERR_ALREADYSET;
307
308     state->set[field] = FIELD_SET;
309
310     /* Parse a previously postponed number, if any. */
311     r = parse_postponed_number (state, field);
312     if (r)
313         return r;
314
315     unset_delim (state);
316
317     state->tm[field] = value;
318     state->last_field = field;
319
320     return 0;
321 }
322
323 /*
324  * Mark n fields in fields to be set to the reference date/time in the
325  * specified time zone, or local timezone if not specified. The fields
326  * will be initialized after parsing is complete and timezone is
327  * known.
328  */
329 static int
330 set_fields_to_now (struct state *state, enum field *fields, size_t n)
331 {
332     size_t i;
333     int r;
334
335     for (i = 0; i < n; i++) {
336         r = set_field (state, fields[i], 0);
337         if (r)
338             return r;
339         state->set[fields[i]] = FIELD_NOW;
340     }
341
342     return 0;
343 }
344
345 /* Modify field by adding value to it. To be used on relative fields,
346  * which can be modified multiple times (to accumulate). */
347 static int
348 add_to_field (struct state *state, enum field field, int value)
349 {
350     int r;
351
352     assert (field < ARRAY_SIZE (state->tm));
353
354     state->set[field] = FIELD_SET;
355
356     /* Parse a previously postponed number, if any. */
357     r = parse_postponed_number (state, field);
358     if (r)
359         return r;
360
361     unset_delim (state);
362
363     state->tm[field] += value;
364     state->last_field = field;
365
366     return 0;
367 }
368
369 /*
370  * Get field value. Make sure the field is set before query. It's most
371  * likely an error to call this while parsing (for example fields set
372  * as FIELD_NOW will only be set to some value after parsing).
373  */
374 static int
375 get_field (struct state *state, enum field field)
376 {
377     assert (field < ARRAY_SIZE (state->tm));
378
379     return state->tm[field];
380 }
381
382 /*
383  * Validity checkers.
384  */
385 static bool is_valid_12hour (int h)
386 {
387     return h >= 1 && h <= 12;
388 }
389
390 static bool is_valid_time (int h, int m, int s)
391 {
392     /* Allow 24:00:00 to denote end of day. */
393     if (h == 24 && m == 0 && s == 0)
394         return true;
395
396     return h >= 0 && h <= 23 && m >= 0 && m <= 59 && s >= 0 && s <= 59;
397 }
398
399 static bool is_valid_mday (int mday)
400 {
401     return mday >= 1 && mday <= 31;
402 }
403
404 static bool is_valid_mon (int mon)
405 {
406     return mon >= 1 && mon <= 12;
407 }
408
409 static bool is_valid_year (int year)
410 {
411     return year >= 1970;
412 }
413
414 static bool is_valid_date (int year, int mon, int mday)
415 {
416     return is_valid_year (year) && is_valid_mon (mon) && is_valid_mday (mday);
417 }
418
419 /* Unset indicator for time and date set helpers. */
420 #define UNSET -1
421
422 /* Time set helper. No input checking. Use UNSET (-1) to leave unset. */
423 static int
424 set_abs_time (struct state *state, int hour, int min, int sec)
425 {
426     int r;
427
428     if (hour != UNSET) {
429         if ((r = set_field (state, TM_ABS_HOUR, hour)))
430             return r;
431     }
432
433     if (min != UNSET) {
434         if ((r = set_field (state, TM_ABS_MIN, min)))
435             return r;
436     }
437
438     if (sec != UNSET) {
439         if ((r = set_field (state, TM_ABS_SEC, sec)))
440             return r;
441     }
442
443     return 0;
444 }
445
446 /* Date set helper. No input checking. Use UNSET (-1) to leave unset. */
447 static int
448 set_abs_date (struct state *state, int year, int mon, int mday)
449 {
450     int r;
451
452     if (year != UNSET) {
453         if ((r = set_field (state, TM_ABS_YEAR, year)))
454             return r;
455     }
456
457     if (mon != UNSET) {
458         if ((r = set_field (state, TM_ABS_MON, mon)))
459             return r;
460     }
461
462     if (mday != UNSET) {
463         if ((r = set_field (state, TM_ABS_MDAY, mday)))
464             return r;
465     }
466
467     return 0;
468 }
469
470 /*
471  * Keyword parsing and handling.
472  */
473 struct keyword;
474 typedef int (*setter_t)(struct state *state, struct keyword *kw);
475
476 struct keyword {
477     const char *name;   /* keyword */
478     enum field field;   /* field to set, or FIELD_NONE if N/A */
479     int value;          /* value to set, or 0 if N/A */
480     setter_t set;       /* function to use for setting, if non-NULL */
481 };
482
483 /*
484  * Setter callback functions for keywords.
485  */
486 static int
487 kw_set_rel (struct state *state, struct keyword *kw)
488 {
489     int multiplier = 1;
490
491     /* Get a previously set multiplier, if any. */
492     consume_postponed_number (state, &multiplier, NULL, NULL);
493
494     /* Accumulate relative field values. */
495     return add_to_field (state, kw->field, multiplier * kw->value);
496 }
497
498 static int
499 kw_set_number (struct state *state, struct keyword *kw)
500 {
501     /* -1 = no length, from keyword. */
502     return set_postponed_number (state, kw->value, -1);
503 }
504
505 static int
506 kw_set_month (struct state *state, struct keyword *kw)
507 {
508     int n = get_postponed_length (state);
509
510     /* Consume postponed number if it could be mday. This handles "20
511      * January". */
512     if (n == 1 || n == 2) {
513         int r, v;
514
515         consume_postponed_number (state, &v, NULL, NULL);
516
517         if (!is_valid_mday (v))
518             return -PARSE_TIME_ERR_INVALIDDATE;
519
520         r = set_field (state, TM_ABS_MDAY, v);
521         if (r)
522             return r;
523     }
524
525     return set_field (state, kw->field, kw->value);
526 }
527
528 static int
529 kw_set_ampm (struct state *state, struct keyword *kw)
530 {
531     int n = get_postponed_length (state);
532
533     /* Consume postponed number if it could be hour. This handles
534      * "5pm". */
535     if (n == 1 || n == 2) {
536         int r, v;
537
538         consume_postponed_number (state, &v, NULL, NULL);
539
540         if (!is_valid_12hour (v))
541             return -PARSE_TIME_ERR_INVALIDTIME;
542
543         r = set_abs_time (state, v, 0, 0);
544         if (r)
545             return r;
546     }
547
548     return set_field (state, kw->field, kw->value);
549 }
550
551 static int
552 kw_set_timeofday (struct state *state, struct keyword *kw)
553 {
554     return set_abs_time (state, kw->value, 0, 0);
555 }
556
557 static int
558 kw_set_today (struct state *state, unused (struct keyword *kw))
559 {
560     enum field fields[] = { TM_ABS_YEAR, TM_ABS_MON, TM_ABS_MDAY };
561
562     return set_fields_to_now (state, fields, ARRAY_SIZE (fields));
563 }
564
565 static int
566 kw_set_now (struct state *state, unused (struct keyword *kw))
567 {
568     enum field fields[] = { TM_ABS_HOUR, TM_ABS_MIN, TM_ABS_SEC };
569
570     return set_fields_to_now (state, fields, ARRAY_SIZE (fields));
571 }
572
573 static int
574 kw_set_ordinal (struct state *state, struct keyword *kw)
575 {
576     int n, v;
577
578     /* Require a postponed number. */
579     if (!consume_postponed_number (state, &v, &n, NULL))
580         return -PARSE_TIME_ERR_DATEFORMAT;
581
582     /* Ordinals are mday. */
583     if (n != 1 && n != 2)
584         return -PARSE_TIME_ERR_DATEFORMAT;
585
586     /* Be strict about st, nd, rd, and lax about th. */
587     if (strcasecmp (kw->name, "st") == 0 && v != 1 && v != 21 && v != 31)
588         return -PARSE_TIME_ERR_INVALIDDATE;
589     else if (strcasecmp (kw->name, "nd") == 0 && v != 2 && v != 22)
590         return -PARSE_TIME_ERR_INVALIDDATE;
591     else if (strcasecmp (kw->name, "rd") == 0 && v != 3 && v != 23)
592         return -PARSE_TIME_ERR_INVALIDDATE;
593     else if (strcasecmp (kw->name, "th") == 0 && !is_valid_mday (v))
594         return -PARSE_TIME_ERR_INVALIDDATE;
595
596     return set_field (state, TM_ABS_MDAY, v);
597 }
598
599 static int
600 kw_ignore (unused (struct state *state), unused (struct keyword *kw))
601 {
602     return 0;
603 }
604
605 /*
606  * Accepted keywords.
607  *
608  * A keyword may optionally contain a '|' to indicate the minimum
609  * match length. Without one, full match is required. It's advisable
610  * to keep the minimum match parts unique across all keywords. If
611  * they're not, the first match wins.
612  *
613  * If keyword begins with '*', then the matching will be case
614  * sensitive. Otherwise the matching is case insensitive.
615  *
616  * If .set is NULL, the field specified by .field will be set to
617  * .value.
618  *
619  * Note: Observe how "m" and "mi" match minutes, "M" and "mo" and
620  * "mont" match months, but "mon" matches Monday.
621  */
622 static struct keyword keywords[] = {
623     /* Weekdays. */
624     { N_("sun|day"),    TM_WDAY,        0,      NULL },
625     { N_("mon|day"),    TM_WDAY,        1,      NULL },
626     { N_("tue|sday"),   TM_WDAY,        2,      NULL },
627     { N_("wed|nesday"), TM_WDAY,        3,      NULL },
628     { N_("thu|rsday"),  TM_WDAY,        4,      NULL },
629     { N_("fri|day"),    TM_WDAY,        5,      NULL },
630     { N_("sat|urday"),  TM_WDAY,        6,      NULL },
631
632     /* Months. */
633     { N_("jan|uary"),   TM_ABS_MON,     1,      kw_set_month },
634     { N_("feb|ruary"),  TM_ABS_MON,     2,      kw_set_month },
635     { N_("mar|ch"),     TM_ABS_MON,     3,      kw_set_month },
636     { N_("apr|il"),     TM_ABS_MON,     4,      kw_set_month },
637     { N_("may"),        TM_ABS_MON,     5,      kw_set_month },
638     { N_("jun|e"),      TM_ABS_MON,     6,      kw_set_month },
639     { N_("jul|y"),      TM_ABS_MON,     7,      kw_set_month },
640     { N_("aug|ust"),    TM_ABS_MON,     8,      kw_set_month },
641     { N_("sep|tember"), TM_ABS_MON,     9,      kw_set_month },
642     { N_("oct|ober"),   TM_ABS_MON,     10,     kw_set_month },
643     { N_("nov|ember"),  TM_ABS_MON,     11,     kw_set_month },
644     { N_("dec|ember"),  TM_ABS_MON,     12,     kw_set_month },
645
646     /* Durations. */
647     { N_("y|ears"),     TM_REL_YEAR,    1,      kw_set_rel },
648     { N_("mo|nths"),    TM_REL_MON,     1,      kw_set_rel },
649     { N_("*M"),         TM_REL_MON,     1,      kw_set_rel },
650     { N_("w|eeks"),     TM_REL_WEEK,    1,      kw_set_rel },
651     { N_("d|ays"),      TM_REL_DAY,     1,      kw_set_rel },
652     { N_("h|ours"),     TM_REL_HOUR,    1,      kw_set_rel },
653     { N_("hr|s"),       TM_REL_HOUR,    1,      kw_set_rel },
654     { N_("mi|nutes"),   TM_REL_MIN,     1,      kw_set_rel },
655     { N_("mins"),       TM_REL_MIN,     1,      kw_set_rel },
656     { N_("*m"),         TM_REL_MIN,     1,      kw_set_rel },
657     { N_("s|econds"),   TM_REL_SEC,     1,      kw_set_rel },
658     { N_("secs"),       TM_REL_SEC,     1,      kw_set_rel },
659
660     /* Numbers. */
661     { N_("one"),        TM_NONE,        1,      kw_set_number },
662     { N_("two"),        TM_NONE,        2,      kw_set_number },
663     { N_("three"),      TM_NONE,        3,      kw_set_number },
664     { N_("four"),       TM_NONE,        4,      kw_set_number },
665     { N_("five"),       TM_NONE,        5,      kw_set_number },
666     { N_("six"),        TM_NONE,        6,      kw_set_number },
667     { N_("seven"),      TM_NONE,        7,      kw_set_number },
668     { N_("eight"),      TM_NONE,        8,      kw_set_number },
669     { N_("nine"),       TM_NONE,        9,      kw_set_number },
670     { N_("ten"),        TM_NONE,        10,     kw_set_number },
671     { N_("dozen"),      TM_NONE,        12,     kw_set_number },
672     { N_("hundred"),    TM_NONE,        100,    kw_set_number },
673
674     /* Special number forms. */
675     { N_("this"),       TM_NONE,        0,      kw_set_number },
676     { N_("last"),       TM_NONE,        1,      kw_set_number },
677
678     /* Other special keywords. */
679     { N_("yesterday"),  TM_REL_DAY,     1,      kw_set_rel },
680     { N_("today"),      TM_NONE,        0,      kw_set_today },
681     { N_("now"),        TM_NONE,        0,      kw_set_now },
682     { N_("noon"),       TM_NONE,        12,     kw_set_timeofday },
683     { N_("midnight"),   TM_NONE,        0,      kw_set_timeofday },
684     { N_("am"),         TM_AMPM,        0,      kw_set_ampm },
685     { N_("a.m."),       TM_AMPM,        0,      kw_set_ampm },
686     { N_("pm"),         TM_AMPM,        1,      kw_set_ampm },
687     { N_("p.m."),       TM_AMPM,        1,      kw_set_ampm },
688     { N_("st"),         TM_NONE,        0,      kw_set_ordinal },
689     { N_("nd"),         TM_NONE,        0,      kw_set_ordinal },
690     { N_("rd"),         TM_NONE,        0,      kw_set_ordinal },
691     { N_("th"),         TM_NONE,        0,      kw_set_ordinal },
692     { N_("ago"),        TM_NONE,        0,      kw_ignore },
693
694     /* Timezone codes: offset in minutes. XXX: Add more codes. */
695     { N_("pst"),        TM_TZ,          -8*60,  NULL },
696     { N_("mst"),        TM_TZ,          -7*60,  NULL },
697     { N_("cst"),        TM_TZ,          -6*60,  NULL },
698     { N_("est"),        TM_TZ,          -5*60,  NULL },
699     { N_("ast"),        TM_TZ,          -4*60,  NULL },
700     { N_("nst"),        TM_TZ,          -(3*60+30),     NULL },
701
702     { N_("gmt"),        TM_TZ,          0,      NULL },
703     { N_("utc"),        TM_TZ,          0,      NULL },
704
705     { N_("wet"),        TM_TZ,          0,      NULL },
706     { N_("cet"),        TM_TZ,          1*60,   NULL },
707     { N_("eet"),        TM_TZ,          2*60,   NULL },
708     { N_("fet"),        TM_TZ,          3*60,   NULL },
709
710     { N_("wat"),        TM_TZ,          1*60,   NULL },
711     { N_("cat"),        TM_TZ,          2*60,   NULL },
712     { N_("eat"),        TM_TZ,          3*60,   NULL },
713 };
714
715 /*
716  * Compare strings str and keyword. Return the number of matching
717  * chars on match, 0 for no match.
718  *
719  * All of the alphabetic characters (isalpha) in str up to the first
720  * non-alpha character (or end of string) must match the
721  * keyword. Consequently, the value returned on match is the number of
722  * consecutive alphabetic characters in str.
723  *
724  * Abbreviated match is accepted if the keyword contains a '|'
725  * character, and str matches keyword up to that character. Any alpha
726  * characters after that in str must still match the keyword following
727  * the '|' character. If no '|' is present, all of keyword must match.
728  *
729  * Excessive, consecutive, and misplaced (at the beginning or end) '|'
730  * characters in keyword are handled gracefully. Only the first one
731  * matters.
732  *
733  * If match_case is true, the matching is case sensitive.
734  */
735 static size_t
736 match_keyword (const char *str, const char *keyword, bool match_case)
737 {
738     const char *s = str;
739     bool prefix_matched = false;
740
741     for (;;) {
742         while (*keyword == '|') {
743             prefix_matched = true;
744             keyword++;
745         }
746
747         if (!*s || !isalpha ((unsigned char) *s) || !*keyword)
748             break;
749
750         if (match_case) {
751             if (*s != *keyword)
752                 return 0;
753         } else {
754             if (tolower ((unsigned char) *s) !=
755                 tolower ((unsigned char) *keyword))
756                 return 0;
757         }
758         s++;
759         keyword++;
760     }
761
762     /* did not match all of the keyword in input string */
763     if (*s && isalpha ((unsigned char) *s))
764         return 0;
765
766     /* did not match enough of keyword */
767     if (*keyword && !prefix_matched)
768         return 0;
769
770     return s - str;
771 }
772
773 /*
774  * Parse a keyword. Return < 0 on error, number of parsed chars on
775  * success.
776  */
777 static ssize_t
778 parse_keyword (struct state *state, const char *s)
779 {
780     unsigned int i;
781     size_t n = 0;
782     struct keyword *kw = NULL;
783     int r;
784
785     for (i = 0; i < ARRAY_SIZE (keywords); i++) {
786         const char *keyword = _(keywords[i].name);
787         bool mcase = false;
788
789         /* Match case if keyword begins with '*'. */
790         if (*keyword == '*') {
791             mcase = true;
792             keyword++;
793         }
794
795         n = match_keyword (s, keyword, mcase);
796         if (n) {
797             kw = &keywords[i];
798             break;
799         }
800     }
801
802     if (!kw)
803         return -PARSE_TIME_ERR_KEYWORD;
804
805     if (kw->set)
806         r = kw->set (state, kw);
807     else
808         r = set_field (state, kw->field, kw->value);
809
810     if (r < 0)
811         return r;
812
813     return n;
814 }
815
816 /*
817  * Non-keyword parsers and their helpers.
818  */
819
820 static int
821 set_user_tz (struct state *state, char sign, int hour, int min)
822 {
823     int tz = hour * 60 + min;
824
825     assert (sign == '+' || sign == '-');
826
827     if (hour < 0 || hour > 14 || min < 0 || min > 59 || min % 15)
828         return -PARSE_TIME_ERR_INVALIDTIME;
829
830     if (sign == '-')
831         tz = -tz;
832
833     return set_field (state, TM_TZ, tz);
834 }
835
836 /*
837  * Parse a previously postponed number if one exists. Independent
838  * parsing of a postponed number when it wasn't consumed during
839  * parsing of the following token.
840  */
841 static int
842 parse_postponed_number (struct state *state, unused (enum field next_field))
843 {
844     int v, n;
845     char d;
846
847     /* Bail out if there's no postponed number. */
848     if (!consume_postponed_number (state, &v, &n, &d))
849         return 0;
850
851     if (n == 1 || n == 2) {
852         /* Notable exception: Previous field affects parsing. This
853          * handles "January 20". */
854         if (state->last_field == TM_ABS_MON) {
855             /* D[D] */
856             if (!is_valid_mday (v))
857                 return -PARSE_TIME_ERR_INVALIDDATE;
858
859             return set_field (state, TM_ABS_MDAY, v);
860         } else if (n == 2) {
861             /* XXX: Only allow if last field is hour, min, or sec? */
862             if (d == '+' || d == '-') {
863                 /* +/-HH */
864                 return set_user_tz (state, d, v, 0);
865             }
866         }
867     } else if (n == 4) {
868         /* Notable exception: Value affects parsing. Time zones are
869          * always at most 1400 and we don't understand years before
870          * 1970. */
871         if (!is_valid_year (v)) {
872             if (d == '+' || d == '-') {
873                 /* +/-HHMM */
874                 return set_user_tz (state, d, v / 100, v % 100);
875             }
876         } else {
877             /* YYYY */
878             return set_field (state, TM_ABS_YEAR, v);
879         }
880     } else if (n == 6) {
881         /* HHMMSS */
882         int hour = v / 10000;
883         int min = (v / 100) % 100;
884         int sec = v % 100;
885
886         if (!is_valid_time (hour, min, sec))
887             return -PARSE_TIME_ERR_INVALIDTIME;
888
889         return set_abs_time (state, hour, min, sec);
890     } else if (n == 8) {
891         /* YYYYMMDD */
892         int year = v / 10000;
893         int mon = (v / 100) % 100;
894         int mday = v % 100;
895
896         if (!is_valid_date (year, mon, mday))
897             return -PARSE_TIME_ERR_INVALIDDATE;
898
899         return set_abs_date (state, year, mon, mday);
900     }
901
902     return -PARSE_TIME_ERR_FORMAT;
903 }
904
905 static int tm_get_field (const struct tm *tm, enum field field);
906
907 static int
908 set_timestamp (struct state *state, time_t t)
909 {
910     struct tm tm;
911     enum field f;
912     int r;
913
914     if (gmtime_r (&t, &tm) == NULL)
915         return -PARSE_TIME_ERR_LIB;
916
917     for (f = TM_ABS_SEC; f != TM_NONE; f = next_abs_field (f)) {
918         r = set_field (state, f, tm_get_field (&tm, f));
919         if (r)
920             return r;
921     }
922
923     r = set_field (state, TM_TZ, 0);
924     if (r)
925         return r;
926
927     /* XXX: Prevent TM_AMPM with timestamp, e.g. "@123456 pm" */
928
929     return 0;
930 }
931
932 /* Parse a single number. Typically postpone parsing until later. */
933 static int
934 parse_single_number (struct state *state, unsigned long v,
935                      unsigned long n)
936 {
937     assert (n);
938
939     if (state->delim == '@')
940         return set_timestamp (state, (time_t) v);
941
942     if (v > INT_MAX)
943         return -PARSE_TIME_ERR_FORMAT;
944
945     return set_postponed_number (state, v, n);
946 }
947
948 static bool
949 is_time_sep (char c)
950 {
951     return c == ':';
952 }
953
954 static bool
955 is_date_sep (char c)
956 {
957     return c == '/' || c == '-' || c == '.';
958 }
959
960 static bool
961 is_sep (char c)
962 {
963     return is_time_sep (c) || is_date_sep (c);
964 }
965
966 /* Two-digit year: 00...69 is 2000s, 70...99 1900s, if n == 0 keep
967  * unset. */
968 static int
969 expand_year (unsigned long year, size_t n)
970 {
971     if (n == 2) {
972         return (year < 70 ? 2000 : 1900) + year;
973     } else if (n == 4) {
974         return year;
975     } else {
976         return UNSET;
977     }
978 }
979
980 /* Parse a date number triplet. */
981 static int
982 parse_date (struct state *state, char sep,
983             unsigned long v1, unsigned long v2, unsigned long v3,
984             size_t n1, size_t n2, size_t n3)
985 {
986     int year = UNSET, mon = UNSET, mday = UNSET;
987
988     assert (is_date_sep (sep));
989
990     switch (sep) {
991     case '/': /* Date: M[M]/D[D][/YY[YY]] or M[M]/YYYY */
992         if (n1 != 1 && n1 != 2)
993             return -PARSE_TIME_ERR_DATEFORMAT;
994
995         if ((n2 == 1 || n2 == 2) && (n3 == 0 || n3 == 2 || n3 == 4)) {
996             /* M[M]/D[D][/YY[YY]] */
997             year = expand_year (v3, n3);
998             mon = v1;
999             mday = v2;
1000         } else if (n2 == 4 && n3 == 0) {
1001             /* M[M]/YYYY */
1002             year = v2;
1003             mon = v1;
1004         } else {
1005             return -PARSE_TIME_ERR_DATEFORMAT;
1006         }
1007         break;
1008
1009     case '-': /* Date: YYYY-MM[-DD] or DD-MM[-YY[YY]] or MM-YYYY */
1010         if (n1 == 4 && n2 == 2 && (n3 == 0 || n3 == 2)) {
1011             /* YYYY-MM[-DD] */
1012             year = v1;
1013             mon = v2;
1014             if (n3)
1015                 mday = v3;
1016         } else if (n1 == 2 && n2 == 2 && (n3 == 0 || n3 == 2 || n3 == 4)) {
1017             /* DD-MM[-YY[YY]] */
1018             year = expand_year (v3, n3);
1019             mon = v2;
1020             mday = v1;
1021         } else if (n1 == 2 && n2 == 4 && n3 == 0) {
1022             /* MM-YYYY */
1023             year = v2;
1024             mon = v1;
1025         } else {
1026             return -PARSE_TIME_ERR_DATEFORMAT;
1027         }
1028         break;
1029
1030     case '.': /* Date: D[D].M[M][.[YY[YY]]] */
1031         if ((n1 != 1 && n1 != 2) || (n2 != 1 && n2 != 2) ||
1032             (n3 != 0 && n3 != 2 && n3 != 4))
1033             return -PARSE_TIME_ERR_DATEFORMAT;
1034
1035         year = expand_year (v3, n3);
1036         mon = v2;
1037         mday = v1;
1038         break;
1039     }
1040
1041     if (year != UNSET && !is_valid_year (year))
1042         return -PARSE_TIME_ERR_INVALIDDATE;
1043
1044     if (mon != UNSET && !is_valid_mon (mon))
1045         return -PARSE_TIME_ERR_INVALIDDATE;
1046
1047     if (mday != UNSET && !is_valid_mday (mday))
1048         return -PARSE_TIME_ERR_INVALIDDATE;
1049
1050     return set_abs_date (state, year, mon, mday);
1051 }
1052
1053 /* Parse a time number triplet. */
1054 static int
1055 parse_time (struct state *state, char sep,
1056             unsigned long v1, unsigned long v2, unsigned long v3,
1057             size_t n1, size_t n2, size_t n3)
1058 {
1059     assert (is_time_sep (sep));
1060
1061     if ((n1 != 1 && n1 != 2) || n2 != 2 || (n3 != 0 && n3 != 2))
1062         return -PARSE_TIME_ERR_TIMEFORMAT;
1063
1064     /*
1065      * Notable exception: Previously set fields affect
1066      * parsing. Interpret (+|-)HH:MM as time zone only if hour and
1067      * minute have been set.
1068      *
1069      * XXX: This could be fixed by restricting the delimiters
1070      * preceding time. For '+' it would be justified, but for '-' it
1071      * might be inconvenient. However prefer to allow '-' as an
1072      * insignificant delimiter preceding time for convenience, and
1073      * handle '+' the same way for consistency between positive and
1074      * negative time zones.
1075      */
1076     if (is_field_set (state, TM_ABS_HOUR) &&
1077         is_field_set (state, TM_ABS_MIN) &&
1078         n1 == 2 && n2 == 2 && n3 == 0 &&
1079         (state->delim == '+' || state->delim == '-')) {
1080         return set_user_tz (state, state->delim, v1, v2);
1081     }
1082
1083     if (!is_valid_time (v1, v2, v3))
1084         return -PARSE_TIME_ERR_INVALIDTIME;
1085
1086     return set_abs_time (state, v1, v2, n3 ? v3 : 0);
1087 }
1088
1089 /* strtoul helper that assigns length. */
1090 static unsigned long
1091 strtoul_len (const char *s, const char **endp, size_t *len)
1092 {
1093     unsigned long val = strtoul (s, (char **) endp, 10);
1094
1095     *len = *endp - s;
1096     return val;
1097 }
1098
1099 /*
1100  * Parse a (group of) number(s). Return < 0 on error, number of parsed
1101  * chars on success.
1102  */
1103 static ssize_t
1104 parse_number (struct state *state, const char *s)
1105 {
1106     int r;
1107     unsigned long v1, v2, v3 = 0;
1108     size_t n1, n2, n3 = 0;
1109     const char *p = s;
1110     char sep;
1111
1112     v1 = strtoul_len (p, &p, &n1);
1113
1114     if (!is_sep (*p) || !isdigit ((unsigned char) *(p + 1))) {
1115         /* A single number. */
1116         r = parse_single_number (state, v1, n1);
1117         if (r)
1118             return r;
1119
1120         return p - s;
1121     }
1122
1123     sep = *p;
1124     v2 = strtoul_len (p + 1, &p, &n2);
1125
1126     /* A group of two or three numbers? */
1127     if (*p == sep && isdigit ((unsigned char) *(p + 1)))
1128         v3 = strtoul_len (p + 1, &p, &n3);
1129
1130     if (is_time_sep (sep))
1131         r = parse_time (state, sep, v1, v2, v3, n1, n2, n3);
1132     else
1133         r = parse_date (state, sep, v1, v2, v3, n1, n2, n3);
1134
1135     if (r)
1136         return r;
1137
1138     return p - s;
1139 }
1140
1141 /*
1142  * Parse delimiter(s). Throw away all except the last one, which is
1143  * stored for parsing the next non-delimiter. Return < 0 on error,
1144  * number of parsed chars on success.
1145  *
1146  * XXX: We might want to be more strict here.
1147  */
1148 static ssize_t
1149 parse_delim (struct state *state, const char *s)
1150 {
1151     const char *p = s;
1152
1153     /*
1154      * Skip non-alpha and non-digit, and store the last for further
1155      * processing.
1156      */
1157     while (*p && !isalnum ((unsigned char) *p)) {
1158         set_delim (state, *p);
1159         p++;
1160     }
1161
1162     return p - s;
1163 }
1164
1165 /*
1166  * Parse a date/time string. Return < 0 on error, number of parsed
1167  * chars on success.
1168  */
1169 static ssize_t
1170 parse_input (struct state *state, const char *s)
1171 {
1172     const char *p = s;
1173     ssize_t n;
1174     int r;
1175
1176     while (*p) {
1177         if (isalpha ((unsigned char) *p)) {
1178             n = parse_keyword (state, p);
1179         } else if (isdigit ((unsigned char) *p)) {
1180             n = parse_number (state, p);
1181         } else {
1182             n = parse_delim (state, p);
1183         }
1184
1185         if (n <= 0) {
1186             if (n == 0)
1187                 n = -PARSE_TIME_ERR;
1188
1189             return n;
1190         }
1191
1192         p += n;
1193     }
1194
1195     /* Parse a previously postponed number, if any. */
1196     r = parse_postponed_number (state, TM_NONE);
1197     if (r < 0)
1198         return r;
1199
1200     return p - s;
1201 }
1202
1203 /*
1204  * Processing the parsed input.
1205  */
1206
1207 /*
1208  * Initialize reference time to tm. Use time zone in state if
1209  * specified, otherwise local time. Use now for reference time if
1210  * non-NULL, otherwise current time.
1211  */
1212 static int
1213 initialize_now (struct state *state, const time_t *ref, struct tm *tm)
1214 {
1215     time_t t;
1216
1217     if (ref) {
1218         t = *ref;
1219     } else {
1220         if (time (&t) == (time_t) -1)
1221             return -PARSE_TIME_ERR_LIB;
1222     }
1223
1224     if (is_field_set (state, TM_TZ)) {
1225         /* Some other time zone. */
1226
1227         /* Adjust now according to the TZ. */
1228         t += get_field (state, TM_TZ) * 60;
1229
1230         /* It's not gm, but this doesn't mess with the TZ. */
1231         if (gmtime_r (&t, tm) == NULL)
1232             return -PARSE_TIME_ERR_LIB;
1233     } else {
1234         /* Local time. */
1235         if (localtime_r (&t, tm) == NULL)
1236             return -PARSE_TIME_ERR_LIB;
1237     }
1238
1239     return 0;
1240 }
1241
1242 /*
1243  * Normalize tm according to mktime(3); if structure members are
1244  * outside their valid interval, they will be normalized (so that, for
1245  * example, 40 October is changed into 9 November), and tm_wday and
1246  * tm_yday are set to values determined from the contents of the other
1247  * fields.
1248  *
1249  * Both mktime(3) and localtime_r(3) use local time, but they cancel
1250  * each other out here, making this function agnostic to time zone.
1251  */
1252 static int
1253 normalize_tm (struct tm *tm)
1254 {
1255     time_t t = mktime (tm);
1256
1257     if (t == (time_t) -1)
1258         return -PARSE_TIME_ERR_LIB;
1259
1260     if (!localtime_r (&t, tm))
1261         return -PARSE_TIME_ERR_LIB;
1262
1263     return 0;
1264 }
1265
1266 /* Get field out of a struct tm. */
1267 static int
1268 tm_get_field (const struct tm *tm, enum field field)
1269 {
1270     switch (field) {
1271     case TM_ABS_SEC:    return tm->tm_sec;
1272     case TM_ABS_MIN:    return tm->tm_min;
1273     case TM_ABS_HOUR:   return tm->tm_hour;
1274     case TM_ABS_MDAY:   return tm->tm_mday;
1275     case TM_ABS_MON:    return tm->tm_mon + 1; /* 0- to 1-based */
1276     case TM_ABS_YEAR:   return 1900 + tm->tm_year;
1277     case TM_WDAY:       return tm->tm_wday;
1278     case TM_ABS_ISDST:  return tm->tm_isdst;
1279     default:
1280         assert (false);
1281         break;
1282     }
1283
1284     return 0;
1285 }
1286
1287 /* Modify hour according to am/pm setting. */
1288 static int
1289 fixup_ampm (struct state *state)
1290 {
1291     int hour, hdiff = 0;
1292
1293     if (!is_field_set (state, TM_AMPM))
1294         return 0;
1295
1296     if (!is_field_set (state, TM_ABS_HOUR))
1297         return -PARSE_TIME_ERR_TIMEFORMAT;
1298
1299     hour = get_field (state, TM_ABS_HOUR);
1300     if (!is_valid_12hour (hour))
1301         return -PARSE_TIME_ERR_INVALIDTIME;
1302
1303     if (get_field (state, TM_AMPM)) {
1304         /* 12pm is noon. */
1305         if (hour != 12)
1306             hdiff = 12;
1307     } else {
1308         /* 12am is midnight, beginning of day. */
1309         if (hour == 12)
1310             hdiff = -12;
1311     }
1312
1313     add_to_field (state, TM_REL_HOUR, -hdiff);
1314
1315     return 0;
1316 }
1317
1318 /* Combine absolute and relative fields, and round. */
1319 static int
1320 create_output (struct state *state, time_t *t_out, const time_t *ref,
1321                int round)
1322 {
1323     struct tm tm = { .tm_isdst = -1 };
1324     struct tm now;
1325     time_t t;
1326     enum field f;
1327     int r;
1328     int week_round = PARSE_TIME_NO_ROUND;
1329
1330     r = initialize_now (state, ref, &now);
1331     if (r)
1332         return r;
1333
1334     /* Initialize fields flagged as "now" to reference time. */
1335     for (f = TM_ABS_SEC; f != TM_NONE; f = next_abs_field (f)) {
1336         if (state->set[f] == FIELD_NOW) {
1337             state->tm[f] = tm_get_field (&now, f);
1338             state->set[f] = FIELD_SET;
1339         }
1340     }
1341
1342     /*
1343      * If WDAY is set but MDAY is not, we consider WDAY relative
1344      *
1345      * XXX: This fails on stuff like "two months monday" because two
1346      * months ago wasn't the same day as today. Postpone until we know
1347      * date?
1348      */
1349     if (is_field_set (state, TM_WDAY) &&
1350         !is_field_set (state, TM_ABS_MDAY)) {
1351         int wday = get_field (state, TM_WDAY);
1352         int today = tm_get_field (&now, TM_WDAY);
1353         int rel_days;
1354
1355         if (today > wday)
1356             rel_days = today - wday;
1357         else
1358             rel_days = today + 7 - wday;
1359
1360         /* This also prevents special week rounding from happening. */
1361         add_to_field (state, TM_REL_DAY, rel_days);
1362
1363         unset_field (state, TM_WDAY);
1364     }
1365
1366     r = fixup_ampm (state);
1367     if (r)
1368         return r;
1369
1370     /*
1371      * Iterate fields from most accurate to least accurate, and set
1372      * unset fields according to requested rounding.
1373      */
1374     for (f = TM_ABS_SEC; f != TM_NONE; f = next_abs_field (f)) {
1375         if (round != PARSE_TIME_NO_ROUND) {
1376             enum field r = abs_to_rel_field (f);
1377
1378             if (is_field_set (state, f) || is_field_set (state, r)) {
1379                 if (round >= PARSE_TIME_ROUND_UP && f != TM_ABS_SEC) {
1380                     /*
1381                      * This is the most accurate field
1382                      * specified. Round up adjusting it towards
1383                      * future.
1384                      */
1385                     add_to_field (state, r, -1);
1386
1387                     /*
1388                      * Go back a second if the result is to be used
1389                      * for inclusive comparisons.
1390                      */
1391                     if (round == PARSE_TIME_ROUND_UP_INCLUSIVE)
1392                         add_to_field (state, TM_REL_SEC, 1);
1393                 }
1394                 round = PARSE_TIME_NO_ROUND; /* No more rounding. */
1395             } else {
1396                 if (f == TM_ABS_MDAY &&
1397                     is_field_set (state, TM_REL_WEEK)) {
1398                     /* Week is most accurate. */
1399                     week_round = round;
1400                     round = PARSE_TIME_NO_ROUND;
1401                 } else {
1402                     set_field (state, f, get_field_epoch_value (f));
1403                 }
1404             }
1405         }
1406
1407         if (!is_field_set (state, f))
1408             set_field (state, f, tm_get_field (&now, f));
1409     }
1410
1411     /* Special case: rounding with week accuracy. */
1412     if (week_round != PARSE_TIME_NO_ROUND) {
1413         /* Temporarily set more accurate fields to now. */
1414         set_field (state, TM_ABS_SEC, tm_get_field (&now, TM_ABS_SEC));
1415         set_field (state, TM_ABS_MIN, tm_get_field (&now, TM_ABS_MIN));
1416         set_field (state, TM_ABS_HOUR, tm_get_field (&now, TM_ABS_HOUR));
1417         set_field (state, TM_ABS_MDAY, tm_get_field (&now, TM_ABS_MDAY));
1418     }
1419
1420     /*
1421      * Set all fields. They may contain out of range values before
1422      * normalization by mktime(3).
1423      */
1424     tm.tm_sec = get_field (state, TM_ABS_SEC) - get_field (state, TM_REL_SEC);
1425     tm.tm_min = get_field (state, TM_ABS_MIN) - get_field (state, TM_REL_MIN);
1426     tm.tm_hour = get_field (state, TM_ABS_HOUR) - get_field (state, TM_REL_HOUR);
1427     tm.tm_mday = get_field (state, TM_ABS_MDAY) -
1428                  get_field (state, TM_REL_DAY) - 7 * get_field (state, TM_REL_WEEK);
1429     tm.tm_mon = get_field (state, TM_ABS_MON) - get_field (state, TM_REL_MON);
1430     tm.tm_mon--; /* 1- to 0-based */
1431     tm.tm_year = get_field (state, TM_ABS_YEAR) - get_field (state, TM_REL_YEAR) - 1900;
1432
1433     /*
1434      * It's always normal time.
1435      *
1436      * XXX: This is probably not a solution that universally
1437      * works. Just make sure DST is not taken into account. We don't
1438      * want rounding to be affected by DST.
1439      */
1440     tm.tm_isdst = -1;
1441
1442     /* Special case: rounding with week accuracy. */
1443     if (week_round != PARSE_TIME_NO_ROUND) {
1444         /* Normalize to get proper tm.wday. */
1445         r = normalize_tm (&tm);
1446         if (r < 0)
1447             return r;
1448
1449         /* Set more accurate fields back to zero. */
1450         tm.tm_sec = 0;
1451         tm.tm_min = 0;
1452         tm.tm_hour = 0;
1453         tm.tm_isdst = -1;
1454
1455         /* Monday is the true 1st day of week, but this is easier. */
1456         if (week_round >= PARSE_TIME_ROUND_UP) {
1457             tm.tm_mday += 7 - tm.tm_wday;
1458             if (week_round == PARSE_TIME_ROUND_UP_INCLUSIVE)
1459                 tm.tm_sec--;
1460         } else {
1461             tm.tm_mday -= tm.tm_wday;
1462         }
1463     }
1464
1465     if (is_field_set (state, TM_TZ)) {
1466         /* tm is in specified TZ, convert to UTC for timegm(3). */
1467         tm.tm_min -= get_field (state, TM_TZ);
1468         t = timegm (&tm);
1469     } else {
1470         /* tm is in local time. */
1471         t = mktime (&tm);
1472     }
1473
1474     if (t == (time_t) -1)
1475         return -PARSE_TIME_ERR_LIB;
1476
1477     *t_out = t;
1478
1479     return 0;
1480 }
1481
1482 /* Internally, all errors are < 0. parse_time_string() returns errors > 0. */
1483 #define EXTERNAL_ERR(r) (-r)
1484
1485 int
1486 parse_time_string (const char *s, time_t *t, const time_t *ref, int round)
1487 {
1488     struct state state = { .last_field = TM_NONE };
1489     int r;
1490
1491     if (!s || !t)
1492         return EXTERNAL_ERR (-PARSE_TIME_ERR);
1493
1494     r = parse_input (&state, s);
1495     if (r < 0)
1496         return EXTERNAL_ERR (r);
1497
1498     r = create_output (&state, t, ref, round);
1499     if (r < 0)
1500         return EXTERNAL_ERR (r);
1501
1502     return 0;
1503 }