* verify.h (verify_true): Provide alternative definition for C++.
[gnulib.git] / lib / posixtm.c
1 /* Parse dates for touch and date.
2
3    Copyright (C) 1989, 1990, 1991, 1998, 2000, 2001, 2002, 2003, 2004, 2005
4    Free Software Foundation Inc.
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2, or (at your option)
9    any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software Foundation,
18    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
19
20 /* Yacc-based version written by Jim Kingdon and David MacKenzie.
21    Rewritten by Jim Meyering.  */
22
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
26
27 #include <stdbool.h>
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <sys/types.h>
32 #include <string.h>
33
34 #ifdef TM_IN_SYS_TIME
35 # include <sys/time.h>
36 #else
37 # include <time.h>
38 #endif
39
40 #include "posixtm.h"
41
42 #if USE_UNLOCKED_IO
43 # include "unlocked-io.h"
44 #endif
45
46 /* ISDIGIT differs from isdigit, as follows:
47    - Its arg may be any int or unsigned int; it need not be an unsigned char.
48    - It's guaranteed to evaluate its argument exactly once.
49    - It's typically faster.
50    POSIX says that only '0' through '9' are digits.  Prefer ISDIGIT to
51    ISDIGIT_LOCALE unless it's important to use the locale's definition
52    of `digit' even when the host does not conform to POSIX.  */
53 #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
54
55 time_t mktime ();
56
57 /*
58   POSIX requires:
59
60   touch -t [[CC]YY]mmddhhmm[.ss] FILE...
61     8, 10, or 12 digits, followed by optional .ss
62     (PDS_LEADING_YEAR | PDS_CENTURY | PDS_SECONDS)
63
64   touch mmddhhmm[YY] FILE... (obsoleted by POSIX 1003.1-2001)
65     8 or 10 digits
66     (PDS_TRAILING_YEAR)
67
68   date mmddhhmm[[CC]YY]
69     8, 10, or 12 digits
70     (PDS_TRAILING_YEAR | PDS_CENTURY)
71
72 */
73
74 static int
75 year (struct tm *tm, const int *digit_pair, size_t n, int allow_century)
76 {
77   switch (n)
78     {
79     case 1:
80       tm->tm_year = *digit_pair;
81       /* Deduce the century based on the year.
82          POSIX requires that 00-68 be interpreted as 2000-2068,
83          and that 69-99 be interpreted as 1969-1999.  */
84       if (digit_pair[0] <= 68)
85         tm->tm_year += 100;
86       break;
87
88     case 2:
89       if (!allow_century)
90         return 1;
91       tm->tm_year = digit_pair[0] * 100 + digit_pair[1] - 1900;
92       break;
93
94     case 0:
95       {
96         time_t now;
97         struct tm *tmp;
98
99         /* Use current year.  */
100         time (&now);
101         tmp = localtime (&now);
102         if (! tmp)
103           return 1;
104         tm->tm_year = tmp->tm_year;
105       }
106       break;
107
108     default:
109       abort ();
110     }
111
112   return 0;
113 }
114
115 static int
116 posix_time_parse (struct tm *tm, const char *s, unsigned int syntax_bits)
117 {
118   const char *dot = NULL;
119   int pair[6];
120   int *p;
121   size_t i;
122
123   size_t s_len = strlen (s);
124   size_t len = (((syntax_bits & PDS_SECONDS) && (dot = strchr (s, '.')))
125                 ? (size_t) (dot - s)
126                 : s_len);
127
128   if (len != 8 && len != 10 && len != 12)
129     return 1;
130
131   if (dot)
132     {
133       if (!(syntax_bits & PDS_SECONDS))
134         return 1;
135
136       if (s_len - len != 3)
137         return 1;
138     }
139
140   for (i = 0; i < len; i++)
141     if (!ISDIGIT (s[i]))
142       return 1;
143
144   len /= 2;
145   for (i = 0; i < len; i++)
146     pair[i] = 10 * (s[2*i] - '0') + s[2*i + 1] - '0';
147
148   p = pair;
149   if (syntax_bits & PDS_LEADING_YEAR)
150     {
151       if (year (tm, p, len - 4, syntax_bits & PDS_CENTURY))
152         return 1;
153       p += len - 4;
154       len = 4;
155     }
156
157   /* Handle 8 digits worth of `MMDDhhmm'.  */
158   tm->tm_mon = *p++ - 1;
159   tm->tm_mday = *p++;
160   tm->tm_hour = *p++;
161   tm->tm_min = *p++;
162   len -= 4;
163
164   /* Handle any trailing year.  */
165   if (syntax_bits & PDS_TRAILING_YEAR)
166     {
167       if (year (tm, p, len, syntax_bits & PDS_CENTURY))
168         return 1;
169     }
170
171   /* Handle seconds.  */
172   if (!dot)
173     {
174       tm->tm_sec = 0;
175     }
176   else
177     {
178       int seconds;
179
180       ++dot;
181       if (!ISDIGIT (dot[0]) || !ISDIGIT (dot[1]))
182         return 1;
183       seconds = 10 * (dot[0] - '0') + dot[1] - '0';
184
185       tm->tm_sec = seconds;
186     }
187
188   return 0;
189 }
190
191 /* Parse a POSIX-style date, returning true if successful.  */
192
193 bool
194 posixtime (time_t *p, const char *s, unsigned int syntax_bits)
195 {
196   struct tm tm0
197 #ifdef lint
198   /* Placate gcc-4's -Wuninitialized.
199      posix_time_parse fails to set all of tm0 only when it returns
200      nonzero (due to year() returning nonzero), and in that case,
201      this code doesn't use the tm0 at all.  */
202     = { 0, }
203 #endif
204     ;
205   struct tm tm1;
206   struct tm const *tm;
207   time_t t;
208
209   if (posix_time_parse (&tm0, s, syntax_bits))
210     return false;
211
212   tm1 = tm0;
213   tm1.tm_isdst = -1;
214   t = mktime (&tm1);
215
216   if (t != (time_t) -1)
217     tm = &tm1;
218   else
219     {
220       /* mktime returns -1 for errors, but -1 is also a valid time_t
221          value.  Check whether an error really occurred.  */
222       tm = localtime (&t);
223       if (! tm)
224         return false;
225     }
226
227   /* Reject dates like "September 31" and times like "25:61".  */
228   if ((tm0.tm_year ^ tm->tm_year)
229       | (tm0.tm_mon ^ tm->tm_mon)
230       | (tm0.tm_mday ^ tm->tm_mday)
231       | (tm0.tm_hour ^ tm->tm_hour)
232       | (tm0.tm_min ^ tm->tm_min)
233       | (tm0.tm_sec ^ tm->tm_sec))
234     return false;
235
236   *p = t;
237   return true;
238 }
239
240 #ifdef TEST_POSIXTIME
241 /*
242     Test mainly with syntax_bits == 13
243     (aka: (PDS_LEADING_YEAR | PDS_CENTURY | PDS_SECONDS))
244
245     This test data assumes Universal Time, e.g., TZ="UTC0".
246
247     This test data also assumes that time_t is signed and is at least
248     39 bits wide, so that it can represent all years from 0000 through
249     9999.  A host with 32-bit signed time_t can represent only time
250     stamps in the range 1901-12-13 20:45:52 through 2038-01-18
251     03:14:07 UTC, assuming POSIX time_t with no leap seconds, so test
252     cases outside this range will not work on such a host.
253
254     Also, the first two lines of test data assume that the current
255     year is 2002.
256
257 BEGIN-DATA
258 12131415.16     13   1039788916 Fri Dec 13 14:15:16 2002
259 12131415.16     13   1039788916 Fri Dec 13 14:15:16 2002
260 000001010000.00 13 -62167132800 Sun Jan  1 00:00:00 0000
261 190112132045.52 13  -2147483648 Fri Dec 13 20:45:52 1901
262 190112132045.53 13  -2147483647 Fri Dec 13 20:45:53 1901
263 190112132046.52 13  -2147483588 Fri Dec 13 20:46:52 1901
264 190112132145.52 13  -2147480048 Fri Dec 13 21:45:52 1901
265 190112142045.52 13  -2147397248 Sat Dec 14 20:45:52 1901
266 190201132045.52 13  -2144805248 Mon Jan 13 20:45:52 1902
267 196912312359.59 13           -1 Wed Dec 31 23:59:59 1969
268 197001010000.00 13            0 Thu Jan  1 00:00:00 1970
269 197001010000.01 13            1 Thu Jan  1 00:00:01 1970
270 197001010001.00 13           60 Thu Jan  1 00:01:00 1970
271 197001010100.00 13         3600 Thu Jan  1 01:00:00 1970
272 197001020000.00 13        86400 Fri Jan  2 00:00:00 1970
273 197002010000.00 13      2678400 Sun Feb  1 00:00:00 1970
274 197101010000.00 13     31536000 Fri Jan  1 00:00:00 1971
275 197001000000.00 13            * *
276 197000010000.00 13            * *
277 197001010000.60 13            * *
278 197001010060.00 13            * *
279 197001012400.00 13            * *
280 197001320000.00 13            * *
281 197013010000.00 13            * *
282 203801190314.06 13   2147483646 Tue Jan 19 03:14:06 2038
283 203801190314.07 13   2147483647 Tue Jan 19 03:14:07 2038
284 203801190314.08 13   2147483648 Tue Jan 19 03:14:08 2038
285 999912312359.59 13 253402300799 Fri Dec 31 23:59:59 9999
286 1112131415      13   1323785700 Tue Dec 13 14:15:00 2011
287 1112131415.16   13   1323785716 Tue Dec 13 14:15:16 2011
288 201112131415.16 13   1323785716 Tue Dec 13 14:15:16 2011
289 191112131415.16 13  -1831974284 Wed Dec 13 14:15:16 1911
290 203712131415.16 13   2144326516 Sun Dec 13 14:15:16 2037
291 3712131415.16   13   2144326516 Sun Dec 13 14:15:16 2037
292 6812131415.16   13   3122633716 Thu Dec 13 14:15:16 2068
293 6912131415.16   13     -1590284 Sat Dec 13 14:15:16 1969
294 7012131415.16   13     29945716 Sun Dec 13 14:15:16 1970
295 1213141599       2    945094500 Mon Dec 13 14:15:00 1999
296 1213141500       2    976716900 Wed Dec 13 14:15:00 2000
297 END-DATA
298
299 */
300
301 # define MAX_BUFF_LEN 1024
302
303 int
304 main (void)
305 {
306   char buff[MAX_BUFF_LEN + 1];
307
308   buff[MAX_BUFF_LEN] = 0;
309   while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0])
310     {
311       char time_str[MAX_BUFF_LEN];
312       unsigned int syntax_bits;
313       time_t t;
314       if (sscanf (buff, "%s %u", time_str, &syntax_bits) != 2)
315         printf ("*\n");
316       else
317         {
318           printf ("%-15s %2u ", time_str, syntax_bits);
319           if (posixtime (&t, time_str, syntax_bits))
320             printf ("%12ld %s", (long int) t, ctime (&t));
321           else
322             printf ("%12s %s", "*", "*\n");
323         }
324     }
325   exit (0);
326
327 }
328 #endif
329 \f
330 /*
331 Local Variables:
332 compile-command: "gcc -DTEST_POSIXTIME -DHAVE_CONFIG_H -I.. -g -O -Wall -W posixtm.c"
333 End:
334 */