1 /* $NetBSD: t_parsedate.c,v 1.33 2022/05/02 19:57:50 christos Exp $ */
2 /*-
3  * Copyright (c) 2010, 2015 The NetBSD Foundation, Inc.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in
14  *    the documentation and/or other materials provided with the
15  *    distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
21  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
23  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
27  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #include <sys/cdefs.h>
32 __RCSID("$NetBSD: t_parsedate.c,v 1.33 2022/05/02 19:57:50 christos Exp $");
33 
34 #include <atf-c.h>
35 #include <errno.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <time.h>
39 #include <util.h>
40 
41 /*
42  * ANY is used as a placeholder for values that do not need to be
43  * checked.  The actual value is arbitrary.  We don't use -1
44  * because some tests might want to use -1 as a literal value.
45  */
46 #define ANY -30215
47 
48 /* parsecheck --
49  * call parsedate(), then call time_to_tm() on the result,
50  * and check that year/month/day/hour/minute/second are as expected.
51  *
52  * time_to_tm should usually be localtime_r or gmtime_r.
53  *
54  * Don't check values specified as ANY.
55  */
56 static void
parsecheck(const char * datestr,const time_t * reftime,const int * zoff,struct tm * time_to_tm (const time_t *,struct tm *),int year,int month,int day,int hour,int minute,int second)57 parsecheck(const char *datestr, const time_t *reftime, const int *zoff,
58           struct tm * time_to_tm(const time_t *, struct tm *),
59           int year, int month, int day, int hour, int minute, int second)
60 {
61           time_t t;
62           struct tm tm;
63           char argstr[128];
64 
65           /*
66            * printable version of the args.
67            *
68            * Note that printf("%.*d", 0, 0)) prints nothing at all,
69            * while printf("%.*d", 1, val) prints the value as usual.
70            */
71           snprintf(argstr, sizeof(argstr), "%s%s%s, %s%.*jd, %s%.*d",
72                     /* NULL or \"<datestr>\" */
73                     (datestr ? "\"" : ""),
74                     (datestr ? datestr : "NULL"),
75                     (datestr ? "\"" : ""),
76                     /* NULL or *reftime */
77                     (reftime ? "" : "NULL"),
78                     (reftime ? 1 : 0),
79                     (reftime ? (intmax_t)*reftime : (intmax_t)0),
80                     /* NULL or *zoff */
81                     (zoff ? "" : "NULL"),
82                     (zoff ? 1 : 0),
83                     (zoff ? *zoff : 0));
84 
85           ATF_CHECK_MSG((t = parsedate(datestr, reftime, zoff)) != -1,
86               "parsedate(%s) returned -1\n", argstr);
87           if (t == -1)
88                     return;
89 
90           ATF_CHECK(time_to_tm(&t, &tm) != NULL);
91           if (year != ANY)
92                     ATF_CHECK_MSG(tm.tm_year + 1900 == year,
93                         "parsedate(%s) expected year %d got %d (+1900)\n",
94                         argstr, year, (int)tm.tm_year);
95           if (month != ANY)
96                     ATF_CHECK_MSG(tm.tm_mon + 1 == month,
97                         "parsedate(%s) expected month %d got %d (+1)\n",
98                         argstr, month, (int)tm.tm_mon);
99           if (day != ANY)
100                     ATF_CHECK_MSG(tm.tm_mday == day,
101                         "parsedate(%s) expected day %d got %d\n",
102                         argstr, day, (int)tm.tm_mday);
103           if (hour != ANY)
104                     ATF_CHECK_MSG(tm.tm_hour == hour,
105                         "parsedate(%s) expected hour %d got %d\n",
106                         argstr, hour, (int)tm.tm_hour);
107           if (minute != ANY)
108                     ATF_CHECK_MSG(tm.tm_min == minute,
109                         "parsedate(%s) expected minute %d got %d\n",
110                         argstr, minute, (int)tm.tm_min);
111           if (second != ANY)
112                     ATF_CHECK_MSG(tm.tm_sec == second,
113                         "parsedate(%s) expected second %d got %d\n",
114                         argstr, second, (int)tm.tm_sec);
115 }
116 
117 ATF_TC(dates);
118 
ATF_TC_HEAD(dates,tc)119 ATF_TC_HEAD(dates, tc)
120 {
121           atf_tc_set_md_var(tc, "descr", "Test unambiguous dates"
122               " (PR lib/44255)");
123 }
124 
ATF_TC_BODY(dates,tc)125 ATF_TC_BODY(dates, tc)
126 {
127 
128           parsecheck("9/10/68", NULL, NULL, localtime_r,
129                     2068, 9, 10, 0, 0, 0); /* year < 69: add 2000 */
130           parsecheck("9/10/69", NULL, NULL, localtime_r,
131                     1969, 9, 10, 0, 0, 0); /* 69 <= year < 100: add 1900 */
132           parsecheck("68-09-10", NULL, NULL, localtime_r,
133                     68, 9, 10, 0, 0, 0); /* ISO8601 year remains unchanged */
134           parsecheck("70-09-10", NULL, NULL, localtime_r,
135                     70, 9, 10, 0, 0, 0); /* ISO8601 year remains unchanged */
136           parsecheck("2006-11-17", NULL, NULL, localtime_r,
137                     2006, 11, 17, 0, 0, 0);
138           parsecheck("10/1/2000", NULL, NULL, localtime_r,
139                     2000, 10, 1, 0, 0, 0); /* month/day/year */
140           parsecheck("12/01/2022", NULL, NULL, localtime_r,
141                     2022, 12, 1, 0, 0, 0); /* month/day/year, December */
142           parsecheck("20 Jun 1994", NULL, NULL, localtime_r,
143                     1994, 6, 20, 0, 0, 0);
144           parsecheck("97 September 2", NULL, NULL, localtime_r,
145                     1997, 9, 2, 0, 0, 0);
146           parsecheck("23jun2001", NULL, NULL, localtime_r,
147                     2001, 6, 23, 0, 0, 0);
148           parsecheck("1-sep-06", NULL, NULL, localtime_r,
149                     2006, 9, 1, 0, 0, 0);
150           parsecheck("1/11", NULL, NULL, localtime_r,
151                     ANY, 1, 11, 0, 0, 0); /* month/day */
152           parsecheck("1500-01-02", NULL, NULL, localtime_r,
153                     1500, 1, 2, 0, 0, 0);
154           parsecheck("9999-12-21", NULL, NULL, localtime_r,
155                     9999, 12, 21, 0, 0, 0);
156           parsecheck("2015.12.07.08.07.35", NULL, NULL, localtime_r,
157                     2015, 12, 7, 8, 7, 35);
158 }
159 
160 ATF_TC(times);
161 
ATF_TC_HEAD(times,tc)162 ATF_TC_HEAD(times, tc)
163 {
164           atf_tc_set_md_var(tc, "descr", "Test times"
165               " (PR lib/44255)");
166 }
167 
ATF_TC_BODY(times,tc)168 ATF_TC_BODY(times, tc)
169 {
170 
171           parsecheck("10:01", NULL, NULL, localtime_r,
172                     ANY, ANY, ANY, 10, 1, 0);
173           parsecheck("10:12pm", NULL, NULL, localtime_r,
174                     ANY, ANY, ANY, 22, 12, 0);
175           parsecheck("12:11:01.000012", NULL, NULL, localtime_r,
176                     ANY, ANY, ANY, 12, 11, 1);
177           parsecheck("12:21-0500", NULL, NULL, gmtime_r,
178                     ANY, ANY, ANY, 12+5, 21, 0);
179           /* numeric zones not permitted with am/pm ... */
180           parsecheck("7 a.m. ICT", NULL, NULL, gmtime_r,
181                     ANY, ANY, ANY, 7-7, 0, 0);
182           parsecheck("midnight", NULL, NULL, localtime_r,
183                     ANY, ANY, ANY, 0, 0, 0);
184           parsecheck("mn", NULL, NULL, localtime_r,
185                     ANY, ANY, ANY, 0, 0, 0);
186           parsecheck("noon", NULL, NULL, localtime_r,
187                     ANY, ANY, ANY, 12, 0, 0);
188 
189           /*
190            * The following tests used to trigger the bug from PR lib/52101
191            * but that is fixed now.
192            *
193           atf_tc_expect_fail("PR lib/52101");
194            */
195 
196           parsecheck("12:30 am", NULL, NULL, localtime_r,
197                     ANY, ANY, ANY, 0, 30, 0);
198           parsecheck("12:30 pm", NULL, NULL, localtime_r,
199                     ANY, ANY, ANY, 12, 30, 0);
200 
201           /*
202            * Technically, these are invalid, noon and midnight
203            * are neither am, nor pm, but this is what people expect...
204            */
205           parsecheck("12:00:00 am", NULL, NULL, localtime_r,
206                     ANY, ANY, ANY, 0, 0, 0);
207           parsecheck("12:00:00 pm", NULL, NULL, localtime_r,
208                     ANY, ANY, ANY, 12, 0, 0);
209           parsecheck("12am", NULL, NULL, localtime_r,
210                     ANY, ANY, ANY, 0, 0, 0);
211           parsecheck("12pm", NULL, NULL, localtime_r,
212                     ANY, ANY, ANY, 12, 0, 0);
213 
214           /* end 52101 bug tests */
215 
216           parsecheck("12 noon", NULL, NULL, localtime_r,
217                     ANY, ANY, ANY, 12, 0, 0);
218           parsecheck("12 midnight", NULL, NULL, localtime_r,
219                     ANY, ANY, ANY, 0, 0, 0);
220           parsecheck("12 midday", NULL, NULL, localtime_r,  /* unlikely! */
221                     ANY, ANY, ANY, 12, 0, 0);
222           parsecheck("12 mn", NULL, NULL, localtime_r,
223                     ANY, ANY, ANY, 0, 0, 0);
224 
225           parsecheck("12:00 noon", NULL, NULL, localtime_r,
226                     ANY, ANY, ANY, 12, 0, 0);
227           parsecheck("12:00 midnight", NULL, NULL, localtime_r,
228                     ANY, ANY, ANY, 0, 0, 0);
229           parsecheck("12:00:00 noon", NULL, NULL, localtime_r,
230                     ANY, ANY, ANY, 12, 0, 0);
231           parsecheck("12:00:00 midnight", NULL, NULL, localtime_r,
232                     ANY, ANY, ANY, 0, 0, 0);
233 }
234 
235 ATF_TC(dsttimes);
236 
ATF_TC_HEAD(dsttimes,tc)237 ATF_TC_HEAD(dsttimes, tc)
238 {
239           atf_tc_set_md_var(tc, "descr", "Test DST transition times"
240               " (PR lib/47916)");
241 }
242 
ATF_TC_BODY(dsttimes,tc)243 ATF_TC_BODY(dsttimes, tc)
244 {
245           struct tm tm;
246           time_t t;
247           int tzoff;
248 
249           putenv(__UNCONST("TZ=EST"));
250           tzset();
251           parsecheck("12:0", NULL, NULL, localtime_r,
252                     ANY, ANY, ANY, 12, 0, 0);
253 
254           putenv(__UNCONST("TZ=Asia/Tokyo"));
255           tzset();
256           parsecheck("12:0", NULL, NULL, localtime_r,
257                     ANY, ANY, ANY, 12, 0, 0);
258 
259           /*
260            * When the effective local time is Tue Jul  9 13:21:53 BST 2013,
261            * check mktime("14:00")
262            */
263           putenv(__UNCONST("TZ=Europe/London"));
264           tzset();
265           tm = (struct tm){
266                     .tm_year = 2013-1900, .tm_mon = 7-1, .tm_mday = 9,
267                     .tm_hour = 13, .tm_min = 21, .tm_sec = 53,
268                     .tm_isdst = 0 };
269           t = mktime(&tm);
270           ATF_CHECK(t != (time_t)-1);
271           parsecheck("14:00", &t, NULL, localtime_r,
272                     2013, 7, 9, 14, 0, 0);
273           tzoff = -60; /* British Summer Time */
274           parsecheck("14:00", &t, &tzoff, localtime_r,
275                     2013, 7, 9, 14, 0, 0);
276 }
277 
278 ATF_TC(relative);
279 
ATF_TC_HEAD(relative,tc)280 ATF_TC_HEAD(relative, tc)
281 {
282           atf_tc_set_md_var(tc, "descr", "Test relative items"
283               " (PR lib/44255)");
284 }
285 
ATF_TC_BODY(relative,tc)286 ATF_TC_BODY(relative, tc)
287 {
288           struct tm tm;
289           time_t now;
290 
291 #define REL_CHECK(s, now, tm) do {                                              \
292           time_t p, q;                                                                    \
293           char nb[30], pb[30], qb[30];                                          \
294           p = parsedate(s, &now, NULL);                                         \
295           q = mktime(&tm);                                                      \
296           ATF_CHECK_EQ_MSG(p, q,                                                          \
297               "From %jd (%24.24s) using \"%s\", obtained %jd (%24.24s); expected %jd (%24.24s)", \
298               (uintmax_t)now, ctime_r(&now, nb),                                \
299               s, (uintmax_t)p, ctime_r(&p, pb), (uintmax_t)q,                   \
300               ctime_r(&q, qb));                                                           \
301     } while (/*CONSTCOND*/0)
302 
303 #define isleap(yr) (((yr) & 3) == 0 && (((yr) % 100) != 0 ||                    \
304                               ((1900+(yr)) % 400) == 0))
305 
306           ATF_CHECK(parsedate("-1 month", NULL, NULL) != -1);
307           ATF_CHECK(parsedate("last friday", NULL, NULL) != -1);
308           ATF_CHECK(parsedate("one week ago", NULL, NULL) != -1);
309           ATF_CHECK(parsedate("this thursday", NULL, NULL) != -1);
310           ATF_CHECK(parsedate("next sunday", NULL, NULL) != -1);
311           ATF_CHECK(parsedate("+2 years", NULL, NULL) != -1);
312 
313           /*
314            * Test relative to a number of fixed dates.  Avoid the
315            * edges of the time_t range to avert under- or overflow
316            * of the relative date, and use a prime step for maximum
317            * coverage of different times of day/week/month/year.
318            */
319           for (now = 0x00FFFFFF; now < 0xFF000000; now += 3777779) {
320                     ATF_CHECK(localtime_r(&now, &tm) != NULL);
321                     tm.tm_mday--;
322                     /* "yesterday" leaves time untouched */
323                     tm.tm_isdst = -1;
324                     REL_CHECK("yesterday", now, tm);
325 
326                     ATF_CHECK(localtime_r(&now, &tm) != NULL);
327                     tm.tm_mday++;
328                     /* as does "tomorrow" */
329                     tm.tm_isdst = -1;
330                     REL_CHECK("tomorrow", now, tm);
331 
332                     ATF_CHECK(localtime_r(&now, &tm) != NULL);
333                     if (tm.tm_wday > 4)
334                               tm.tm_mday += 7;
335                     tm.tm_mday += 4 - tm.tm_wday;
336                     /* if a day name is mentioned, it means midnight (by default) */
337                     tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
338                     tm.tm_isdst = -1;
339                     REL_CHECK("this thursday", now, tm);
340 
341                     ATF_CHECK(localtime_r(&now, &tm) != NULL);
342                     tm.tm_mday += 14 - (tm.tm_wday ? tm.tm_wday : 7);
343                     tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
344                     tm.tm_isdst = -1;
345                     REL_CHECK("next sunday", now, tm);
346 
347                     ATF_CHECK(localtime_r(&now, &tm) != NULL);
348                     if (tm.tm_wday <= 5)
349                               tm.tm_mday -= 7;
350                     tm.tm_mday += 5 - tm.tm_wday;
351                     tm.tm_sec = tm.tm_min = 0;
352                     tm.tm_hour = 16;
353                     tm.tm_isdst = -1;
354                     REL_CHECK("last friday 4 p.m.", now, tm);
355 
356                     ATF_CHECK(localtime_r(&now, &tm) != NULL);
357                     tm.tm_mday += 14;
358                     if (tm.tm_wday > 3)
359                               tm.tm_mday += 7;
360                     tm.tm_mday += 3 - tm.tm_wday;
361                     tm.tm_sec = tm.tm_min = 0;
362                     tm.tm_hour = 3;
363                     tm.tm_isdst = -1;
364                     REL_CHECK("we fortnight 3 a.m.", now, tm);
365 
366                     ATF_CHECK(localtime_r(&now, &tm) != NULL);
367                     tm.tm_min -= 5;
368                     tm.tm_isdst = -1;
369                     REL_CHECK("5 minutes ago", now, tm);
370 
371                     ATF_CHECK(localtime_r(&now, &tm) != NULL);
372                     tm.tm_hour++;
373                     tm.tm_min += 37;
374                     tm.tm_isdst = -1;
375                     REL_CHECK("97 minutes", now, tm);
376 
377                     ATF_CHECK(localtime_r(&now, &tm) != NULL);
378                     tm.tm_mon++;
379                     if (tm.tm_mon == 1 &&
380                         tm.tm_mday > 28 + isleap(tm.tm_year))
381                               tm.tm_mday = 28 + isleap(tm.tm_year);
382                     else if ((tm.tm_mon == 3 || tm.tm_mon == 5 ||
383                         tm.tm_mon == 8 || tm.tm_mon == 10) && tm.tm_mday == 31)
384                               tm.tm_mday = 30;
385                     tm.tm_isdst = -1;
386                     REL_CHECK("month", now, tm);
387 
388                     ATF_CHECK(localtime_r(&now, &tm) != NULL);
389                     tm.tm_mon += 2;               /* "next" means add 2 ... */
390                     if (tm.tm_mon == 13 &&
391                         tm.tm_mday > 28 + isleap(tm.tm_year + 1))
392                               tm.tm_mday = 28 + isleap(tm.tm_year + 1);
393                     else if (tm.tm_mon == 8 && tm.tm_mday == 31)
394                               tm.tm_mday = 30;
395                     tm.tm_isdst = -1;
396                     REL_CHECK("next month", now, tm);
397 
398                     ATF_CHECK(localtime_r(&now, &tm) != NULL);
399                     tm.tm_mon--;
400                     if (tm.tm_mon == 1 &&
401                         tm.tm_mday > 28 + isleap(tm.tm_year))
402                               tm.tm_mday = 28 + isleap(tm.tm_year);
403                     else if ((tm.tm_mon == 3 || tm.tm_mon == 5 ||
404                         tm.tm_mon == 8 || tm.tm_mon == 10) && tm.tm_mday == 31)
405                               tm.tm_mday = 30;
406                     tm.tm_isdst = -1;
407                     REL_CHECK("last month", now, tm);
408 
409                     ATF_CHECK(localtime_r(&now, &tm) != NULL);
410                     tm.tm_mon += 6;
411                     if (tm.tm_mon == 13 &&
412                         tm.tm_mday > 28 + isleap(tm.tm_year + 1))
413                               tm.tm_mday = 28 + isleap(tm.tm_year + 1);
414                     else if ((tm.tm_mon == 15 || tm.tm_mon == 17 ||
415                         tm.tm_mon == 8 || tm.tm_mon == 10) && tm.tm_mday == 31)
416                               tm.tm_mday = 30;
417                     tm.tm_mday += 2;
418                     tm.tm_isdst = -1;
419                     REL_CHECK("+6 months 2 days", now, tm);
420 
421                     ATF_CHECK(localtime_r(&now, &tm) != NULL);
422                     tm.tm_mon -= 9;
423                     if (tm.tm_mon == 1 && tm.tm_mday > 28 + isleap(tm.tm_year))
424                               tm.tm_mday = 28 + isleap(tm.tm_year);
425                     else if ((tm.tm_mon == -9 || tm.tm_mon == -7 ||
426                         tm.tm_mon == -2) && tm.tm_mday == 31)
427                               tm.tm_mday = 30;
428                     tm.tm_isdst = -1;
429                     REL_CHECK("9 months ago", now, tm);
430 
431                     ATF_CHECK(localtime_r(&now, &tm) != NULL);
432                     if (tm.tm_wday <= 2)
433                               tm.tm_mday -= 7;
434                     tm.tm_mday += 2 - tm.tm_wday;
435                     tm.tm_isdst = -1;
436                     tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
437                     REL_CHECK("1 week ago Tu", now, tm);
438 
439                     ATF_CHECK(localtime_r(&now, &tm) != NULL);
440                     tm.tm_isdst = -1;
441                     tm.tm_mday++;
442                     tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
443                     REL_CHECK("midnight tomorrow", now, tm);
444 
445                     ATF_CHECK(localtime_r(&now, &tm) != NULL);
446                     tm.tm_isdst = -1;
447                     tm.tm_mday++;
448                     tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
449                     REL_CHECK("tomorrow midnight", now, tm);
450 
451                     ATF_CHECK(localtime_r(&now, &tm) != NULL);
452                     tm.tm_isdst = -1;
453                     tm.tm_mday++;
454                     tm.tm_hour = 12;
455                     tm.tm_min = tm.tm_sec = 0;
456                     REL_CHECK("noon tomorrow", now, tm);
457 
458                     ATF_CHECK(localtime_r(&now, &tm) != NULL);
459                     if (tm.tm_wday > 2)
460                               tm.tm_mday += 7;
461                     tm.tm_mday += 2 - tm.tm_wday;
462                     tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
463                     tm.tm_isdst = -1;
464                     REL_CHECK("midnight Tuesday", now, tm);
465 
466                     ATF_CHECK(localtime_r(&now, &tm) != NULL);
467                     if (tm.tm_wday > 2 + 1)
468                               tm.tm_mday += 7;
469                     tm.tm_mday += 2 - tm.tm_wday;
470                     tm.tm_mday++;       /* xxx midnight --> the next day */
471                     tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
472                     tm.tm_isdst = -1;
473                     REL_CHECK("Tuesday midnight", now, tm);
474           }
475 }
476 
477 ATF_TC(atsecs);
478 
ATF_TC_HEAD(atsecs,tc)479 ATF_TC_HEAD(atsecs, tc)
480 {
481           atf_tc_set_md_var(tc, "descr", "Test seconds past the epoch");
482 }
483 
ATF_TC_BODY(atsecs,tc)484 ATF_TC_BODY(atsecs, tc)
485 {
486           int tzoff;
487 
488           /* "@0" -> (time_t)0, regardless of timezone */
489           ATF_CHECK(parsedate("@0", NULL, NULL) == (time_t)0);
490           putenv(__UNCONST("TZ=Europe/Berlin"));
491           tzset();
492           ATF_CHECK(parsedate("@0", NULL, NULL) == (time_t)0);
493           putenv(__UNCONST("TZ=America/New_York"));
494           tzset();
495           ATF_CHECK(parsedate("@0", NULL, NULL) == (time_t)0);
496           tzoff = 0;
497           ATF_CHECK(parsedate("@0", NULL, &tzoff) == (time_t)0);
498           tzoff = 3600;
499           ATF_CHECK(parsedate("@0", NULL, &tzoff) == (time_t)0);
500           tzoff = -3600;
501           ATF_CHECK(parsedate("@0", NULL, &tzoff) == (time_t)0);
502 
503           /* -1 or other negative numbers are not errors */
504           errno = 0;
505           ATF_CHECK(parsedate("@-1", NULL, &tzoff) == (time_t)-1 && errno == 0);
506           ATF_CHECK(parsedate("@-2", NULL, &tzoff) == (time_t)-2 && errno == 0);
507 
508           /* junk is an error */
509           errno = 0;
510           ATF_CHECK(parsedate("@junk", NULL, NULL) == (time_t)-1 && errno != 0);
511 }
512 
513 ATF_TC(zones);
514 
ATF_TC_HEAD(zones,tc)515 ATF_TC_HEAD(zones, tc)
516 {
517           atf_tc_set_md_var(tc, "descr", "Test parsing dates with zones");
518 }
519 
ATF_TC_BODY(zones,tc)520 ATF_TC_BODY(zones, tc)
521 {
522           parsecheck("2015-12-06 16:11:48 UTC", NULL, NULL, gmtime_r,
523                     2015, 12, 6, 16, 11, 48);
524           parsecheck("2015-12-06 16:11:48 UT", NULL, NULL, gmtime_r,
525                     2015, 12, 6, 16, 11, 48);
526           parsecheck("2015-12-06 16:11:48 GMT", NULL, NULL, gmtime_r,
527                     2015, 12, 6, 16, 11, 48);
528           parsecheck("2015-12-06 16:11:48 +0000", NULL, NULL, gmtime_r,
529                     2015, 12, 6, 16, 11, 48);
530 
531           parsecheck("2015-12-06 16:11:48 -0500", NULL, NULL, gmtime_r,
532                     2015, 12, 6, 21, 11, 48);
533           parsecheck("2015-12-06 16:11:48 EST", NULL, NULL, gmtime_r,
534                     2015, 12, 6, 21, 11, 48);
535           parsecheck("2015-12-06 16:11:48 EDT", NULL, NULL, gmtime_r,
536                     2015, 12, 6, 20, 11, 48);
537           parsecheck("2015-12-06 16:11:48 +0500", NULL, NULL, gmtime_r,
538                     2015, 12, 6, 11, 11, 48);
539 
540           parsecheck("2015-12-06 16:11:48 +1000", NULL, NULL, gmtime_r,
541                     2015, 12, 6, 6, 11, 48);
542           parsecheck("2015-12-06 16:11:48 AEST", NULL, NULL, gmtime_r,
543                     2015, 12, 6, 6, 11, 48);
544           parsecheck("2015-12-06 16:11:48 -1000", NULL, NULL, gmtime_r,
545                     2015, 12, 7, 2, 11, 48);
546           parsecheck("2015-12-06 16:11:48 HST", NULL, NULL, gmtime_r,
547                     2015, 12, 7, 2, 11, 48);
548 
549           parsecheck("2015-12-06 16:11:48 AWST", NULL, NULL, gmtime_r,
550                     2015, 12, 6, 8, 11, 48);
551           parsecheck("2015-12-06 16:11:48 NZDT", NULL, NULL, gmtime_r,
552                     2015, 12, 6, 3, 11, 48);
553 
554         parsecheck("Sun, 6 Dec 2015 09:43:16 -0500", NULL, NULL, gmtime_r,
555                     2015, 12, 6, 14, 43, 16);
556           parsecheck("Mon Dec  7 03:13:31 ICT 2015", NULL, NULL, gmtime_r,
557                     2015, 12, 6, 20, 13, 31);
558           /* the day name is ignored when a day of month (etc) is given... */
559           parsecheck("Sat Dec  7 03:13:31 ICT 2015", NULL, NULL, gmtime_r,
560                     2015, 12, 6, 20, 13, 31);
561 
562 
563           parsecheck("2015-12-06 12:00:00 IDLW", NULL, NULL, gmtime_r,
564                     2015, 12, 7, 0, 0, 0);
565           parsecheck("2015-12-06 12:00:00 IDLE", NULL, NULL, gmtime_r,
566                     2015, 12, 6, 0, 0, 0);
567 
568           parsecheck("2015-12-06 21:17:33 NFT", NULL, NULL, gmtime_r,
569                     2015, 12, 7, 0, 47, 33);
570           parsecheck("2015-12-06 21:17:33 ACST", NULL, NULL, gmtime_r,
571                     2015, 12, 6, 11, 47, 33);
572           parsecheck("2015-12-06 21:17:33 +0717", NULL, NULL, gmtime_r,
573                     2015, 12, 6, 14, 0, 33);
574 
575           parsecheck("2015-12-06 21:21:21 Z", NULL, NULL, gmtime_r,
576                     2015, 12, 6, 21, 21, 21);
577           parsecheck("2015-12-06 21:21:21 A", NULL, NULL, gmtime_r,
578                     2015, 12, 6, 22, 21, 21);
579           parsecheck("2015-12-06 21:21:21 G", NULL, NULL, gmtime_r,
580                     2015, 12, 7, 4, 21, 21);
581           parsecheck("2015-12-06 21:21:21 M", NULL, NULL, gmtime_r,
582                     2015, 12, 7, 9, 21, 21);
583           parsecheck("2015-12-06 21:21:21 N", NULL, NULL, gmtime_r,
584                     2015, 12, 6, 20, 21, 21);
585           parsecheck("2015-12-06 21:21:21 T", NULL, NULL, gmtime_r,
586                     2015, 12, 6, 14, 21, 21);
587           parsecheck("2015-12-06 21:21:21 Y", NULL, NULL, gmtime_r,
588                     2015, 12, 6, 9, 21, 21);
589 
590 }
591 
592 ATF_TC(gibberish);
593 
ATF_TC_HEAD(gibberish,tc)594 ATF_TC_HEAD(gibberish, tc)
595 {
596           atf_tc_set_md_var(tc, "descr", "Test (not) parsing nonsense");
597 }
598 
ATF_TC_BODY(gibberish,tc)599 ATF_TC_BODY(gibberish, tc)
600 {
601           errno = 0;
602           ATF_CHECK(parsedate("invalid nonsense", NULL, NULL) == (time_t)-1
603               && errno != 0);
604           errno = 0;
605           ATF_CHECK(parsedate("12th day of Christmas", NULL, NULL) == (time_t)-1
606               && errno != 0);
607           errno = 0;
608           ATF_CHECK(parsedate("2015-31-07 15:00", NULL, NULL) == (time_t)-1
609               && errno != 0);
610           errno = 0;
611           ATF_CHECK(parsedate("2015-02-29 10:01", NULL, NULL) == (time_t)-1
612               && errno != 0);
613           errno = 0;
614           ATF_CHECK(parsedate("2015-12-06 24:01", NULL, NULL) == (time_t)-1
615               && errno != 0);
616           errno = 0;
617           ATF_CHECK(parsedate("2015-12-06 14:61", NULL, NULL) == (time_t)-1
618               && errno != 0);
619 }
620 
ATF_TP_ADD_TCS(tp)621 ATF_TP_ADD_TCS(tp)
622 {
623           setenv("TZ", "UTC", 1);
624           tzset();
625           ATF_TP_ADD_TC(tp, dates);
626           ATF_TP_ADD_TC(tp, times);
627           ATF_TP_ADD_TC(tp, dsttimes);
628           ATF_TP_ADD_TC(tp, relative);
629           ATF_TP_ADD_TC(tp, atsecs);
630           ATF_TP_ADD_TC(tp, zones);
631           ATF_TP_ADD_TC(tp, gibberish);
632 
633           return atf_no_error();
634 }
635 
636