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