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 if (h > 0) {
191 *hour = floor(h);
192 *min = floor((h - *hour) * 60);
193 *sec = (h - *hour - ((float) *min / 60)) * 3600;
194 } else {
195 *hour = ceil(h);
196 *min = 0 - ceil((h - *hour) * 60);
197 *sec = 0 - (h - *hour - ((float) *min / -60)) * 3600;
198 }
199 }
200
timelib_hms_to_decimal_hour(int hour,int min,int sec,double * h)201 void timelib_hms_to_decimal_hour(int hour, int min, int sec, double *h)
202 {
203 if (hour >= 0) {
204 *h = ((double)hour + (double)min / 60 + (double)sec / 3600);
205 } else {
206 *h = ((double)hour - (double)min / 60 - (double)sec / 3600);
207 }
208 }
209
timelib_hmsf_to_decimal_hour(int hour,int min,int sec,int us,double * h)210 void timelib_hmsf_to_decimal_hour(int hour, int min, int sec, int us, double *h)
211 {
212 if (hour >= 0) {
213 *h = ((double)hour + (double)min / MINS_PER_HOUR + (double)sec / SECS_PER_HOUR) + (double)us / USECS_PER_HOUR;
214 } else {
215 *h = ((double)hour - (double)min / MINS_PER_HOUR - (double)sec / SECS_PER_HOUR) - (double)us / USECS_PER_HOUR;
216 }
217 }
218
timelib_hms_to_seconds(timelib_sll h,timelib_sll m,timelib_sll s)219 timelib_sll timelib_hms_to_seconds(timelib_sll h, timelib_sll m, timelib_sll s)
220 {
221 return (h * SECS_PER_HOUR) + (m * 60) + s;
222 }
223
224 static const unsigned char timelib_tolower_map[256] = {
225 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
226 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
227 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
228 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
229 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
230 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
231 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
232 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
233 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
234 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
235 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
236 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
237 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
238 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
239 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
240 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
241 };
242
243 #define timelib_tolower(c) (timelib_tolower_map[(unsigned char)(c)])
244 #undef MIN
245 #undef MAX
246 #define MAX(a, b) (((a)>(b))?(a):(b))
247 #define MIN(a, b) (((a)<(b))?(a):(b))
248
timelib_strcasecmp(const char * s1,const char * s2)249 int timelib_strcasecmp(const char *s1, const char *s2)
250 {
251 size_t len;
252 size_t len1 = strlen(s1);
253 size_t len2 = strlen(s2);
254 int c1, c2;
255
256 if (s1 == s2) {
257 return 0;
258 }
259
260 len = MIN(len1, len2);
261 while (len--) {
262 c1 = timelib_tolower(*(unsigned char *)s1++);
263 c2 = timelib_tolower(*(unsigned char *)s2++);
264 if (c1 != c2) {
265 return c1 - c2;
266 }
267 }
268
269 return (int)(len1 - len2);
270 }
271
timelib_strncasecmp(const char * s1,const char * s2,size_t length)272 int timelib_strncasecmp(const char *s1, const char *s2, size_t length)
273 {
274 size_t len;
275 size_t len1 = strlen(s1);
276 size_t len2 = strlen(s2);
277 int c1, c2;
278
279 if (s1 == s2) {
280 return 0;
281 }
282 len = MIN(length, MIN(len1, len2));
283 while (len--) {
284 c1 = timelib_tolower(*(unsigned char *)s1++);
285 c2 = timelib_tolower(*(unsigned char *)s2++);
286 if (c1 != c2) {
287 return c1 - c2;
288 }
289 }
290
291 return (int)(MIN(length, len1) - MIN(length, len2));
292 }
293
294 #undef MIN
295 #undef MAX
296
timelib_dump_date(timelib_time * d,int options)297 void timelib_dump_date(timelib_time *d, int options)
298 {
299 if ((options & 2) == 2) {
300 printf("TYPE: %d ", d->zone_type);
301 }
302 printf("TS: %lld | %s%04lld-%02lld-%02lld %02lld:%02lld:%02lld",
303 d->sse, d->y < 0 ? "-" : "", TIMELIB_LLABS(d->y), d->m, d->d, d->h, d->i, d->s);
304 if (d->us > 0) {
305 printf(" 0.%06lld", d->us);
306 }
307
308 if (d->is_localtime) {
309 switch (d->zone_type) {
310 case TIMELIB_ZONETYPE_OFFSET: /* Only offset */
311 printf(" GMT %05d%s", d->z, d->dst == 1 ? " (DST)" : "");
312 break;
313 case TIMELIB_ZONETYPE_ID: /* Timezone struct */
314 /* Show abbreviation if wanted */
315 if (d->tz_abbr) {
316 printf(" %s", d->tz_abbr);
317 }
318 /* Do we have a TimeZone struct? */
319 if (d->tz_info) {
320 printf(" %s", d->tz_info->name);
321 }
322 break;
323 case TIMELIB_ZONETYPE_ABBR:
324 printf(" %s", d->tz_abbr);
325 printf(" %05d%s", d->z, d->dst == 1 ? " (DST)" : "");
326 break;
327 }
328 }
329
330 if ((options & 1) == 1) {
331 if (d->have_relative) {
332 printf("%3lldY %3lldM %3lldD / %3lldH %3lldM %3lldS",
333 d->relative.y, d->relative.m, d->relative.d, d->relative.h, d->relative.i, d->relative.s);
334 if (d->relative.us) {
335 printf(" 0.%06lld", d->relative.us);
336 }
337 if (d->relative.first_last_day_of != 0) {
338 switch (d->relative.first_last_day_of) {
339 case 1:
340 printf(" / first day of");
341 break;
342 case 2:
343 printf(" / last day of");
344 break;
345 }
346 }
347 if (d->relative.have_weekday_relative) {
348 printf(" / %d.%d", d->relative.weekday, d->relative.weekday_behavior);
349 }
350 if (d->relative.have_special_relative) {
351 switch (d->relative.special.type) {
352 case TIMELIB_SPECIAL_WEEKDAY:
353 printf(" / %lld weekday", d->relative.special.amount);
354 break;
355 case TIMELIB_SPECIAL_DAY_OF_WEEK_IN_MONTH:
356 printf(" / x y of z month");
357 break;
358 case TIMELIB_SPECIAL_LAST_DAY_OF_WEEK_IN_MONTH:
359 printf(" / last y of z month");
360 break;
361 }
362 }
363 }
364 }
365 printf("\n");
366 }
367
timelib_dump_rel_time(timelib_rel_time * d)368 void timelib_dump_rel_time(timelib_rel_time *d)
369 {
370 printf("%3lldY %3lldM %3lldD / %3lldH %3lldM %3lldS (days: %lld)%s",
371 d->y, d->m, d->d, d->h, d->i, d->s, d->days, d->invert ? " inverted" : "");
372 if (d->first_last_day_of != 0) {
373 switch (d->first_last_day_of) {
374 case 1:
375 printf(" / first day of");
376 break;
377 case 2:
378 printf(" / last day of");
379 break;
380 }
381 }
382 printf("\n");
383 }
384