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 gmtoff = gmt_offset (ref_time);
129 /* ISO 8601 extended date and time of day representation,
130 'T' separator, local time zone */
131 p = "2011-05-01T11:55:18";
132 expected.tv_sec = ref_time - gmtoff;
133 expected.tv_nsec = 0;
134 ASSERT (parse_datetime (&result, p, 0));
135 LOG (p, expected, result);
136 ASSERT (expected.tv_sec == result.tv_sec
137 && expected.tv_nsec == result.tv_nsec);
139 /* ISO 8601 extended date and time of day representation,
140 ' ' separator, local time zone */
141 p = "2011-05-01 11:55:18";
142 expected.tv_sec = ref_time - gmtoff;
143 expected.tv_nsec = 0;
144 ASSERT (parse_datetime (&result, p, 0));
145 LOG (p, expected, result);
146 ASSERT (expected.tv_sec == result.tv_sec
147 && expected.tv_nsec == result.tv_nsec);
150 /* ISO 8601, extended date and time of day representation,
151 'T' separator, UTC */
152 p = "2011-05-01T11:55:18Z";
153 expected.tv_sec = ref_time;
154 expected.tv_nsec = 0;
155 ASSERT (parse_datetime (&result, p, 0));
156 LOG (p, expected, result);
157 ASSERT (expected.tv_sec == result.tv_sec
158 && expected.tv_nsec == result.tv_nsec);
160 /* ISO 8601, extended date and time of day representation,
161 ' ' separator, UTC */
162 p = "2011-05-01 11:55:18Z";
163 expected.tv_sec = ref_time;
164 expected.tv_nsec = 0;
165 ASSERT (parse_datetime (&result, p, 0));
166 LOG (p, expected, result);
167 ASSERT (expected.tv_sec == result.tv_sec
168 && expected.tv_nsec == result.tv_nsec);
171 /* ISO 8601 extended date and time of day representation,
172 'T' separator, w/UTC offset */
173 p = "2011-05-01T11:55:18-07:00";
174 expected.tv_sec = 1304276118;
175 expected.tv_nsec = 0;
176 ASSERT (parse_datetime (&result, p, 0));
177 LOG (p, expected, result);
178 ASSERT (expected.tv_sec == result.tv_sec
179 && expected.tv_nsec == result.tv_nsec);
181 /* ISO 8601 extended date and time of day representation,
182 ' ' separator, w/UTC offset */
183 p = "2011-05-01 11:55:18-07:00";
184 expected.tv_sec = 1304276118;
185 expected.tv_nsec = 0;
186 ASSERT (parse_datetime (&result, p, 0));
187 LOG (p, expected, result);
188 ASSERT (expected.tv_sec == result.tv_sec
189 && expected.tv_nsec == result.tv_nsec);
192 /* ISO 8601 extended date and time of day representation,
193 'T' separator, w/hour only UTC offset */
194 p = "2011-05-01T11:55:18-07";
195 expected.tv_sec = 1304276118;
196 expected.tv_nsec = 0;
197 ASSERT (parse_datetime (&result, p, 0));
198 LOG (p, expected, result);
199 ASSERT (expected.tv_sec == result.tv_sec
200 && expected.tv_nsec == result.tv_nsec);
202 /* ISO 8601 extended date and time of day representation,
203 ' ' separator, w/hour only UTC offset */
204 p = "2011-05-01 11:55:18-07";
205 expected.tv_sec = 1304276118;
206 expected.tv_nsec = 0;
207 ASSERT (parse_datetime (&result, p, 0));
208 LOG (p, expected, result);
209 ASSERT (expected.tv_sec == result.tv_sec
210 && expected.tv_nsec == result.tv_nsec);
216 ASSERT (parse_datetime (&result, p, &now));
217 LOG (p, now, result);
218 ASSERT (now.tv_sec == result.tv_sec && now.tv_nsec == result.tv_nsec);
223 ASSERT (parse_datetime (&result, p, &now));
224 LOG (p, now, result);
225 ASSERT (now.tv_sec + 24 * 60 * 60 == result.tv_sec
226 && now.tv_nsec == result.tv_nsec);
231 ASSERT (parse_datetime (&result, p, &now));
232 LOG (p, now, result);
233 ASSERT (now.tv_sec - 24 * 60 * 60 == result.tv_sec
234 && now.tv_nsec == result.tv_nsec);
239 ASSERT (parse_datetime (&result, p, &now));
240 LOG (p, now, result);
241 ASSERT (now.tv_sec + 4 * 60 * 60 == result.tv_sec
242 && now.tv_nsec == result.tv_nsec);
244 /* test if timezone is not being ignored for day offset */
247 p = "UTC+400 +24 hours";
248 ASSERT (parse_datetime (&result, p, &now));
249 LOG (p, now, result);
250 p = "UTC+400 +1 day";
251 ASSERT (parse_datetime (&result2, p, &now));
252 LOG (p, now, result2);
253 ASSERT (result.tv_sec == result2.tv_sec
254 && result.tv_nsec == result2.tv_nsec);
256 /* test if several time zones formats are handled same way */
260 ASSERT (parse_datetime (&result, p, &now));
261 LOG (p, now, result);
263 ASSERT (parse_datetime (&result2, p, &now));
264 LOG (p, now, result2);
265 ASSERT (result.tv_sec == result2.tv_sec
266 && result.tv_nsec == result2.tv_nsec);
268 ASSERT (parse_datetime (&result2, p, &now));
269 LOG (p, now, result2);
270 ASSERT (result.tv_sec == result2.tv_sec
271 && result.tv_nsec == result2.tv_nsec);
276 ASSERT (parse_datetime (&result, p, &now));
277 LOG (p, now, result);
279 ASSERT (parse_datetime (&result2, p, &now));
280 LOG (p, now, result2);
281 ASSERT (result.tv_sec == result2.tv_sec
282 && result.tv_nsec == result2.tv_nsec);
284 ASSERT (parse_datetime (&result2, p, &now));
285 LOG (p, now, result2);
286 ASSERT (result.tv_sec == result2.tv_sec
287 && result.tv_nsec == result2.tv_nsec);
292 ASSERT (parse_datetime (&result, p, &now));
293 LOG (p, now, result);
295 ASSERT (parse_datetime (&result2, p, &now));
296 LOG (p, now, result2);
297 ASSERT (result.tv_sec == result2.tv_sec
298 && result.tv_nsec == result2.tv_nsec);
303 ASSERT (parse_datetime (&result, p, &now));
304 LOG (p, now, result);
306 ASSERT (parse_datetime (&result2, p, &now));
307 LOG (p, now, result2);
308 ASSERT (result.tv_sec == result2.tv_sec
309 && result.tv_nsec == result2.tv_nsec);
312 /* TZ out of range should cause parse_datetime failure */
316 ASSERT (!parse_datetime (&result, p, &now));
318 /* Check for several invalid countable dayshifts */
321 p = "UTC+4:00 +40 yesterday";
322 ASSERT (!parse_datetime (&result, p, &now));
323 p = "UTC+4:00 next yesterday";
324 ASSERT (!parse_datetime (&result, p, &now));
325 p = "UTC+4:00 tomorrow ago";
326 ASSERT (!parse_datetime (&result, p, &now));
327 p = "UTC+4:00 tomorrow hence";
328 ASSERT (!parse_datetime (&result, p, &now));
329 p = "UTC+4:00 40 now ago";
330 ASSERT (!parse_datetime (&result, p, &now));
331 p = "UTC+4:00 last tomorrow";
332 ASSERT (!parse_datetime (&result, p, &now));
333 p = "UTC+4:00 -4 today";
334 ASSERT (!parse_datetime (&result, p, &now));
336 /* And check correct usage of dayshifts */
339 p = "UTC+400 tomorrow";
340 ASSERT (parse_datetime (&result, p, &now));
341 LOG (p, now, result);
342 p = "UTC+400 +1 day";
343 ASSERT (parse_datetime (&result2, p, &now));
344 LOG (p, now, result2);
345 ASSERT (result.tv_sec == result2.tv_sec
346 && result.tv_nsec == result2.tv_nsec);
347 p = "UTC+400 1 day hence";
348 ASSERT (parse_datetime (&result2, p, &now));
349 LOG (p, now, result2);
350 ASSERT (result.tv_sec == result2.tv_sec
351 && result.tv_nsec == result2.tv_nsec);
354 p = "UTC+400 yesterday";
355 ASSERT (parse_datetime (&result, p, &now));
356 LOG (p, now, result);
357 p = "UTC+400 1 day ago";
358 ASSERT (parse_datetime (&result2, p, &now));
359 LOG (p, now, result2);
360 ASSERT (result.tv_sec == result2.tv_sec
361 && result.tv_nsec == result2.tv_nsec);
365 ASSERT (parse_datetime (&result, p, &now));
366 LOG (p, now, result);
367 p = "UTC+400 +0 minutes"; /* silly, but simple "UTC+400" is different*/
368 ASSERT (parse_datetime (&result2, p, &now));
369 LOG (p, now, result2);
370 ASSERT (result.tv_sec == result2.tv_sec
371 && result.tv_nsec == result2.tv_nsec);
373 /* Check that some "next Monday", "last Wednesday", etc. are correct. */
374 setenv ("TZ", "UTC0", 1);
375 for (i = 0; day_table[i]; i++)
377 unsigned int thur2 = 7 * 24 * 3600; /* 2nd thursday */
379 sprintf (tmp, "NEXT %s", day_table[i]);
380 now.tv_sec = thur2 + 4711;
382 ASSERT (parse_datetime (&result, tmp, &now));
383 LOG (tmp, now, result);
384 ASSERT (result.tv_nsec == 0);
385 ASSERT (result.tv_sec == thur2 + (i == 4 ? 7 : (i + 3) % 7) * 24 * 3600);
387 sprintf (tmp, "LAST %s", day_table[i]);
388 now.tv_sec = thur2 + 4711;
390 ASSERT (parse_datetime (&result, tmp, &now));
391 LOG (tmp, now, result);
392 ASSERT (result.tv_nsec == 0);
393 ASSERT (result.tv_sec == thur2 + ((i + 3) % 7 - 7) * 24 * 3600);
396 p = "THURSDAY UTC+00"; /* The epoch was on Thursday. */
399 ASSERT (parse_datetime (&result, p, &now));
400 LOG (p, now, result);
401 ASSERT (result.tv_sec == now.tv_sec
402 && result.tv_nsec == now.tv_nsec);
407 ASSERT (parse_datetime (&result, p, &now));
408 LOG (p, now, result);
409 ASSERT (result.tv_sec == 24 * 3600
410 && result.tv_nsec == now.tv_nsec);