xref: /aosp_15_r20/external/musl/src/time/strptime.c (revision c9945492fdd68bbe62686c5b452b4dc1be3f8453)
1 #include <stdlib.h>
2 #include <langinfo.h>
3 #include <time.h>
4 #include <ctype.h>
5 #include <stddef.h>
6 #include <string.h>
7 #include <strings.h>
8 
strptime(const char * restrict s,const char * restrict f,struct tm * restrict tm)9 char *strptime(const char *restrict s, const char *restrict f, struct tm *restrict tm)
10 {
11 	int i, w, neg, adj, min, range, *dest, dummy;
12 	const char *ex;
13 	size_t len;
14 	int want_century = 0, century = 0, relyear = 0;
15 	while (*f) {
16 		if (*f != '%') {
17 			if (isspace(*f)) for (; *s && isspace(*s); s++);
18 			else if (*s != *f) return 0;
19 			else s++;
20 			f++;
21 			continue;
22 		}
23 		f++;
24 		if (*f == '+') f++;
25 		if (isdigit(*f)) {
26 			char *new_f;
27 			w=strtoul(f, &new_f, 10);
28 			f = new_f;
29 		} else {
30 			w=-1;
31 		}
32 		adj=0;
33 		switch (*f++) {
34 		case 'a': case 'A':
35 			dest = &tm->tm_wday;
36 			min = ABDAY_1;
37 			range = 7;
38 			goto symbolic_range;
39 		case 'b': case 'B': case 'h':
40 			dest = &tm->tm_mon;
41 			min = ABMON_1;
42 			range = 12;
43 			goto symbolic_range;
44 		case 'c':
45 			s = strptime(s, nl_langinfo(D_T_FMT), tm);
46 			if (!s) return 0;
47 			break;
48 		case 'C':
49 			dest = &century;
50 			if (w<0) w=2;
51 			want_century |= 2;
52 			goto numeric_digits;
53 		case 'd': case 'e':
54 			dest = &tm->tm_mday;
55 			min = 1;
56 			range = 31;
57 			goto numeric_range;
58 		case 'D':
59 			s = strptime(s, "%m/%d/%y", tm);
60 			if (!s) return 0;
61 			break;
62 		case 'F':
63 			/* Use temp buffer to implement the odd requirement
64 			 * that entire field be width-limited but the year
65 			 * subfield not itself be limited. */
66 			i = 0;
67 			char tmp[20];
68 			if (*s == '-' || *s == '+') tmp[i++] = *s++;
69 			while (*s=='0' && isdigit(s[1])) s++;
70 			for (; *s && i<(size_t)w && i+1<sizeof tmp; i++) {
71 				tmp[i] = *s++;
72 			}
73 			tmp[i] = 0;
74 			char *p = strptime(tmp, "%12Y-%m-%d", tm);
75 			if (!p) return 0;
76 			s -= tmp+i-p;
77 			break;
78 		case 'H':
79 			dest = &tm->tm_hour;
80 			min = 0;
81 			range = 24;
82 			goto numeric_range;
83 		case 'I':
84 			dest = &tm->tm_hour;
85 			min = 1;
86 			range = 12;
87 			goto numeric_range;
88 		case 'j':
89 			dest = &tm->tm_yday;
90 			min = 1;
91 			range = 366;
92 			adj = 1;
93 			goto numeric_range;
94 		case 'm':
95 			dest = &tm->tm_mon;
96 			min = 1;
97 			range = 12;
98 			adj = 1;
99 			goto numeric_range;
100 		case 'M':
101 			dest = &tm->tm_min;
102 			min = 0;
103 			range = 60;
104 			goto numeric_range;
105 		case 'n': case 't':
106 			for (; *s && isspace(*s); s++);
107 			break;
108 		case 'p':
109 			ex = nl_langinfo(AM_STR);
110 			len = strlen(ex);
111 			if (!strncasecmp(s, ex, len)) {
112 				tm->tm_hour %= 12;
113 				s += len;
114 				break;
115 			}
116 			ex = nl_langinfo(PM_STR);
117 			len = strlen(ex);
118 			if (!strncasecmp(s, ex, len)) {
119 				tm->tm_hour %= 12;
120 				tm->tm_hour += 12;
121 				s += len;
122 				break;
123 			}
124 			return 0;
125 		case 'r':
126 			s = strptime(s, nl_langinfo(T_FMT_AMPM), tm);
127 			if (!s) return 0;
128 			break;
129 		case 'R':
130 			s = strptime(s, "%H:%M", tm);
131 			if (!s) return 0;
132 			break;
133 		case 's':
134 			/* Parse only. Effect on tm is unspecified
135 			 * and presently no effect is implemented.. */
136 			if (*s == '-') s++;
137 			if (!isdigit(*s)) return 0;
138 			while (isdigit(*s)) s++;
139 			break;
140 		case 'S':
141 			dest = &tm->tm_sec;
142 			min = 0;
143 			range = 61;
144 			goto numeric_range;
145 		case 'T':
146 			s = strptime(s, "%H:%M:%S", tm);
147 			if (!s) return 0;
148 			break;
149 		case 'U':
150 		case 'W':
151 			/* Throw away result of %U, %V, %W, %g, and %G. Effect
152 			 * is unspecified and there is no clear right choice. */
153 			dest = &dummy;
154 			min = 0;
155 			range = 54;
156 			goto numeric_range;
157 		case 'V':
158 			dest = &dummy;
159 			min = 1;
160 			range = 53;
161 			goto numeric_range;
162 		case 'g':
163 			dest = &dummy;
164 			w = 2;
165 			goto numeric_digits;
166 		case 'G':
167 			dest = &dummy;
168 			if (w<0) w=4;
169 			goto numeric_digits;
170 		case 'u':
171 			dest = &tm->tm_wday;
172 			min = 1;
173 			range = 7;
174 			goto numeric_range;
175 		case 'w':
176 			dest = &tm->tm_wday;
177 			min = 0;
178 			range = 7;
179 			goto numeric_range;
180 		case 'x':
181 			s = strptime(s, nl_langinfo(D_FMT), tm);
182 			if (!s) return 0;
183 			break;
184 		case 'X':
185 			s = strptime(s, nl_langinfo(T_FMT), tm);
186 			if (!s) return 0;
187 			break;
188 		case 'y':
189 			dest = &relyear;
190 			w = 2;
191 			want_century |= 1;
192 			goto numeric_digits;
193 		case 'Y':
194 			dest = &tm->tm_year;
195 			if (w<0) w=4;
196 			adj = 1900;
197 			want_century = 0;
198 			goto numeric_digits;
199 		case 'z':
200 			if (*s == '+') neg = 0;
201 			else if (*s == '-') neg = 1;
202 			else return 0;
203 			for (i=0; i<4; i++) if (!isdigit(s[1+i])) return 0;
204 			tm->__tm_gmtoff = (s[1]-'0')*36000+(s[2]-'0')*3600
205 				+ (s[3]-'0')*600 + (s[4]-'0')*60;
206 			if (neg) tm->__tm_gmtoff = -tm->__tm_gmtoff;
207 			s += 5;
208 			break;
209 		case 'Z':
210 			if (!strncmp(s, tzname[0], len = strlen(tzname[0]))) {
211 				tm->tm_isdst = 0;
212 				s += len;
213 			} else if (!strncmp(s, tzname[1], len=strlen(tzname[1]))) {
214 				tm->tm_isdst = 1;
215 				s += len;
216 			} else {
217 				/* FIXME: is this supposed to be an error? */
218 				while ((*s|32)-'a' <= 'z'-'a') s++;
219 			}
220 			break;
221 		case '%':
222 			if (*s++ != '%') return 0;
223 			break;
224 		default:
225 			return 0;
226 		numeric_range:
227 			if (!isdigit(*s)) return 0;
228 			*dest = 0;
229 			for (i=1; i<=min+range && isdigit(*s); i*=10)
230 				*dest = *dest * 10 + *s++ - '0';
231 			if (*dest - min >= (unsigned)range) return 0;
232 			*dest -= adj;
233 			switch((char *)dest - (char *)tm) {
234 			case offsetof(struct tm, tm_yday):
235 				;
236 			}
237 			goto update;
238 		numeric_digits:
239 			neg = 0;
240 			if (*s == '+') s++;
241 			else if (*s == '-') neg=1, s++;
242 			if (!isdigit(*s)) return 0;
243 			for (*dest=i=0; i<w && isdigit(*s); i++)
244 				*dest = *dest * 10 + *s++ - '0';
245 			if (neg) *dest = -*dest;
246 			*dest -= adj;
247 			goto update;
248 		symbolic_range:
249 			for (i=2*range-1; i>=0; i--) {
250 				ex = nl_langinfo(min+i);
251 				len = strlen(ex);
252 				if (strncasecmp(s, ex, len)) continue;
253 				s += len;
254 				*dest = i % range;
255 				break;
256 			}
257 			if (i<0) return 0;
258 			goto update;
259 		update:
260 			//FIXME
261 			;
262 		}
263 	}
264 	if (want_century) {
265 		tm->tm_year = relyear;
266 		if (want_century & 2) tm->tm_year += century * 100 - 1900;
267 		else if (tm->tm_year <= 68) tm->tm_year += 100;
268 	}
269 	return (char *)s;
270 }
271