1 /* Test of parse_datetime() function.
2 Copyright (C) 2008-2011 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, write to the Free Software Foundation,
16 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
18 /* Written by Simon Josefsson <simon@josefsson.org>, 2008. */
22 #include "parse-datetime.h"
32 #define LOG(str, now, res) \
33 printf ("string `%s' diff %d %d\n", \
34 str, res.tv_sec - now.tv_sec, res.tv_nsec - now.tv_nsec);
36 #define LOG(str, now, res) (void) 0
39 static const char *const day_table[] =
53 /* Shift A right by B bits portably, by dividing A by 2**B and
54 truncating towards minus infinity. A and B should be free of side
55 effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
56 INT_BITS is the number of useful bits in an int. GNU code can
57 assume that INT_BITS is at least 32.
59 ISO C99 says that A >> B is implementation-defined if A < 0. Some
60 implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
61 right in the usual way when A < 0, so SHR falls back on division if
62 ordinary A >> B doesn't seem to be the usual signed shift. */
66 : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
68 #define TM_YEAR_BASE 1900
70 /* Yield the difference between *A and *B,
71 measured in seconds, ignoring leap seconds.
72 The body of this function is taken directly from the GNU C Library;
73 see src/strftime.c. */
75 tm_diff (struct tm const *a, struct tm const *b)
77 /* Compute intervening leap days correctly even if year is negative.
78 Take care to avoid int overflow in leap day calculations. */
79 int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3);
80 int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3);
81 int a100 = a4 / 25 - (a4 % 25 < 0);
82 int b100 = b4 / 25 - (b4 % 25 < 0);
83 int a400 = SHR (a100, 2);
84 int b400 = SHR (b100, 2);
85 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
86 long int ayear = a->tm_year;
87 long int years = ayear - b->tm_year;
88 long int days = (365 * years + intervening_leap_days
89 + (a->tm_yday - b->tm_yday));
90 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
91 + (a->tm_min - b->tm_min))
92 + (a->tm_sec - b->tm_sec));
94 #endif /* ! HAVE_TM_GMTOFF */
105 struct tm tm_local = *localtime (&now);
106 struct tm tm_gmt = *gmtime (&now);
108 gmtoff = tm_diff (&tm_local, &tm_gmt);
110 gmtoff = localtime (&now)->tm_gmtoff;
117 main (int argc _GL_UNUSED, char **argv)
119 struct timespec result;
120 struct timespec result2;
121 struct timespec expected;
127 set_program_name (argv[0]);
129 gmtoff = gmt_offset ();
132 /* ISO 8601 extended date and time of day representation,
133 'T' separator, local time zone */
134 p = "2011-05-01T11:55:18";
135 expected.tv_sec = 1304250918 - gmtoff;
136 expected.tv_nsec = 0;
137 ASSERT (parse_datetime (&result, p, 0));
138 LOG (p, expected, result);
139 ASSERT (expected.tv_sec == result.tv_sec
140 && expected.tv_nsec == result.tv_nsec);
142 /* ISO 8601 extended date and time of day representation,
143 ' ' separator, local time zone */
144 p = "2011-05-01 11:55:18";
145 expected.tv_sec = 1304250918 - gmtoff;
146 expected.tv_nsec = 0;
147 ASSERT (parse_datetime (&result, p, 0));
148 LOG (p, expected, result);
149 ASSERT (expected.tv_sec == result.tv_sec
150 && expected.tv_nsec == result.tv_nsec);
153 /* ISO 8601, extended date and time of day representation,
154 'T' separator, UTC */
155 p = "2011-05-01T11:55:18Z";
156 expected.tv_sec = 1304250918;
157 expected.tv_nsec = 0;
158 ASSERT (parse_datetime (&result, p, 0));
159 LOG (p, expected, result);
160 ASSERT (expected.tv_sec == result.tv_sec
161 && expected.tv_nsec == result.tv_nsec);
163 /* ISO 8601, extended date and time of day representation,
164 ' ' separator, UTC */
165 p = "2011-05-01 11:55:18Z";
166 expected.tv_sec = 1304250918;
167 expected.tv_nsec = 0;
168 ASSERT (parse_datetime (&result, p, 0));
169 LOG (p, expected, result);
170 ASSERT (expected.tv_sec == result.tv_sec
171 && expected.tv_nsec == result.tv_nsec);
174 /* ISO 8601 extended date and time of day representation,
175 'T' separator, w/UTC offset */
176 p = "2011-05-01T11:55:18-07:00";
177 expected.tv_sec = 1304276118;
178 expected.tv_nsec = 0;
179 ASSERT (parse_datetime (&result, p, 0));
180 LOG (p, expected, result);
181 ASSERT (expected.tv_sec == result.tv_sec
182 && expected.tv_nsec == result.tv_nsec);
184 /* ISO 8601 extended date and time of day representation,
185 ' ' separator, w/UTC offset */
186 p = "2011-05-01 11:55:18-07:00";
187 expected.tv_sec = 1304276118;
188 expected.tv_nsec = 0;
189 ASSERT (parse_datetime (&result, p, 0));
190 LOG (p, expected, result);
191 ASSERT (expected.tv_sec == result.tv_sec
192 && expected.tv_nsec == result.tv_nsec);
195 /* ISO 8601 extended date and time of day representation,
196 'T' separator, w/hour only UTC offset */
197 p = "2011-05-01T11:55:18-07";
198 expected.tv_sec = 1304276118;
199 expected.tv_nsec = 0;
200 ASSERT (parse_datetime (&result, p, 0));
201 LOG (p, expected, result);
202 ASSERT (expected.tv_sec == result.tv_sec
203 && expected.tv_nsec == result.tv_nsec);
205 /* ISO 8601 extended date and time of day representation,
206 ' ' separator, w/hour only UTC offset */
207 p = "2011-05-01 11:55:18-07";
208 expected.tv_sec = 1304276118;
209 expected.tv_nsec = 0;
210 ASSERT (parse_datetime (&result, p, 0));
211 LOG (p, expected, result);
212 ASSERT (expected.tv_sec == result.tv_sec
213 && expected.tv_nsec == result.tv_nsec);
219 ASSERT (parse_datetime (&result, p, &now));
220 LOG (p, now, result);
221 ASSERT (now.tv_sec == result.tv_sec && now.tv_nsec == result.tv_nsec);
226 ASSERT (parse_datetime (&result, p, &now));
227 LOG (p, now, result);
228 ASSERT (now.tv_sec + 24 * 60 * 60 == result.tv_sec
229 && now.tv_nsec == result.tv_nsec);
234 ASSERT (parse_datetime (&result, p, &now));
235 LOG (p, now, result);
236 ASSERT (now.tv_sec - 24 * 60 * 60 == result.tv_sec
237 && now.tv_nsec == result.tv_nsec);
242 ASSERT (parse_datetime (&result, p, &now));
243 LOG (p, now, result);
244 ASSERT (now.tv_sec + 4 * 60 * 60 == result.tv_sec
245 && now.tv_nsec == result.tv_nsec);
247 /* test if timezone is not being ignored for day offset */
250 p = "UTC+400 +24 hours";
251 ASSERT (parse_datetime (&result, p, &now));
252 LOG (p, now, result);
253 p = "UTC+400 +1 day";
254 ASSERT (parse_datetime (&result2, p, &now));
255 LOG (p, now, result2);
256 ASSERT (result.tv_sec == result2.tv_sec
257 && result.tv_nsec == result2.tv_nsec);
259 /* test if several time zones formats are handled same way */
263 ASSERT (parse_datetime (&result, p, &now));
264 LOG (p, now, result);
266 ASSERT (parse_datetime (&result2, p, &now));
267 LOG (p, now, result2);
268 ASSERT (result.tv_sec == result2.tv_sec
269 && result.tv_nsec == result2.tv_nsec);
271 ASSERT (parse_datetime (&result2, p, &now));
272 LOG (p, now, result2);
273 ASSERT (result.tv_sec == result2.tv_sec
274 && result.tv_nsec == result2.tv_nsec);
279 ASSERT (parse_datetime (&result, p, &now));
280 LOG (p, now, result);
282 ASSERT (parse_datetime (&result2, p, &now));
283 LOG (p, now, result2);
284 ASSERT (result.tv_sec == result2.tv_sec
285 && result.tv_nsec == result2.tv_nsec);
287 ASSERT (parse_datetime (&result2, p, &now));
288 LOG (p, now, result2);
289 ASSERT (result.tv_sec == result2.tv_sec
290 && result.tv_nsec == result2.tv_nsec);
295 ASSERT (parse_datetime (&result, p, &now));
296 LOG (p, now, result);
298 ASSERT (parse_datetime (&result2, p, &now));
299 LOG (p, now, result2);
300 ASSERT (result.tv_sec == result2.tv_sec
301 && result.tv_nsec == result2.tv_nsec);
306 ASSERT (parse_datetime (&result, p, &now));
307 LOG (p, now, result);
309 ASSERT (parse_datetime (&result2, p, &now));
310 LOG (p, now, result2);
311 ASSERT (result.tv_sec == result2.tv_sec
312 && result.tv_nsec == result2.tv_nsec);
315 /* TZ out of range should cause parse_datetime failure */
319 ASSERT (!parse_datetime (&result, p, &now));
321 /* Check for several invalid countable dayshifts */
324 p = "UTC+4:00 +40 yesterday";
325 ASSERT (!parse_datetime (&result, p, &now));
326 p = "UTC+4:00 next yesterday";
327 ASSERT (!parse_datetime (&result, p, &now));
328 p = "UTC+4:00 tomorrow ago";
329 ASSERT (!parse_datetime (&result, p, &now));
330 p = "UTC+4:00 tomorrow hence";
331 ASSERT (!parse_datetime (&result, p, &now));
332 p = "UTC+4:00 40 now ago";
333 ASSERT (!parse_datetime (&result, p, &now));
334 p = "UTC+4:00 last tomorrow";
335 ASSERT (!parse_datetime (&result, p, &now));
336 p = "UTC+4:00 -4 today";
337 ASSERT (!parse_datetime (&result, p, &now));
339 /* And check correct usage of dayshifts */
342 p = "UTC+400 tomorrow";
343 ASSERT (parse_datetime (&result, p, &now));
344 LOG (p, now, result);
345 p = "UTC+400 +1 day";
346 ASSERT (parse_datetime (&result2, p, &now));
347 LOG (p, now, result2);
348 ASSERT (result.tv_sec == result2.tv_sec
349 && result.tv_nsec == result2.tv_nsec);
350 p = "UTC+400 1 day hence";
351 ASSERT (parse_datetime (&result2, p, &now));
352 LOG (p, now, result2);
353 ASSERT (result.tv_sec == result2.tv_sec
354 && result.tv_nsec == result2.tv_nsec);
357 p = "UTC+400 yesterday";
358 ASSERT (parse_datetime (&result, p, &now));
359 LOG (p, now, result);
360 p = "UTC+400 1 day ago";
361 ASSERT (parse_datetime (&result2, p, &now));
362 LOG (p, now, result2);
363 ASSERT (result.tv_sec == result2.tv_sec
364 && result.tv_nsec == result2.tv_nsec);
368 ASSERT (parse_datetime (&result, p, &now));
369 LOG (p, now, result);
370 p = "UTC+400 +0 minutes"; /* silly, but simple "UTC+400" is different*/
371 ASSERT (parse_datetime (&result2, p, &now));
372 LOG (p, now, result2);
373 ASSERT (result.tv_sec == result2.tv_sec
374 && result.tv_nsec == result2.tv_nsec);
376 /* Check that some "next Monday", "last Wednesday", etc. are correct. */
377 setenv ("TZ", "UTC0", 1);
378 for (i = 0; day_table[i]; i++)
380 unsigned int thur2 = 7 * 24 * 3600; /* 2nd thursday */
382 sprintf (tmp, "NEXT %s", day_table[i]);
383 now.tv_sec = thur2 + 4711;
385 ASSERT (parse_datetime (&result, tmp, &now));
386 LOG (tmp, now, result);
387 ASSERT (result.tv_nsec == 0);
388 ASSERT (result.tv_sec == thur2 + (i == 4 ? 7 : (i + 3) % 7) * 24 * 3600);
390 sprintf (tmp, "LAST %s", day_table[i]);
391 now.tv_sec = thur2 + 4711;
393 ASSERT (parse_datetime (&result, tmp, &now));
394 LOG (tmp, now, result);
395 ASSERT (result.tv_nsec == 0);
396 ASSERT (result.tv_sec == thur2 + ((i + 3) % 7 - 7) * 24 * 3600);
399 p = "THURSDAY UTC+00"; /* The epoch was on Thursday. */
402 ASSERT (parse_datetime (&result, p, &now));
403 LOG (p, now, result);
404 ASSERT (result.tv_sec == now.tv_sec
405 && result.tv_nsec == now.tv_nsec);
410 ASSERT (parse_datetime (&result, p, &now));
411 LOG (p, now, result);
412 ASSERT (result.tv_sec == 24 * 3600
413 && result.tv_nsec == now.tv_nsec);