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