03aaaf20aeba9b815172828d1490af2f9853aef4
[gnulib.git] / lib / posixtm.c
1 /* Parse dates for touch.
2    Copyright (C) 1989, 1990, 1991, 1998 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 <stdio.h>
26 #include <sys/types.h>
27 #if HAVE_STRING_H
28 # include <string.h>
29 #else
30 # include <strings.h>
31 #endif
32
33 #ifdef TM_IN_SYS_TIME
34 # include <sys/time.h>
35 #else
36 # include <time.h>
37 #endif
38
39 #include "posixtm.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 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that
46    only '0' through '9' are digits.  Prefer ISDIGIT to isdigit unless
47    it's important to use the locale's definition of `digit' even when the
48    host does not conform to Posix.  */
49 #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
50
51 /* The return value. */
52 static struct tm t;
53
54 time_t mktime ();
55
56 /*
57   POSIX requires:
58
59   touch -t [[CC]YY]mmddhhmm[.ss] FILE...
60     8, 10, or 12 digits, followed by optional .ss
61     (PDS_LEADING_YEAR | PDS_CENTURY | PDS_SECONDS)
62
63   touch mmddhhmm[YY] FILE... (obsolescent)
64     8 or 10 digits
65     (PDS_TRAILING_YEAR)
66
67   date mmddhhmm[[CC]YY]
68     8, 10, or 12 digits
69     (PDS_TRAILING_YEAR | PDS_CENTURY)
70
71 */
72
73 static int
74 year (const int *digit_pair, size_t n, int allow_century)
75 {
76   switch (n)
77     {
78     case 1:
79       t.tm_year = *digit_pair;
80       /* Deduce the century based on the year.
81          See POSIX.2 section 4.63.3.  */
82       if (digit_pair[0] <= 68)
83         t.tm_year += 100;
84       break;
85
86     case 2:
87       if (!allow_century)
88         return 1;
89       t.tm_year = digit_pair[0] * 100 + digit_pair[1];
90       if (t.tm_year < 1900)
91         return 1;
92       t.tm_year -= 1900;
93       break;
94
95     case 0:
96       {
97         time_t now;
98         struct tm *tmp;
99
100         /* Use current year.  */
101         time (&now);
102         tmp = localtime (&now);
103         t.tm_year = tmp->tm_year;
104       }
105       break;
106
107     default:
108       abort ();
109     }
110
111   return 0;
112 }
113
114 static int
115 posix_time_parse (const char *s, unsigned int syntax_bits)
116 {
117   const char *dot = NULL;
118   int pair[6];
119   int *p;
120   int i;
121
122   size_t s_len = strlen (s);
123   size_t len = (((syntax_bits & PDS_SECONDS) && (dot = strchr (s, '.')))
124                 ? dot - s
125                 : s_len);
126
127   if (len != 8 && len != 10 && len != 12)
128     return 1;
129
130   if (dot)
131     {
132       if (!(syntax_bits & PDS_SECONDS))
133         return 1;
134
135       if (s_len - len != 3)
136         return 1;
137     }
138
139   for (i = 0; i < len; i++)
140     if (!ISDIGIT (s[i]))
141       return 1;
142
143   len /= 2;
144   for (i = 0; i < len; i++)
145     pair[i] = 10 * (s[2*i] - '0') + s[2*i + 1] - '0';
146
147   p = pair;
148   if (syntax_bits & PDS_LEADING_YEAR)
149     {
150       if (year (p, len - 4, syntax_bits & PDS_CENTURY))
151         return 1;
152       p += len - 4;
153       len = 4;
154     }
155
156   /* Handle 8 digits worth of `MMDDhhmm'.  */
157   if (*p < 1 || *p > 12)
158     return 1;
159   t.tm_mon = *p - 1;
160   ++p; --len;
161
162   if (*p < 1 || *p > 31)
163     return 1;
164   t.tm_mday = *p;
165   ++p; --len;
166
167   if (*p < 0 || *p > 23)
168     return 1;
169   t.tm_hour = *p;
170   ++p; --len;
171
172   if (*p < 0 || *p > 59)
173     return 1;
174   t.tm_min = *p;
175   ++p; --len;
176
177   /* Handle any trailing year.  */
178   if (syntax_bits & PDS_TRAILING_YEAR)
179     {
180       if (year (p, len, syntax_bits & PDS_CENTURY))
181         return 1;
182     }
183
184   /* Handle seconds.  */
185   if (!dot)
186     {
187       t.tm_sec = 0;
188     }
189   else
190     {
191       int seconds;
192
193       ++dot;
194       if (!ISDIGIT (dot[0]) || !ISDIGIT (dot[1]))
195         return 1;
196       seconds = 10 * (dot[0] - '0') + dot[1] - '0';
197
198       if (seconds < 0 || seconds > 61)
199         return 1;
200       t.tm_sec = seconds;
201     }
202
203   return 0;
204 }
205
206 /* Parse a POSIX-style date and return it, or (time_t)-1 for an error.  */
207
208 time_t
209 posixtime (const char *s, unsigned int syntax_bits)
210 {
211   t.tm_isdst = -1;
212   if (posix_time_parse (s, syntax_bits))
213     return (time_t)-1;
214   else
215     return mktime (&t);
216 }
217
218 /* Parse a POSIX-style date and return it, or NULL for an error.  */
219
220 struct tm *
221 posixtm (const char *s, unsigned int syntax_bits)
222 {
223   if (posixtime (s, syntax_bits) == -1)
224     return NULL;
225   return &t;
226 }
227
228 #ifdef TEST_POSIXTIME
229 /*
230     Test mainly with syntax_bits == 13
231     (aka: (PDS_LEADING_YEAR | PDS_CENTURY | PDS_SECONDS))
232
233 BEGIN-DATA
234 1112131415      13 1323807300 Tue Dec 13 14:15:00 2011
235 1112131415.16   13 1323807316 Tue Dec 13 14:15:16 2011
236 201112131415.16 13 1323807316 Tue Dec 13 14:15:16 2011
237 191112131415.16 13 -1 ***
238 203712131415.16 13 2144348116 Sun Dec 13 14:15:16 2037
239 3712131415.16   13 2144348116 Sun Dec 13 14:15:16 2037
240 6812131415.16   13 -1 ***
241 6912131415.16   13 -1 ***
242 7012131415.16   13 29967316 Sun Dec 13 14:15:16 1970
243 12131415.16     13 913580116 Sun Dec 13 14:15:16 1998
244
245 1213141599       2 945116100 Mon Dec 13 14:15:00 1999
246 1213141500       2 976738500 Wed Dec 13 14:15:00 2000
247 END-DATA
248
249 */
250
251 # define MAX_BUFF_LEN 1024
252
253 int
254 main ()
255 {
256   char buff[MAX_BUFF_LEN + 1];
257
258   buff[MAX_BUFF_LEN] = 0;
259   while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0])
260     {
261       char time_str[MAX_BUFF_LEN];
262       unsigned int syntax_bits;
263       time_t t;
264       char *result;
265       sscanf (buff, "%s %u", time_str, &syntax_bits);
266       t = posixtime (time_str, syntax_bits);
267       result = (t == (time_t) -1 ? "***" : ctime (&t));
268       printf ("%d %s\n", (int) t, result);
269     }
270   exit (0);
271
272 }
273 #endif