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