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