stat: Improve last change.
[gnulib.git] / lib / posixtm.c
blobb2441ed7ecafdf025373648d41c936d39b0eefd1
1 /* Parse dates for touch and date.
3 Copyright (C) 1989-1991, 1998, 2000-2017 Free Software Foundation, Inc.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 /* Yacc-based version written by Jim Kingdon and David MacKenzie.
19 Rewritten by Jim Meyering. */
21 #include <config.h>
23 #include "posixtm.h"
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <sys/types.h>
28 #include <string.h>
30 #if USE_UNLOCKED_IO
31 # include "unlocked-io.h"
32 #endif
34 /* ISDIGIT differs from isdigit, as follows:
35 - Its arg may be any int or unsigned int; it need not be an unsigned char
36 or EOF.
37 - It's typically faster.
38 POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
39 isdigit unless it's important to use the locale's definition
40 of "digit" even when the host does not conform to POSIX. */
41 #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
44 POSIX requires:
46 touch -t [[CC]YY]mmddhhmm[.ss] FILE...
47 8, 10, or 12 digits, followed by optional .ss
48 (PDS_LEADING_YEAR | PDS_CENTURY | PDS_SECONDS)
50 touch mmddhhmm[YY] FILE... (obsoleted by POSIX 1003.1-2001)
51 8 or 10 digits, YY (if present) must be in the range 69-99
52 (PDS_TRAILING_YEAR | PDS_PRE_2000)
54 date mmddhhmm[[CC]YY]
55 8, 10, or 12 digits
56 (PDS_TRAILING_YEAR | PDS_CENTURY)
60 static bool
61 year (struct tm *tm, const int *digit_pair, size_t n, unsigned int syntax_bits)
63 switch (n)
65 case 1:
66 tm->tm_year = *digit_pair;
67 /* Deduce the century based on the year.
68 POSIX requires that 00-68 be interpreted as 2000-2068,
69 and that 69-99 be interpreted as 1969-1999. */
70 if (digit_pair[0] <= 68)
72 if (syntax_bits & PDS_PRE_2000)
73 return false;
74 tm->tm_year += 100;
76 break;
78 case 2:
79 if (! (syntax_bits & PDS_CENTURY))
80 return false;
81 tm->tm_year = digit_pair[0] * 100 + digit_pair[1] - 1900;
82 break;
84 case 0:
86 time_t now;
87 struct tm *tmp;
89 /* Use current year. */
90 time (&now);
91 tmp = localtime (&now);
92 if (! tmp)
93 return false;
94 tm->tm_year = tmp->tm_year;
96 break;
98 default:
99 abort ();
102 return true;
105 static bool
106 posix_time_parse (struct tm *tm, const char *s, unsigned int syntax_bits)
108 const char *dot = NULL;
109 int pair[6];
110 int *p;
111 size_t i;
113 size_t s_len = strlen (s);
114 size_t len = s_len;
116 if (syntax_bits & PDS_SECONDS)
118 dot = strchr (s, '.');
119 if (dot)
121 len = dot - s;
122 if (s_len - len != 3)
123 return false;
127 if (! (8 <= len && len <= 12 && len % 2 == 0))
128 return false;
130 for (i = 0; i < len; i++)
131 if (!ISDIGIT (s[i]))
132 return false;
134 len /= 2;
135 for (i = 0; i < len; i++)
136 pair[i] = 10 * (s[2*i] - '0') + s[2*i + 1] - '0';
138 p = pair;
139 if (syntax_bits & PDS_LEADING_YEAR)
141 if (! year (tm, p, len - 4, syntax_bits))
142 return false;
143 p += len - 4;
144 len = 4;
147 /* Handle 8 digits worth of 'MMDDhhmm'. */
148 tm->tm_mon = *p++ - 1;
149 tm->tm_mday = *p++;
150 tm->tm_hour = *p++;
151 tm->tm_min = *p++;
152 len -= 4;
154 /* Handle any trailing year. */
155 if (syntax_bits & PDS_TRAILING_YEAR)
157 if (! year (tm, p, len, syntax_bits))
158 return false;
161 /* Handle seconds. */
162 if (!dot)
163 tm->tm_sec = 0;
164 else if (ISDIGIT (dot[1]) && ISDIGIT (dot[2]))
165 tm->tm_sec = 10 * (dot[1] - '0') + dot[2] - '0';
166 else
167 return false;
169 return true;
172 /* Parse a POSIX-style date, returning true if successful. */
174 bool
175 posixtime (time_t *p, const char *s, unsigned int syntax_bits)
177 struct tm tm0;
178 struct tm tm1;
179 struct tm const *tm;
180 time_t t;
182 if (! posix_time_parse (&tm0, s, syntax_bits))
183 return false;
185 tm1 = tm0;
186 tm1.tm_isdst = -1;
187 t = mktime (&tm1);
189 if (t != (time_t) -1)
190 tm = &tm1;
191 else
193 /* mktime returns -1 for errors, but -1 is also a valid time_t
194 value. Check whether an error really occurred. */
195 tm = localtime (&t);
196 if (! tm)
197 return false;
200 /* Reject dates like "September 31" and times like "25:61".
201 However, allow a seconds count of 60 even in time zones that do
202 not support leap seconds, treating it as the following second;
203 POSIX requires this. */
204 if ((tm0.tm_year ^ tm->tm_year)
205 | (tm0.tm_mon ^ tm->tm_mon)
206 | (tm0.tm_mday ^ tm->tm_mday)
207 | (tm0.tm_hour ^ tm->tm_hour)
208 | (tm0.tm_min ^ tm->tm_min)
209 | (tm0.tm_sec ^ tm->tm_sec))
211 /* Any mismatch without 60 in the tm_sec field is invalid. */
212 if (tm0.tm_sec != 60)
213 return false;
216 /* Allow times like 01:35:60 or 23:59:60. */
217 time_t dummy;
218 char buf[16];
219 char *b = stpcpy (buf, s);
220 strcpy (b - 2, "59");
221 if (!posixtime (&dummy, buf, syntax_bits))
222 return false;
226 *p = t;
227 return true;