xref: /PHP-8.0/ext/date/lib/timelib.c (revision 4aeff60f)
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[9] = {
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 	"A 'slim' timezone file has been detected",
47 };
48 
timelib_get_error_message(int error_code)49 const char *timelib_get_error_message(int error_code)
50 {
51 	int entries = sizeof(timelib_error_messages) / sizeof(char*);
52 
53 	if (error_code >= 0 && error_code < entries) {
54 		return timelib_error_messages[error_code];
55 	}
56 	return "Unknown error code";
57 }
58 
timelib_time_ctor(void)59 timelib_time* timelib_time_ctor(void)
60 {
61 	timelib_time *t;
62 	t = timelib_calloc(1, sizeof(timelib_time));
63 
64 	return t;
65 }
66 
timelib_time_dtor(timelib_time * t)67 void timelib_time_dtor(timelib_time* t)
68 {
69 	TIMELIB_TIME_FREE(t->tz_abbr);
70 	TIMELIB_TIME_FREE(t);
71 }
72 
timelib_time_compare(timelib_time * t1,timelib_time * t2)73 int timelib_time_compare(timelib_time *t1, timelib_time *t2)
74 {
75 	if (t1->sse == t2->sse) {
76 		if (t1->us == t2->us) {
77 			return 0;
78 		}
79 
80 		return (t1->us < t2->us) ? -1 : 1;
81 	}
82 
83 	return (t1->sse < t2->sse) ? -1 : 1;
84 }
85 
timelib_time_clone(timelib_time * orig)86 timelib_time* timelib_time_clone(timelib_time *orig)
87 {
88 	timelib_time *tmp = timelib_time_ctor();
89 	memcpy(tmp, orig, sizeof(timelib_time));
90 	if (orig->tz_abbr) {
91 		tmp->tz_abbr = timelib_strdup(orig->tz_abbr);
92 	}
93 	if (orig->tz_info) {
94 		tmp->tz_info = orig->tz_info;
95 	}
96 	return tmp;
97 }
98 
timelib_rel_time_ctor(void)99 timelib_rel_time* timelib_rel_time_ctor(void)
100 {
101 	timelib_rel_time *t;
102 	t = timelib_calloc(1, sizeof(timelib_rel_time));
103 
104 	return t;
105 }
106 
timelib_rel_time_dtor(timelib_rel_time * t)107 void timelib_rel_time_dtor(timelib_rel_time* t)
108 {
109 	TIMELIB_TIME_FREE(t);
110 }
111 
timelib_rel_time_clone(timelib_rel_time * rel)112 timelib_rel_time* timelib_rel_time_clone(timelib_rel_time *rel)
113 {
114 	timelib_rel_time *tmp = timelib_rel_time_ctor();
115 	memcpy(tmp, rel, sizeof(timelib_rel_time));
116 	return tmp;
117 }
118 
timelib_time_tz_abbr_update(timelib_time * tm,const char * tz_abbr)119 void timelib_time_tz_abbr_update(timelib_time* tm, const char* tz_abbr)
120 {
121 	unsigned int i;
122 	size_t tz_abbr_len = strlen(tz_abbr);
123 
124 	TIMELIB_TIME_FREE(tm->tz_abbr);
125 	tm->tz_abbr = timelib_strdup(tz_abbr);
126 	for (i = 0; i < tz_abbr_len; i++) {
127 		tm->tz_abbr[i] = toupper(tz_abbr[i]);
128 	}
129 }
130 
timelib_time_offset_ctor(void)131 timelib_time_offset* timelib_time_offset_ctor(void)
132 {
133 	timelib_time_offset *t;
134 	t = timelib_calloc(1, sizeof(timelib_time_offset));
135 
136 	return t;
137 }
138 
timelib_time_offset_dtor(timelib_time_offset * t)139 void timelib_time_offset_dtor(timelib_time_offset* t)
140 {
141 	TIMELIB_TIME_FREE(t->abbr);
142 	TIMELIB_TIME_FREE(t);
143 }
144 
timelib_get_tz_abbr_ptr(timelib_time * t)145 char *timelib_get_tz_abbr_ptr(timelib_time *t)
146 {
147 	if (!t->sse_uptodate) {
148 		timelib_update_ts(t, NULL);
149 	};
150 	return t->tz_abbr;
151 }
152 
timelib_error_container_dtor(timelib_error_container * errors)153 void timelib_error_container_dtor(timelib_error_container *errors)
154 {
155 	int i;
156 
157 	for (i = 0; i < errors->warning_count; i++) {
158 		timelib_free(errors->warning_messages[i].message);
159 	}
160 	timelib_free(errors->warning_messages);
161 	for (i = 0; i < errors->error_count; i++) {
162 		timelib_free(errors->error_messages[i].message);
163 	}
164 	timelib_free(errors->error_messages);
165 	timelib_free(errors);
166 }
167 
timelib_date_to_int(timelib_time * d,int * error)168 timelib_long timelib_date_to_int(timelib_time *d, int *error)
169 {
170 	timelib_sll ts;
171 
172 	ts = d->sse;
173 
174 	if (ts < TIMELIB_LONG_MIN || ts > TIMELIB_LONG_MAX) {
175 		if (error) {
176 			*error = 1;
177 		}
178 		return 0;
179 	}
180 	if (error) {
181 		*error = 0;
182 	}
183 	return (timelib_long) d->sse;
184 }
185 
timelib_decimal_hour_to_hms(double h,int * hour,int * min,int * sec)186 void timelib_decimal_hour_to_hms(double h, int *hour, int *min, int *sec)
187 {
188 	if (h > 0) {
189 		*hour = floor(h);
190 		*min = floor((h - *hour) * 60);
191 		*sec = (h - *hour - ((float) *min / 60)) * 3600;
192 	} else {
193 		*hour = ceil(h);
194 		*min = 0 - ceil((h - *hour) * 60);
195 		*sec = 0 - (h - *hour - ((float) *min / -60)) * 3600;
196 	}
197 }
198 
timelib_hms_to_decimal_hour(int hour,int min,int sec,double * h)199 void timelib_hms_to_decimal_hour(int hour, int min, int sec, double *h)
200 {
201 	if (hour > 0) {
202 		*h = ((double)hour + (double)min / 60 + (double)sec / 3600);
203 	} else {
204 		*h = ((double)hour - (double)min / 60 - (double)sec / 3600);
205 	}
206 }
207 
208 static const unsigned char timelib_tolower_map[256] = {
209 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
210 	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
211 	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
212 	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
213 	0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
214 	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
215 	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
216 	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
217 	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
218 	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
219 	0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
220 	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
221 	0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
222 	0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
223 	0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
224 	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
225 };
226 
227 #define timelib_tolower(c) (timelib_tolower_map[(unsigned char)(c)])
228 #undef MIN
229 #undef MAX
230 #define MAX(a, b)  (((a)>(b))?(a):(b))
231 #define MIN(a, b)  (((a)<(b))?(a):(b))
232 
timelib_strcasecmp(const char * s1,const char * s2)233 int timelib_strcasecmp(const char *s1, const char *s2)
234 {
235 	size_t len;
236 	size_t len1 = strlen(s1);
237 	size_t len2 = strlen(s2);
238 	int c1, c2;
239 
240 	if (s1 == s2) {
241 		return 0;
242 	}
243 
244 	len = MIN(len1, len2);
245 	while (len--) {
246 		c1 = timelib_tolower(*(unsigned char *)s1++);
247 		c2 = timelib_tolower(*(unsigned char *)s2++);
248 		if (c1 != c2) {
249 			return c1 - c2;
250 		}
251 	}
252 
253 	return (int)(len1 - len2);
254 }
255 
timelib_strncasecmp(const char * s1,const char * s2,size_t length)256 int timelib_strncasecmp(const char *s1, const char *s2, size_t length)
257 {
258 	size_t len;
259 	size_t len1 = strlen(s1);
260 	size_t len2 = strlen(s2);
261 	int c1, c2;
262 
263 	if (s1 == s2) {
264 		return 0;
265 	}
266 	len = MIN(length, MIN(len1, len2));
267 	while (len--) {
268 		c1 = timelib_tolower(*(unsigned char *)s1++);
269 		c2 = timelib_tolower(*(unsigned char *)s2++);
270 		if (c1 != c2) {
271 			return c1 - c2;
272 		}
273 	}
274 
275 	return (int)(MIN(length, len1) - MIN(length, len2));
276 }
277 
278 #undef MIN
279 #undef MAX
280 
timelib_dump_date(timelib_time * d,int options)281 void timelib_dump_date(timelib_time *d, int options)
282 {
283 	if ((options & 2) == 2) {
284 		printf("TYPE: %d ", d->zone_type);
285 	}
286 	printf("TS: %lld | %s%04lld-%02lld-%02lld %02lld:%02lld:%02lld",
287 		d->sse, d->y < 0 ? "-" : "", TIMELIB_LLABS(d->y), d->m, d->d, d->h, d->i, d->s);
288 	if (d->us > 0) {
289 		printf(" 0.%06lld", d->us);
290 	}
291 
292 	if (d->is_localtime) {
293 		switch (d->zone_type) {
294 			case TIMELIB_ZONETYPE_OFFSET: /* Only offset */
295 				printf(" GMT %05d%s", d->z, d->dst == 1 ? " (DST)" : "");
296 				break;
297 			case TIMELIB_ZONETYPE_ID: /* Timezone struct */
298 				/* Show abbreviation if wanted */
299 				if (d->tz_abbr) {
300 					printf(" %s", d->tz_abbr);
301 				}
302 				/* Do we have a TimeZone struct? */
303 				if (d->tz_info) {
304 					printf(" %s", d->tz_info->name);
305 				}
306 				break;
307 			case TIMELIB_ZONETYPE_ABBR:
308 				printf(" %s", d->tz_abbr);
309 				printf(" %05d%s", d->z, d->dst == 1 ? " (DST)" : "");
310 				break;
311 		}
312 	}
313 
314 	if ((options & 1) == 1) {
315 		if (d->have_relative) {
316 			printf("%3lldY %3lldM %3lldD / %3lldH %3lldM %3lldS",
317 				d->relative.y, d->relative.m, d->relative.d, d->relative.h, d->relative.i, d->relative.s);
318 			if (d->relative.us) {
319 				printf(" 0.%06lld", d->relative.us);
320 			}
321 			if (d->relative.first_last_day_of != 0) {
322 				switch (d->relative.first_last_day_of) {
323 					case 1:
324 						printf(" / first day of");
325 						break;
326 					case 2:
327 						printf(" / last day of");
328 						break;
329 				}
330 			}
331 			if (d->relative.have_weekday_relative) {
332 				printf(" / %d.%d", d->relative.weekday, d->relative.weekday_behavior);
333 			}
334 			if (d->relative.have_special_relative) {
335 				switch (d->relative.special.type) {
336 					case TIMELIB_SPECIAL_WEEKDAY:
337 						printf(" / %lld weekday", d->relative.special.amount);
338 						break;
339 					case TIMELIB_SPECIAL_DAY_OF_WEEK_IN_MONTH:
340 						printf(" / x y of z month");
341 						break;
342 					case TIMELIB_SPECIAL_LAST_DAY_OF_WEEK_IN_MONTH:
343 						printf(" / last y of z month");
344 						break;
345 				}
346 			}
347 		}
348 	}
349 	printf("\n");
350 }
351 
timelib_dump_rel_time(timelib_rel_time * d)352 void timelib_dump_rel_time(timelib_rel_time *d)
353 {
354 	printf("%3lldY %3lldM %3lldD / %3lldH %3lldM %3lldS (days: %lld)%s",
355 		d->y, d->m, d->d, d->h, d->i, d->s, d->days, d->invert ? " inverted" : "");
356 	if (d->first_last_day_of != 0) {
357 		switch (d->first_last_day_of) {
358 			case 1:
359 				printf(" / first day of");
360 				break;
361 			case 2:
362 				printf(" / last day of");
363 				break;
364 		}
365 	}
366 	printf("\n");
367 }
368