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