1 /* Test of parse_datetime() function.
2 Copyright (C) 2008-2012 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, see <http://www.gnu.org/licenses/>. */
17 /* Written by Simon Josefsson <simon@josefsson.org>, 2008. */
21 #include "parse-datetime.h"
31 #define LOG(str, now, res) \
32 printf ("string '%s' diff %d %d\n", \
33 str, res.tv_sec - now.tv_sec, res.tv_nsec - now.tv_nsec);
35 #define LOG(str, now, res) (void) 0
38 static const char *const day_table[] =
52 /* Shift A right by B bits portably, by dividing A by 2**B and
53 truncating towards minus infinity. A and B should be free of side
54 effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
55 INT_BITS is the number of useful bits in an int. GNU code can
56 assume that INT_BITS is at least 32.
58 ISO C99 says that A >> B is implementation-defined if A < 0. Some
59 implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
60 right in the usual way when A < 0, so SHR falls back on division if
61 ordinary A >> B doesn't seem to be the usual signed shift. */
65 : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
67 #define TM_YEAR_BASE 1900
69 /* Yield the difference between *A and *B,
70 measured in seconds, ignoring leap seconds.
71 The body of this function is taken directly from the GNU C Library;
72 see src/strftime.c. */
74 tm_diff (struct tm const *a, struct tm const *b)
76 /* Compute intervening leap days correctly even if year is negative.
77 Take care to avoid int overflow in leap day calculations. */
78 int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3);
79 int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3);
80 int a100 = a4 / 25 - (a4 % 25 < 0);
81 int b100 = b4 / 25 - (b4 % 25 < 0);
82 int a400 = SHR (a100, 2);
83 int b400 = SHR (b100, 2);
84 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
85 long int ayear = a->tm_year;
86 long int years = ayear - b->tm_year;
87 long int days = (365 * years + intervening_leap_days
88 + (a->tm_yday - b->tm_yday));
89 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
90 + (a->tm_min - b->tm_min))
91 + (a->tm_sec - b->tm_sec));
93 #endif /* ! HAVE_TM_GMTOFF */
101 struct tm tm_local = *localtime (&s);
102 struct tm tm_gmt = *gmtime (&s);
104 gmtoff = tm_diff (&tm_local, &tm_gmt);
106 gmtoff = localtime (&s)->tm_gmtoff;
113 main (int argc _GL_UNUSED, char **argv)
115 struct timespec result;
116 struct timespec result2;
117 struct timespec expected;
122 time_t ref_time = 1304250918;
124 set_program_name (argv[0]);
126 /* Set the time zone to US Eastern time with the 2012 rules. This
127 should disable any leap second support. Otherwise, there will be
128 a problem with glibc on sites that default to leap seconds; see
129 <http://bugs.gnu.org/12206>. */
130 setenv ("TZ", "EST5EDT,M3.2.0,M11.1.0", 1);
132 gmtoff = gmt_offset (ref_time);
135 /* ISO 8601 extended date and time of day representation,
136 'T' separator, local time zone */
137 p = "2011-05-01T11:55:18";
138 expected.tv_sec = ref_time - gmtoff;
139 expected.tv_nsec = 0;
140 ASSERT (parse_datetime (&result, p, 0));
141 LOG (p, expected, result);
142 ASSERT (expected.tv_sec == result.tv_sec
143 && expected.tv_nsec == result.tv_nsec);
145 /* ISO 8601 extended date and time of day representation,
146 ' ' separator, local time zone */
147 p = "2011-05-01 11:55:18";
148 expected.tv_sec = ref_time - gmtoff;
149 expected.tv_nsec = 0;
150 ASSERT (parse_datetime (&result, p, 0));
151 LOG (p, expected, result);
152 ASSERT (expected.tv_sec == result.tv_sec
153 && expected.tv_nsec == result.tv_nsec);
156 /* ISO 8601, extended date and time of day representation,
157 'T' separator, UTC */
158 p = "2011-05-01T11:55:18Z";
159 expected.tv_sec = ref_time;
160 expected.tv_nsec = 0;
161 ASSERT (parse_datetime (&result, p, 0));
162 LOG (p, expected, result);
163 ASSERT (expected.tv_sec == result.tv_sec
164 && expected.tv_nsec == result.tv_nsec);
166 /* ISO 8601, extended date and time of day representation,
167 ' ' separator, UTC */
168 p = "2011-05-01 11:55:18Z";
169 expected.tv_sec = ref_time;
170 expected.tv_nsec = 0;
171 ASSERT (parse_datetime (&result, p, 0));
172 LOG (p, expected, result);
173 ASSERT (expected.tv_sec == result.tv_sec
174 && expected.tv_nsec == result.tv_nsec);
177 /* ISO 8601 extended date and time of day representation,
178 'T' separator, w/UTC offset */
179 p = "2011-05-01T11:55:18-07:00";
180 expected.tv_sec = 1304276118;
181 expected.tv_nsec = 0;
182 ASSERT (parse_datetime (&result, p, 0));
183 LOG (p, expected, result);
184 ASSERT (expected.tv_sec == result.tv_sec
185 && expected.tv_nsec == result.tv_nsec);
187 /* ISO 8601 extended date and time of day representation,
188 ' ' separator, w/UTC offset */
189 p = "2011-05-01 11:55:18-07:00";
190 expected.tv_sec = 1304276118;
191 expected.tv_nsec = 0;
192 ASSERT (parse_datetime (&result, p, 0));
193 LOG (p, expected, result);
194 ASSERT (expected.tv_sec == result.tv_sec
195 && expected.tv_nsec == result.tv_nsec);
198 /* ISO 8601 extended date and time of day representation,
199 'T' separator, w/hour only UTC offset */
200 p = "2011-05-01T11:55:18-07";
201 expected.tv_sec = 1304276118;
202 expected.tv_nsec = 0;
203 ASSERT (parse_datetime (&result, p, 0));
204 LOG (p, expected, result);
205 ASSERT (expected.tv_sec == result.tv_sec
206 && expected.tv_nsec == result.tv_nsec);
208 /* ISO 8601 extended date and time of day representation,
209 ' ' separator, w/hour only UTC offset */
210 p = "2011-05-01 11:55:18-07";
211 expected.tv_sec = 1304276118;
212 expected.tv_nsec = 0;
213 ASSERT (parse_datetime (&result, p, 0));
214 LOG (p, expected, result);
215 ASSERT (expected.tv_sec == result.tv_sec
216 && expected.tv_nsec == result.tv_nsec);
222 ASSERT (parse_datetime (&result, p, &now));
223 LOG (p, now, result);
224 ASSERT (now.tv_sec == result.tv_sec && now.tv_nsec == result.tv_nsec);
229 ASSERT (parse_datetime (&result, p, &now));
230 LOG (p, now, result);
231 ASSERT (now.tv_sec + 24 * 60 * 60 == result.tv_sec
232 && now.tv_nsec == result.tv_nsec);
237 ASSERT (parse_datetime (&result, p, &now));
238 LOG (p, now, result);
239 ASSERT (now.tv_sec - 24 * 60 * 60 == result.tv_sec
240 && now.tv_nsec == result.tv_nsec);
245 ASSERT (parse_datetime (&result, p, &now));
246 LOG (p, now, result);
247 ASSERT (now.tv_sec + 4 * 60 * 60 == result.tv_sec
248 && now.tv_nsec == result.tv_nsec);
250 /* test if timezone is not being ignored for day offset */
253 p = "UTC+400 +24 hours";
254 ASSERT (parse_datetime (&result, p, &now));
255 LOG (p, now, result);
256 p = "UTC+400 +1 day";
257 ASSERT (parse_datetime (&result2, p, &now));
258 LOG (p, now, result2);
259 ASSERT (result.tv_sec == result2.tv_sec
260 && result.tv_nsec == result2.tv_nsec);
262 /* test if several time zones formats are handled same way */
266 ASSERT (parse_datetime (&result, p, &now));
267 LOG (p, now, result);
269 ASSERT (parse_datetime (&result2, p, &now));
270 LOG (p, now, result2);
271 ASSERT (result.tv_sec == result2.tv_sec
272 && result.tv_nsec == result2.tv_nsec);
274 ASSERT (parse_datetime (&result2, p, &now));
275 LOG (p, now, result2);
276 ASSERT (result.tv_sec == result2.tv_sec
277 && result.tv_nsec == result2.tv_nsec);
282 ASSERT (parse_datetime (&result, p, &now));
283 LOG (p, now, result);
285 ASSERT (parse_datetime (&result2, p, &now));
286 LOG (p, now, result2);
287 ASSERT (result.tv_sec == result2.tv_sec
288 && result.tv_nsec == result2.tv_nsec);
290 ASSERT (parse_datetime (&result2, p, &now));
291 LOG (p, now, result2);
292 ASSERT (result.tv_sec == result2.tv_sec
293 && result.tv_nsec == result2.tv_nsec);
298 ASSERT (parse_datetime (&result, p, &now));
299 LOG (p, now, result);
301 ASSERT (parse_datetime (&result2, p, &now));
302 LOG (p, now, result2);
303 ASSERT (result.tv_sec == result2.tv_sec
304 && result.tv_nsec == result2.tv_nsec);
309 ASSERT (parse_datetime (&result, p, &now));
310 LOG (p, now, result);
312 ASSERT (parse_datetime (&result2, p, &now));
313 LOG (p, now, result2);
314 ASSERT (result.tv_sec == result2.tv_sec
315 && result.tv_nsec == result2.tv_nsec);
318 /* TZ out of range should cause parse_datetime failure */
322 ASSERT (!parse_datetime (&result, p, &now));
324 /* Check for several invalid countable dayshifts */
327 p = "UTC+4:00 +40 yesterday";
328 ASSERT (!parse_datetime (&result, p, &now));
329 p = "UTC+4:00 next yesterday";
330 ASSERT (!parse_datetime (&result, p, &now));
331 p = "UTC+4:00 tomorrow ago";
332 ASSERT (!parse_datetime (&result, p, &now));
333 p = "UTC+4:00 tomorrow hence";
334 ASSERT (!parse_datetime (&result, p, &now));
335 p = "UTC+4:00 40 now ago";
336 ASSERT (!parse_datetime (&result, p, &now));
337 p = "UTC+4:00 last tomorrow";
338 ASSERT (!parse_datetime (&result, p, &now));
339 p = "UTC+4:00 -4 today";
340 ASSERT (!parse_datetime (&result, p, &now));
342 /* And check correct usage of dayshifts */
345 p = "UTC+400 tomorrow";
346 ASSERT (parse_datetime (&result, p, &now));
347 LOG (p, now, result);
348 p = "UTC+400 +1 day";
349 ASSERT (parse_datetime (&result2, p, &now));
350 LOG (p, now, result2);
351 ASSERT (result.tv_sec == result2.tv_sec
352 && result.tv_nsec == result2.tv_nsec);
353 p = "UTC+400 1 day hence";
354 ASSERT (parse_datetime (&result2, p, &now));
355 LOG (p, now, result2);
356 ASSERT (result.tv_sec == result2.tv_sec
357 && result.tv_nsec == result2.tv_nsec);
360 p = "UTC+400 yesterday";
361 ASSERT (parse_datetime (&result, p, &now));
362 LOG (p, now, result);
363 p = "UTC+400 1 day ago";
364 ASSERT (parse_datetime (&result2, p, &now));
365 LOG (p, now, result2);
366 ASSERT (result.tv_sec == result2.tv_sec
367 && result.tv_nsec == result2.tv_nsec);
371 ASSERT (parse_datetime (&result, p, &now));
372 LOG (p, now, result);
373 p = "UTC+400 +0 minutes"; /* silly, but simple "UTC+400" is different*/
374 ASSERT (parse_datetime (&result2, p, &now));
375 LOG (p, now, result2);
376 ASSERT (result.tv_sec == result2.tv_sec
377 && result.tv_nsec == result2.tv_nsec);
379 /* Check that some "next Monday", "last Wednesday", etc. are correct. */
380 setenv ("TZ", "UTC0", 1);
381 for (i = 0; day_table[i]; i++)
383 unsigned int thur2 = 7 * 24 * 3600; /* 2nd thursday */
385 sprintf (tmp, "NEXT %s", day_table[i]);
386 now.tv_sec = thur2 + 4711;
388 ASSERT (parse_datetime (&result, tmp, &now));
389 LOG (tmp, now, result);
390 ASSERT (result.tv_nsec == 0);
391 ASSERT (result.tv_sec == thur2 + (i == 4 ? 7 : (i + 3) % 7) * 24 * 3600);
393 sprintf (tmp, "LAST %s", day_table[i]);
394 now.tv_sec = thur2 + 4711;
396 ASSERT (parse_datetime (&result, tmp, &now));
397 LOG (tmp, now, result);
398 ASSERT (result.tv_nsec == 0);
399 ASSERT (result.tv_sec == thur2 + ((i + 3) % 7 - 7) * 24 * 3600);
402 p = "THURSDAY UTC+00"; /* The epoch was on Thursday. */
405 ASSERT (parse_datetime (&result, p, &now));
406 LOG (p, now, result);
407 ASSERT (result.tv_sec == now.tv_sec
408 && result.tv_nsec == now.tv_nsec);
413 ASSERT (parse_datetime (&result, p, &now));
414 LOG (p, now, result);
415 ASSERT (result.tv_sec == 24 * 3600
416 && result.tv_nsec == now.tv_nsec);
418 /* Exercise a sign-extension bug. Before July 2012, an input
419 starting with a high-bit-set byte would be treated like "0". */
420 ASSERT ( ! parse_datetime (&result, "\xb0", &now));