xref: /PHP-7.4/ext/date/lib/timelib.c (revision aae5907c)
1 /*
2  * The MIT License (MIT)
3  *
4  * Copyright (c) 2015-2019 Derick Rethans
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  * Portions copyright (c) 1998-2017 Zend Technologies Ltd.
25  *
26  * The timelib_strcasecmp and timelib_strncasecmp are taken from PHP's
27  * Zend/zend_operators.[hc] source files.
28  *
29  */
30 
31 #include "timelib.h"
32 #include "timelib_private.h"
33 #include <ctype.h>
34 #include <math.h>
35 
36 #define TIMELIB_LLABS(y) (y < 0 ? (y * -1) : y)
37 
38 const char *timelib_error_messages[8] = {
39 	"No error",
40 	"Can not allocate buffer for parsing",
41 	"Corrupt tzfile: The transitions in the file don't always increase",
42 	"Corrupt tzfile: The expected 64-bit preamble is missing",
43 	"Corrupt tzfile: No abbreviation could be found for a transition",
44 	"The version used in this timezone identifier is unsupported",
45 	"No timezone with this name could be found",
46 };
47 
timelib_get_error_message(int error_code)48 const char *timelib_get_error_message(int error_code)
49 {
50 	int entries = sizeof(timelib_error_messages) / sizeof(char*);
51 
52 	if (error_code >= 0 && error_code < entries) {
53 		return timelib_error_messages[error_code];
54 	}
55 	return "Unknown error code";
56 }
57 
timelib_time_ctor(void)58 timelib_time* timelib_time_ctor(void)
59 {
60 	timelib_time *t;
61 	t = timelib_calloc(1, sizeof(timelib_time));
62 
63 	return t;
64 }
65 
timelib_time_dtor(timelib_time * t)66 void timelib_time_dtor(timelib_time* t)
67 {
68 	TIMELIB_TIME_FREE(t->tz_abbr);
69 	TIMELIB_TIME_FREE(t);
70 }
71 
timelib_time_compare(timelib_time * t1,timelib_time * t2)72 int timelib_time_compare(timelib_time *t1, timelib_time *t2)
73 {
74 	if (t1->sse == t2->sse) {
75 		if (t1->us == t2->us) {
76 			return 0;
77 		}
78 
79 		return (t1->us < t2->us) ? -1 : 1;
80 	}
81 
82 	return (t1->sse < t2->sse) ? -1 : 1;
83 }
84 
timelib_time_clone(timelib_time * orig)85 timelib_time* timelib_time_clone(timelib_time *orig)
86 {
87 	timelib_time *tmp = timelib_time_ctor();
88 	memcpy(tmp, orig, sizeof(timelib_time));
89 	if (orig->tz_abbr) {
90 		tmp->tz_abbr = timelib_strdup(orig->tz_abbr);
91 	}
92 	if (orig->tz_info) {
93 		tmp->tz_info = orig->tz_info;
94 	}
95 	return tmp;
96 }
97 
timelib_rel_time_ctor(void)98 timelib_rel_time* timelib_rel_time_ctor(void)
99 {
100 	timelib_rel_time *t;
101 	t = timelib_calloc(1, sizeof(timelib_rel_time));
102 
103 	return t;
104 }
105 
timelib_rel_time_dtor(timelib_rel_time * t)106 void timelib_rel_time_dtor(timelib_rel_time* t)
107 {
108 	TIMELIB_TIME_FREE(t);
109 }
110 
timelib_rel_time_clone(timelib_rel_time * rel)111 timelib_rel_time* timelib_rel_time_clone(timelib_rel_time *rel)
112 {
113 	timelib_rel_time *tmp = timelib_rel_time_ctor();
114 	memcpy(tmp, rel, sizeof(timelib_rel_time));
115 	return tmp;
116 }
117 
timelib_time_tz_abbr_update(timelib_time * tm,char * tz_abbr)118 void timelib_time_tz_abbr_update(timelib_time* tm, char* tz_abbr)
119 {
120 	unsigned int i;
121 	size_t tz_abbr_len = strlen(tz_abbr);
122 
123 	TIMELIB_TIME_FREE(tm->tz_abbr);
124 	tm->tz_abbr = timelib_strdup(tz_abbr);
125 	for (i = 0; i < tz_abbr_len; i++) {
126 		tm->tz_abbr[i] = toupper(tz_abbr[i]);
127 	}
128 }
129 
timelib_time_offset_ctor(void)130 timelib_time_offset* timelib_time_offset_ctor(void)
131 {
132 	timelib_time_offset *t;
133 	t = timelib_calloc(1, sizeof(timelib_time_offset));
134 
135 	return t;
136 }
137 
timelib_time_offset_dtor(timelib_time_offset * t)138 void timelib_time_offset_dtor(timelib_time_offset* t)
139 {
140 	TIMELIB_TIME_FREE(t->abbr);
141 	TIMELIB_TIME_FREE(t);
142 }
143 
timelib_get_tz_abbr_ptr(timelib_time * t)144 char *timelib_get_tz_abbr_ptr(timelib_time *t)
145 {
146 	if (!t->sse_uptodate) {
147 		timelib_update_ts(t, NULL);
148 	};
149 	return t->tz_abbr;
150 }
151 
timelib_error_container_dtor(timelib_error_container * errors)152 void timelib_error_container_dtor(timelib_error_container *errors)
153 {
154 	int i;
155 
156 	for (i = 0; i < errors->warning_count; i++) {
157 		timelib_free(errors->warning_messages[i].message);
158 	}
159 	timelib_free(errors->warning_messages);
160 	for (i = 0; i < errors->error_count; i++) {
161 		timelib_free(errors->error_messages[i].message);
162 	}
163 	timelib_free(errors->error_messages);
164 	timelib_free(errors);
165 }
166 
timelib_date_to_int(timelib_time * d,int * error)167 timelib_long timelib_date_to_int(timelib_time *d, int *error)
168 {
169 	timelib_sll ts;
170 
171 	ts = d->sse;
172 
173 	if (ts < TIMELIB_LONG_MIN || ts > TIMELIB_LONG_MAX) {
174 		if (error) {
175 			*error = 1;
176 		}
177 		return 0;
178 	}
179 	if (error) {
180 		*error = 0;
181 	}
182 	return (timelib_long) d->sse;
183 }
184 
timelib_decimal_hour_to_hms(double h,int * hour,int * min,int * sec)185 void timelib_decimal_hour_to_hms(double h, int *hour, int *min, int *sec)
186 {
187 	if (h > 0) {
188 		*hour = floor(h);
189 		*min = floor((h - *hour) * 60);
190 		*sec = (h - *hour - ((float) *min / 60)) * 3600;
191 	} else {
192 		*hour = ceil(h);
193 		*min = 0 - ceil((h - *hour) * 60);
194 		*sec = 0 - (h - *hour - ((float) *min / -60)) * 3600;
195 	}
196 }
197 
timelib_hms_to_decimal_hour(int hour,int min,int sec,double * h)198 void timelib_hms_to_decimal_hour(int hour, int min, int sec, double *h)
199 {
200 	if (hour > 0) {
201 		*h = ((double)hour + (double)min / 60 + (double)sec / 3600);
202 	} else {
203 		*h = ((double)hour - (double)min / 60 - (double)sec / 3600);
204 	}
205 }
206 
207 static const unsigned char timelib_tolower_map[256] = {
208 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
209 	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
210 	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
211 	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
212 	0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
213 	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
214 	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
215 	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
216 	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
217 	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
218 	0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
219 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
220 	0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
221 	0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
222 	0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
223 	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
224 };
225 
226 #define timelib_tolower(c) (timelib_tolower_map[(unsigned char)(c)])
227 #undef MIN
228 #undef MAX
229 #define MAX(a, b)  (((a)>(b))?(a):(b))
230 #define MIN(a, b)  (((a)<(b))?(a):(b))
231 
timelib_strcasecmp(const char * s1,const char * s2)232 int timelib_strcasecmp(const char *s1, const char *s2)
233 {
234 	size_t len;
235 	size_t len1 = strlen(s1);
236 	size_t len2 = strlen(s2);
237 	int c1, c2;
238 
239 	if (s1 == s2) {
240 		return 0;
241 	}
242 
243 	len = MIN(len1, len2);
244 	while (len--) {
245 		c1 = timelib_tolower(*(unsigned char *)s1++);
246 		c2 = timelib_tolower(*(unsigned char *)s2++);
247 		if (c1 != c2) {
248 			return c1 - c2;
249 		}
250 	}
251 
252 	return (int)(len1 - len2);
253 }
254 
timelib_strncasecmp(const char * s1,const char * s2,size_t length)255 int timelib_strncasecmp(const char *s1, const char *s2, size_t length)
256 {
257 	size_t len;
258 	size_t len1 = strlen(s1);
259 	size_t len2 = strlen(s2);
260 	int c1, c2;
261 
262 	if (s1 == s2) {
263 		return 0;
264 	}
265 	len = MIN(length, MIN(len1, len2));
266 	while (len--) {
267 		c1 = timelib_tolower(*(unsigned char *)s1++);
268 		c2 = timelib_tolower(*(unsigned char *)s2++);
269 		if (c1 != c2) {
270 			return c1 - c2;
271 		}
272 	}
273 
274 	return (int)(MIN(length, len1) - MIN(length, len2));
275 }
276 
277 #undef MIN
278 #undef MAX
279 
timelib_dump_date(timelib_time * d,int options)280 void timelib_dump_date(timelib_time *d, int options)
281 {
282 	if ((options & 2) == 2) {
283 		printf("TYPE: %d ", d->zone_type);
284 	}
285 	printf("TS: %lld | %s%04lld-%02lld-%02lld %02lld:%02lld:%02lld",
286 		d->sse, d->y < 0 ? "-" : "", TIMELIB_LLABS(d->y), d->m, d->d, d->h, d->i, d->s);
287 	if (d->us > 0) {
288 		printf(" 0.%06lld", d->us);
289 	}
290 
291 	if (d->is_localtime) {
292 		switch (d->zone_type) {
293 			case TIMELIB_ZONETYPE_OFFSET: /* Only offset */
294 				printf(" GMT %05d%s", d->z, d->dst == 1 ? " (DST)" : "");
295 				break;
296 			case TIMELIB_ZONETYPE_ID: /* Timezone struct */
297 				/* Show abbreviation if wanted */
298 				if (d->tz_abbr) {
299 					printf(" %s", d->tz_abbr);
300 				}
301 				/* Do we have a TimeZone struct? */
302 				if (d->tz_info) {
303 					printf(" %s", d->tz_info->name);
304 				}
305 				break;
306 			case TIMELIB_ZONETYPE_ABBR:
307 				printf(" %s", d->tz_abbr);
308 				printf(" %05d%s", d->z, d->dst == 1 ? " (DST)" : "");
309 				break;
310 		}
311 	}
312 
313 	if ((options & 1) == 1) {
314 		if (d->have_relative) {
315 			printf("%3lldY %3lldM %3lldD / %3lldH %3lldM %3lldS",
316 				d->relative.y, d->relative.m, d->relative.d, d->relative.h, d->relative.i, d->relative.s);
317 			if (d->relative.us) {
318 				printf(" 0.%06lld", d->relative.us);
319 			}
320 			if (d->relative.first_last_day_of != 0) {
321 				switch (d->relative.first_last_day_of) {
322 					case 1:
323 						printf(" / first day of");
324 						break;
325 					case 2:
326 						printf(" / last day of");
327 						break;
328 				}
329 			}
330 			if (d->relative.have_weekday_relative) {
331 				printf(" / %d.%d", d->relative.weekday, d->relative.weekday_behavior);
332 			}
333 			if (d->relative.have_special_relative) {
334 				switch (d->relative.special.type) {
335 					case TIMELIB_SPECIAL_WEEKDAY:
336 						printf(" / %lld weekday", d->relative.special.amount);
337 						break;
338 					case TIMELIB_SPECIAL_DAY_OF_WEEK_IN_MONTH:
339 						printf(" / x y of z month");
340 						break;
341 					case TIMELIB_SPECIAL_LAST_DAY_OF_WEEK_IN_MONTH:
342 						printf(" / last y of z month");
343 						break;
344 				}
345 			}
346 		}
347 	}
348 	printf("\n");
349 }
350 
timelib_dump_rel_time(timelib_rel_time * d)351 void timelib_dump_rel_time(timelib_rel_time *d)
352 {
353 	printf("%3lldY %3lldM %3lldD / %3lldH %3lldM %3lldS (days: %lld)%s",
354 		d->y, d->m, d->d, d->h, d->i, d->s, d->days, d->invert ? " inverted" : "");
355 	if (d->first_last_day_of != 0) {
356 		switch (d->first_last_day_of) {
357 			case 1:
358 				printf(" / first day of");
359 				break;
360 			case 2:
361 				printf(" / last day of");
362 				break;
363 		}
364 	}
365 	printf("\n");
366 }
367