* config/srclist.txt: Temporarily comment out mktime.c until glibc bug
[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      or EOF.
49    - It's typically faster.
50    POSIX says that only '0' through '9' are digits.  Prefer ISDIGIT to
51    isdigit 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, YY (if present) must be in the range 69-99
66     (PDS_TRAILING_YEAR | PDS_PRE_2000)
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, unsigned int syntax_bits)
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         {
86           if (syntax_bits & PDS_PRE_2000)
87             return 1;
88           tm->tm_year += 100;
89         }
90       break;
91
92     case 2:
93       if (! (syntax_bits & PDS_CENTURY))
94         return 1;
95       tm->tm_year = digit_pair[0] * 100 + digit_pair[1] - 1900;
96       break;
97
98     case 0:
99       {
100         time_t now;
101         struct tm *tmp;
102
103         /* Use current year.  */
104         time (&now);
105         tmp = localtime (&now);
106         if (! tmp)
107           return 1;
108         tm->tm_year = tmp->tm_year;
109       }
110       break;
111
112     default:
113       abort ();
114     }
115
116   return 0;
117 }
118
119 static int
120 posix_time_parse (struct tm *tm, const char *s, unsigned int syntax_bits)
121 {
122   const char *dot = NULL;
123   int pair[6];
124   int *p;
125   size_t i;
126
127   size_t s_len = strlen (s);
128   size_t len = (((syntax_bits & PDS_SECONDS) && (dot = strchr (s, '.')))
129                 ? (size_t) (dot - s)
130                 : s_len);
131
132   if (len != 8 && len != 10 && len != 12)
133     return 1;
134
135   if (dot)
136     {
137       if (!(syntax_bits & PDS_SECONDS))
138         return 1;
139
140       if (s_len - len != 3)
141         return 1;
142     }
143
144   for (i = 0; i < len; i++)
145     if (!ISDIGIT (s[i]))
146       return 1;
147
148   len /= 2;
149   for (i = 0; i < len; i++)
150     pair[i] = 10 * (s[2*i] - '0') + s[2*i + 1] - '0';
151
152   p = pair;
153   if (syntax_bits & PDS_LEADING_YEAR)
154     {
155       if (year (tm, p, len - 4, syntax_bits))
156         return 1;
157       p += len - 4;
158       len = 4;
159     }
160
161   /* Handle 8 digits worth of `MMDDhhmm'.  */
162   tm->tm_mon = *p++ - 1;
163   tm->tm_mday = *p++;
164   tm->tm_hour = *p++;
165   tm->tm_min = *p++;
166   len -= 4;
167
168   /* Handle any trailing year.  */
169   if (syntax_bits & PDS_TRAILING_YEAR)
170     {
171       if (year (tm, p, len, syntax_bits))
172         return 1;
173     }
174
175   /* Handle seconds.  */
176   if (!dot)
177     {
178       tm->tm_sec = 0;
179     }
180   else
181     {
182       int seconds;
183
184       ++dot;
185       if (!ISDIGIT (dot[0]) || !ISDIGIT (dot[1]))
186         return 1;
187       seconds = 10 * (dot[0] - '0') + dot[1] - '0';
188
189       tm->tm_sec = seconds;
190     }
191
192   return 0;
193 }
194
195 /* Parse a POSIX-style date, returning true if successful.  */
196
197 bool
198 posixtime (time_t *p, const char *s, unsigned int syntax_bits)
199 {
200   struct tm tm0
201 #ifdef lint
202   /* Placate gcc-4's -Wuninitialized.
203      posix_time_parse fails to set all of tm0 only when it returns
204      nonzero (due to year() returning nonzero), and in that case,
205      this code doesn't use the tm0 at all.  */
206     = { 0, }
207 #endif
208     ;
209   struct tm tm1;
210   struct tm const *tm;
211   time_t t;
212
213   if (posix_time_parse (&tm0, s, syntax_bits))
214     return false;
215
216   tm1 = tm0;
217   tm1.tm_isdst = -1;
218   t = mktime (&tm1);
219
220   if (t != (time_t) -1)
221     tm = &tm1;
222   else
223     {
224       /* mktime returns -1 for errors, but -1 is also a valid time_t
225          value.  Check whether an error really occurred.  */
226       tm = localtime (&t);
227       if (! tm)
228         return false;
229     }
230
231   /* Reject dates like "September 31" and times like "25:61".  */
232   if ((tm0.tm_year ^ tm->tm_year)
233       | (tm0.tm_mon ^ tm->tm_mon)
234       | (tm0.tm_mday ^ tm->tm_mday)
235       | (tm0.tm_hour ^ tm->tm_hour)
236       | (tm0.tm_min ^ tm->tm_min)
237       | (tm0.tm_sec ^ tm->tm_sec))
238     return false;
239
240   *p = t;
241   return true;
242 }
243
244 #ifdef TEST_POSIXTIME
245 /*
246     Test mainly with syntax_bits == 13
247     (aka: (PDS_LEADING_YEAR | PDS_CENTURY | PDS_SECONDS))
248
249     This test data assumes Universal Time, e.g., TZ="UTC0".
250
251     This test data also assumes that time_t is signed and is at least
252     39 bits wide, so that it can represent all years from 0000 through
253     9999.  A host with 32-bit signed time_t can represent only time
254     stamps in the range 1901-12-13 20:45:52 through 2038-01-18
255     03:14:07 UTC, assuming POSIX time_t with no leap seconds, so test
256     cases outside this range will not work on such a host.
257
258     Also, the first two lines of test data assume that the current
259     year is 2002.
260
261 BEGIN-DATA
262 12131415.16     13   1039788916 Fri Dec 13 14:15:16 2002
263 12131415.16     13   1039788916 Fri Dec 13 14:15:16 2002
264 000001010000.00 13 -62167132800 Sun Jan  1 00:00:00 0000
265 190112132045.52 13  -2147483648 Fri Dec 13 20:45:52 1901
266 190112132045.53 13  -2147483647 Fri Dec 13 20:45:53 1901
267 190112132046.52 13  -2147483588 Fri Dec 13 20:46:52 1901
268 190112132145.52 13  -2147480048 Fri Dec 13 21:45:52 1901
269 190112142045.52 13  -2147397248 Sat Dec 14 20:45:52 1901
270 190201132045.52 13  -2144805248 Mon Jan 13 20:45:52 1902
271 196912312359.59 13           -1 Wed Dec 31 23:59:59 1969
272 197001010000.00 13            0 Thu Jan  1 00:00:00 1970
273 197001010000.01 13            1 Thu Jan  1 00:00:01 1970
274 197001010001.00 13           60 Thu Jan  1 00:01:00 1970
275 197001010100.00 13         3600 Thu Jan  1 01:00:00 1970
276 197001020000.00 13        86400 Fri Jan  2 00:00:00 1970
277 197002010000.00 13      2678400 Sun Feb  1 00:00:00 1970
278 197101010000.00 13     31536000 Fri Jan  1 00:00:00 1971
279 197001000000.00 13            * *
280 197000010000.00 13            * *
281 197001010000.60 13            * *
282 197001010060.00 13            * *
283 197001012400.00 13            * *
284 197001320000.00 13            * *
285 197013010000.00 13            * *
286 203801190314.06 13   2147483646 Tue Jan 19 03:14:06 2038
287 203801190314.07 13   2147483647 Tue Jan 19 03:14:07 2038
288 203801190314.08 13   2147483648 Tue Jan 19 03:14:08 2038
289 999912312359.59 13 253402300799 Fri Dec 31 23:59:59 9999
290 1112131415      13   1323785700 Tue Dec 13 14:15:00 2011
291 1112131415.16   13   1323785716 Tue Dec 13 14:15:16 2011
292 201112131415.16 13   1323785716 Tue Dec 13 14:15:16 2011
293 191112131415.16 13  -1831974284 Wed Dec 13 14:15:16 1911
294 203712131415.16 13   2144326516 Sun Dec 13 14:15:16 2037
295 3712131415.16   13   2144326516 Sun Dec 13 14:15:16 2037
296 6812131415.16   13   3122633716 Thu Dec 13 14:15:16 2068
297 6912131415.16   13     -1590284 Sat Dec 13 14:15:16 1969
298 7012131415.16   13     29945716 Sun Dec 13 14:15:16 1970
299 1213141599       2    945094500 Mon Dec 13 14:15:00 1999
300 1213141500       2    976716900 Wed Dec 13 14:15:00 2000
301 END-DATA
302
303 */
304
305 # define MAX_BUFF_LEN 1024
306
307 int
308 main (void)
309 {
310   char buff[MAX_BUFF_LEN + 1];
311
312   buff[MAX_BUFF_LEN] = 0;
313   while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0])
314     {
315       char time_str[MAX_BUFF_LEN];
316       unsigned int syntax_bits;
317       time_t t;
318       if (sscanf (buff, "%s %u", time_str, &syntax_bits) != 2)
319         printf ("*\n");
320       else
321         {
322           printf ("%-15s %2u ", time_str, syntax_bits);
323           if (posixtime (&t, time_str, syntax_bits))
324             printf ("%12ld %s", (long int) t, ctime (&t));
325           else
326             printf ("%12s %s", "*", "*\n");
327         }
328     }
329   exit (0);
330
331 }
332 #endif
333 \f
334 /*
335 Local Variables:
336 compile-command: "gcc -DTEST_POSIXTIME -DHAVE_CONFIG_H -I.. -g -O -Wall -W posixtm.c"
337 End:
338 */