.
[gnulib.git] / lib / posixtm.c
blobb09c03f1ef62c317e8e553ddca2e4324e618048a
1 /* Parse dates for touch and date.
2 Copyright (C) 1989, 1990, 1991, 1998, 2000-2002 Free Software Foundation Inc.
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.
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.
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. */
18 /* Yacc-based version written by Jim Kingdon and David MacKenzie.
19 Rewritten by Jim Meyering. */
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
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
36 #ifdef TM_IN_SYS_TIME
37 # include <sys/time.h>
38 #else
39 # include <time.h>
40 #endif
42 #include "posixtm.h"
43 #include "unlocked-io.h"
45 /* ISDIGIT differs from isdigit, as follows:
46 - Its arg may be any int or unsigned int; it need not be an unsigned char.
47 - It's guaranteed to evaluate its argument exactly once.
48 - It's typically faster.
49 POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
50 ISDIGIT_LOCALE unless it's important to use the locale's definition
51 of `digit' even when the host does not conform to POSIX. */
52 #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
54 /* The return value. */
55 static struct tm t;
57 time_t mktime ();
60 POSIX requires:
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)
66 touch mmddhhmm[YY] FILE... (obsoleted by POSIX 1003.1-2001)
67 8 or 10 digits
68 (PDS_TRAILING_YEAR)
70 date mmddhhmm[[CC]YY]
71 8, 10, or 12 digits
72 (PDS_TRAILING_YEAR | PDS_CENTURY)
76 static int
77 year (const int *digit_pair, size_t n, int allow_century)
79 switch (n)
81 case 1:
82 t.tm_year = *digit_pair;
83 /* Deduce the century based on the year.
84 POSIX requires that 00-68 be interpreted as 2000-2068,
85 and that 69-99 be interpreted as 1969-1999. */
86 if (digit_pair[0] <= 68)
87 t.tm_year += 100;
88 break;
90 case 2:
91 if (!allow_century)
92 return 1;
93 t.tm_year = digit_pair[0] * 100 + digit_pair[1];
94 if (t.tm_year < 1900)
95 return 1;
96 t.tm_year -= 1900;
97 break;
99 case 0:
101 time_t now;
102 struct tm *tmp;
104 /* Use current year. */
105 time (&now);
106 tmp = localtime (&now);
107 t.tm_year = tmp->tm_year;
109 break;
111 default:
112 abort ();
115 return 0;
118 static int
119 posix_time_parse (const char *s, unsigned int syntax_bits)
121 const char *dot = NULL;
122 int pair[6];
123 int *p;
124 unsigned int i;
126 size_t s_len = strlen (s);
127 size_t len = (((syntax_bits & PDS_SECONDS) && (dot = strchr (s, '.')))
128 ? (size_t) (dot - s)
129 : s_len);
131 if (len != 8 && len != 10 && len != 12)
132 return 1;
134 if (dot)
136 if (!(syntax_bits & PDS_SECONDS))
137 return 1;
139 if (s_len - len != 3)
140 return 1;
143 for (i = 0; i < len; i++)
144 if (!ISDIGIT (s[i]))
145 return 1;
147 len /= 2;
148 for (i = 0; i < len; i++)
149 pair[i] = 10 * (s[2*i] - '0') + s[2*i + 1] - '0';
151 p = pair;
152 if (syntax_bits & PDS_LEADING_YEAR)
154 if (year (p, len - 4, syntax_bits & PDS_CENTURY))
155 return 1;
156 p += len - 4;
157 len = 4;
160 /* Handle 8 digits worth of `MMDDhhmm'. */
161 if (*p < 1 || *p > 12)
162 return 1;
163 t.tm_mon = *p - 1;
164 ++p; --len;
166 if (*p < 1 || *p > 31)
167 return 1;
168 t.tm_mday = *p;
169 ++p; --len;
171 if (*p < 0 || *p > 23)
172 return 1;
173 t.tm_hour = *p;
174 ++p; --len;
176 if (*p < 0 || *p > 59)
177 return 1;
178 t.tm_min = *p;
179 ++p; --len;
181 /* Handle any trailing year. */
182 if (syntax_bits & PDS_TRAILING_YEAR)
184 if (year (p, len, syntax_bits & PDS_CENTURY))
185 return 1;
188 /* Handle seconds. */
189 if (!dot)
191 t.tm_sec = 0;
193 else
195 int seconds;
197 ++dot;
198 if (!ISDIGIT (dot[0]) || !ISDIGIT (dot[1]))
199 return 1;
200 seconds = 10 * (dot[0] - '0') + dot[1] - '0';
202 if (seconds < 0 || seconds > 61)
203 return 1;
204 t.tm_sec = seconds;
207 return 0;
210 /* Parse a POSIX-style date and return it, or (time_t)-1 for an error. */
212 time_t
213 posixtime (const char *s, unsigned int syntax_bits)
215 t.tm_isdst = -1;
216 if (posix_time_parse (s, syntax_bits))
217 return (time_t)-1;
218 else
219 return mktime (&t);
222 /* Parse a POSIX-style date and return it, or NULL for an error. */
224 struct tm *
225 posixtm (const char *s, unsigned int syntax_bits)
227 if (posixtime (s, syntax_bits) == -1)
228 return NULL;
229 return &t;
232 #ifdef TEST_POSIXTIME
234 Test mainly with syntax_bits == 13
235 (aka: (PDS_LEADING_YEAR | PDS_CENTURY | PDS_SECONDS))
237 BEGIN-DATA
238 1112131415 13 1323807300 Tue Dec 13 14:15:00 2011
239 1112131415.16 13 1323807316 Tue Dec 13 14:15:16 2011
240 201112131415.16 13 1323807316 Tue Dec 13 14:15:16 2011
241 191112131415.16 13 -1 ***
242 203712131415.16 13 2144348116 Sun Dec 13 14:15:16 2037
243 3712131415.16 13 2144348116 Sun Dec 13 14:15:16 2037
244 6812131415.16 13 -1 ***
245 6912131415.16 13 -1 ***
246 7012131415.16 13 29967316 Sun Dec 13 14:15:16 1970
247 12131415.16 13 913580116 Sun Dec 13 14:15:16 1998
249 1213141599 2 945116100 Mon Dec 13 14:15:00 1999
250 1213141500 2 976738500 Wed Dec 13 14:15:00 2000
251 END-DATA
255 # define MAX_BUFF_LEN 1024
258 main ()
260 char buff[MAX_BUFF_LEN + 1];
262 buff[MAX_BUFF_LEN] = 0;
263 while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0])
265 char time_str[MAX_BUFF_LEN];
266 unsigned int syntax_bits;
267 time_t t;
268 char *result;
269 sscanf (buff, "%s %u", time_str, &syntax_bits);
270 t = posixtime (time_str, syntax_bits);
271 result = (t == (time_t) -1 ? "***" : ctime (&t));
272 printf ("%d %s\n", (int) t, result);
274 exit (0);
277 #endif