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 */
102 struct tm tm_local = *localtime (&s);
103 struct tm tm_gmt = *gmtime (&s);
105 gmtoff = tm_diff (&tm_local, &tm_gmt);
107 gmtoff = localtime (&s)->tm_gmtoff;
114 main (int argc _GL_UNUSED, char **argv)
116 struct timespec result;
117 struct timespec result2;
118 struct timespec expected;
123 time_t ref_time = 1304250918;
125 set_program_name (argv[0]);
127 gmtoff = gmt_offset (ref_time);
130 /* ISO 8601 extended date and time of day representation,
131 'T' separator, local time zone */
132 p = "2011-05-01T11:55:18";
133 expected.tv_sec = ref_time - gmtoff;
134 expected.tv_nsec = 0;
135 ASSERT (parse_datetime (&result, p, 0));
136 LOG (p, expected, result);
137 ASSERT (expected.tv_sec == result.tv_sec
138 && expected.tv_nsec == result.tv_nsec);
140 /* ISO 8601 extended date and time of day representation,
141 ' ' separator, local time zone */
142 p = "2011-05-01 11:55:18";
143 expected.tv_sec = ref_time - gmtoff;
144 expected.tv_nsec = 0;
145 ASSERT (parse_datetime (&result, p, 0));
146 LOG (p, expected, result);
147 ASSERT (expected.tv_sec == result.tv_sec
148 && expected.tv_nsec == result.tv_nsec);
151 /* ISO 8601, extended date and time of day representation,
152 'T' separator, UTC */
153 p = "2011-05-01T11:55:18Z";
154 expected.tv_sec = ref_time;
155 expected.tv_nsec = 0;
156 ASSERT (parse_datetime (&result, p, 0));
157 LOG (p, expected, result);
158 ASSERT (expected.tv_sec == result.tv_sec
159 && expected.tv_nsec == result.tv_nsec);
161 /* ISO 8601, extended date and time of day representation,
162 ' ' separator, UTC */
163 p = "2011-05-01 11:55:18Z";
164 expected.tv_sec = ref_time;
165 expected.tv_nsec = 0;
166 ASSERT (parse_datetime (&result, p, 0));
167 LOG (p, expected, result);
168 ASSERT (expected.tv_sec == result.tv_sec
169 && expected.tv_nsec == result.tv_nsec);
172 /* ISO 8601 extended date and time of day representation,
173 'T' separator, w/UTC offset */
174 p = "2011-05-01T11:55:18-07:00";
175 expected.tv_sec = 1304276118;
176 expected.tv_nsec = 0;
177 ASSERT (parse_datetime (&result, p, 0));
178 LOG (p, expected, result);
179 ASSERT (expected.tv_sec == result.tv_sec
180 && expected.tv_nsec == result.tv_nsec);
182 /* ISO 8601 extended date and time of day representation,
183 ' ' separator, w/UTC offset */
184 p = "2011-05-01 11:55:18-07:00";
185 expected.tv_sec = 1304276118;
186 expected.tv_nsec = 0;
187 ASSERT (parse_datetime (&result, p, 0));
188 LOG (p, expected, result);
189 ASSERT (expected.tv_sec == result.tv_sec
190 && expected.tv_nsec == result.tv_nsec);
193 /* ISO 8601 extended date and time of day representation,
194 'T' separator, w/hour only UTC offset */
195 p = "2011-05-01T11:55:18-07";
196 expected.tv_sec = 1304276118;
197 expected.tv_nsec = 0;
198 ASSERT (parse_datetime (&result, p, 0));
199 LOG (p, expected, result);
200 ASSERT (expected.tv_sec == result.tv_sec
201 && expected.tv_nsec == result.tv_nsec);
203 /* ISO 8601 extended date and time of day representation,
204 ' ' separator, w/hour only UTC offset */
205 p = "2011-05-01 11:55:18-07";
206 expected.tv_sec = 1304276118;
207 expected.tv_nsec = 0;
208 ASSERT (parse_datetime (&result, p, 0));
209 LOG (p, expected, result);
210 ASSERT (expected.tv_sec == result.tv_sec
211 && expected.tv_nsec == result.tv_nsec);
217 ASSERT (parse_datetime (&result, p, &now));
218 LOG (p, now, result);
219 ASSERT (now.tv_sec == result.tv_sec && now.tv_nsec == result.tv_nsec);
224 ASSERT (parse_datetime (&result, p, &now));
225 LOG (p, now, result);
226 ASSERT (now.tv_sec + 24 * 60 * 60 == result.tv_sec
227 && now.tv_nsec == result.tv_nsec);
232 ASSERT (parse_datetime (&result, p, &now));
233 LOG (p, now, result);
234 ASSERT (now.tv_sec - 24 * 60 * 60 == result.tv_sec
235 && now.tv_nsec == result.tv_nsec);
240 ASSERT (parse_datetime (&result, p, &now));
241 LOG (p, now, result);
242 ASSERT (now.tv_sec + 4 * 60 * 60 == result.tv_sec
243 && now.tv_nsec == result.tv_nsec);
245 /* test if timezone is not being ignored for day offset */
248 p = "UTC+400 +24 hours";
249 ASSERT (parse_datetime (&result, p, &now));
250 LOG (p, now, result);
251 p = "UTC+400 +1 day";
252 ASSERT (parse_datetime (&result2, p, &now));
253 LOG (p, now, result2);
254 ASSERT (result.tv_sec == result2.tv_sec
255 && result.tv_nsec == result2.tv_nsec);
257 /* test if several time zones formats are handled same way */
261 ASSERT (parse_datetime (&result, p, &now));
262 LOG (p, now, result);
264 ASSERT (parse_datetime (&result2, p, &now));
265 LOG (p, now, result2);
266 ASSERT (result.tv_sec == result2.tv_sec
267 && result.tv_nsec == result2.tv_nsec);
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);
277 ASSERT (parse_datetime (&result, p, &now));
278 LOG (p, now, result);
280 ASSERT (parse_datetime (&result2, p, &now));
281 LOG (p, now, result2);
282 ASSERT (result.tv_sec == result2.tv_sec
283 && result.tv_nsec == result2.tv_nsec);
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);
293 ASSERT (parse_datetime (&result, p, &now));
294 LOG (p, now, result);
296 ASSERT (parse_datetime (&result2, p, &now));
297 LOG (p, now, result2);
298 ASSERT (result.tv_sec == result2.tv_sec
299 && result.tv_nsec == result2.tv_nsec);
304 ASSERT (parse_datetime (&result, p, &now));
305 LOG (p, now, result);
307 ASSERT (parse_datetime (&result2, p, &now));
308 LOG (p, now, result2);
309 ASSERT (result.tv_sec == result2.tv_sec
310 && result.tv_nsec == result2.tv_nsec);
313 /* TZ out of range should cause parse_datetime failure */
317 ASSERT (!parse_datetime (&result, p, &now));
319 /* Check for several invalid countable dayshifts */
322 p = "UTC+4:00 +40 yesterday";
323 ASSERT (!parse_datetime (&result, p, &now));
324 p = "UTC+4:00 next yesterday";
325 ASSERT (!parse_datetime (&result, p, &now));
326 p = "UTC+4:00 tomorrow ago";
327 ASSERT (!parse_datetime (&result, p, &now));
328 p = "UTC+4:00 tomorrow hence";
329 ASSERT (!parse_datetime (&result, p, &now));
330 p = "UTC+4:00 40 now ago";
331 ASSERT (!parse_datetime (&result, p, &now));
332 p = "UTC+4:00 last tomorrow";
333 ASSERT (!parse_datetime (&result, p, &now));
334 p = "UTC+4:00 -4 today";
335 ASSERT (!parse_datetime (&result, p, &now));
337 /* And check correct usage of dayshifts */
340 p = "UTC+400 tomorrow";
341 ASSERT (parse_datetime (&result, p, &now));
342 LOG (p, now, result);
343 p = "UTC+400 +1 day";
344 ASSERT (parse_datetime (&result2, p, &now));
345 LOG (p, now, result2);
346 ASSERT (result.tv_sec == result2.tv_sec
347 && result.tv_nsec == result2.tv_nsec);
348 p = "UTC+400 1 day hence";
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);
355 p = "UTC+400 yesterday";
356 ASSERT (parse_datetime (&result, p, &now));
357 LOG (p, now, result);
358 p = "UTC+400 1 day ago";
359 ASSERT (parse_datetime (&result2, p, &now));
360 LOG (p, now, result2);
361 ASSERT (result.tv_sec == result2.tv_sec
362 && result.tv_nsec == result2.tv_nsec);
366 ASSERT (parse_datetime (&result, p, &now));
367 LOG (p, now, result);
368 p = "UTC+400 +0 minutes"; /* silly, but simple "UTC+400" is different*/
369 ASSERT (parse_datetime (&result2, p, &now));
370 LOG (p, now, result2);
371 ASSERT (result.tv_sec == result2.tv_sec
372 && result.tv_nsec == result2.tv_nsec);
374 /* Check that some "next Monday", "last Wednesday", etc. are correct. */
375 setenv ("TZ", "UTC0", 1);
376 for (i = 0; day_table[i]; i++)
378 unsigned int thur2 = 7 * 24 * 3600; /* 2nd thursday */
380 sprintf (tmp, "NEXT %s", day_table[i]);
381 now.tv_sec = thur2 + 4711;
383 ASSERT (parse_datetime (&result, tmp, &now));
384 LOG (tmp, now, result);
385 ASSERT (result.tv_nsec == 0);
386 ASSERT (result.tv_sec == thur2 + (i == 4 ? 7 : (i + 3) % 7) * 24 * 3600);
388 sprintf (tmp, "LAST %s", day_table[i]);
389 now.tv_sec = thur2 + 4711;
391 ASSERT (parse_datetime (&result, tmp, &now));
392 LOG (tmp, now, result);
393 ASSERT (result.tv_nsec == 0);
394 ASSERT (result.tv_sec == thur2 + ((i + 3) % 7 - 7) * 24 * 3600);
397 p = "THURSDAY UTC+00"; /* The epoch was on Thursday. */
400 ASSERT (parse_datetime (&result, p, &now));
401 LOG (p, now, result);
402 ASSERT (result.tv_sec == now.tv_sec
403 && result.tv_nsec == now.tv_nsec);
408 ASSERT (parse_datetime (&result, p, &now));
409 LOG (p, now, result);
410 ASSERT (result.tv_sec == 24 * 3600
411 && result.tv_nsec == now.tv_nsec);