Line data Source code
1 : /*
2 : * The MIT License (MIT)
3 : *
4 : * Copyright (c) 2021 MongoDB, Inc.
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 :
25 : #include "timelib.h"
26 : #include "timelib_private.h"
27 :
28 : // This section adds the missing 'strndup' implementation on Windows.
29 : #if TIMELIB_USE_BUILTIN_STRNDUP == 1
30 : # include <stdlib.h>
31 : # include <string.h>
32 :
33 : /**
34 : * char* timelib_strndup(const char* s, size_t n)
35 : *
36 : * Returns a pointer to a copy of 's' with at most 'n' characters
37 : * in memory obtained from 'malloc', or 'NULL' if insufficient
38 : * memory was available. The result is always 'NULL' terminated.
39 : */
40 : static char* timelib_strndup(const char* s, size_t n)
41 : {
42 : char* result;
43 : size_t len = strlen(s);
44 :
45 : if (n < len) {
46 : len = n;
47 : }
48 :
49 : result = (char*)malloc(len + 1);
50 : if (!result) {
51 : return 0;
52 : }
53 :
54 : result[len] = '\0';
55 : return (char*)memcpy(result, s, len);
56 : }
57 : #endif
58 :
59 : /* Forwards declrations */
60 : static timelib_posix_trans_info *timelib_posix_trans_info_ctor(void);
61 : static void timelib_posix_trans_info_dtor(timelib_posix_trans_info* ts);
62 :
63 : /* "<" [+-]? .+? ">" */
64 0 : static char *read_description_numeric_abbr(char **ptr)
65 : {
66 0 : const char *begin = *ptr + 1;
67 :
68 : // skip '<'
69 0 : (*ptr)++;
70 :
71 0 : while (**ptr != '\0' && **ptr != '>') {
72 0 : (*ptr)++;
73 : }
74 :
75 0 : if (**ptr == '\0') {
76 0 : return NULL;
77 : }
78 :
79 0 : if (**ptr == '>') {
80 0 : (*ptr)++;
81 : }
82 :
83 : // Abbreviation may not be empty
84 0 : if (*ptr - begin - 1 < 1) {
85 0 : return NULL;
86 : }
87 :
88 0 : return timelib_strndup(begin, *ptr - begin - 1);
89 : }
90 :
91 : /* [A-Z]+ */
92 0 : static char *read_description_abbr(char **ptr)
93 : {
94 0 : const char *begin = *ptr;
95 :
96 : // Find the end
97 0 : while ((**ptr >= 'A' && **ptr <= 'Z') || (**ptr >= 'a' && **ptr <= 'z')) {
98 0 : (*ptr)++;
99 : }
100 :
101 : // Abbreviation may not be empty
102 0 : if (*ptr - begin < 1) {
103 0 : return NULL;
104 : }
105 :
106 0 : return timelib_strndup(begin, *ptr - begin);
107 : }
108 :
109 : /* "<" [+-]? .+? ">" | [A-Z]+ */
110 0 : static char *read_description(char **ptr)
111 : {
112 0 : if (**ptr == '<') {
113 0 : return read_description_numeric_abbr(ptr);
114 : } else {
115 0 : return read_description_abbr(ptr);
116 : }
117 : }
118 :
119 : /* [+-]? */
120 0 : static int read_sign(char **ptr)
121 : {
122 0 : int bias = 1;
123 :
124 0 : if (**ptr == '+') {
125 0 : (*ptr)++;
126 0 : } else if (**ptr == '-') {
127 0 : bias = -1;
128 0 : (*ptr)++;
129 : }
130 :
131 0 : return bias;
132 : }
133 :
134 : /* [0-9]+ */
135 0 : static timelib_sll read_number(char **ptr)
136 : {
137 0 : const char *begin = *ptr;
138 0 : int acc = 0;
139 :
140 : // skip leading 0's
141 0 : while (**ptr == '0') {
142 0 : (*ptr)++;
143 : }
144 :
145 0 : while (**ptr >= '0' && **ptr <= '9') {
146 0 : acc = acc * 10;
147 0 : acc += (**ptr) - '0';
148 0 : (*ptr)++;
149 : }
150 :
151 0 : if (begin == *ptr) {
152 0 : return TIMELIB_UNSET;
153 : }
154 :
155 0 : return acc;
156 : }
157 :
158 : /* [+-]? [0-9]+ ( ":" [0-9]+ ( ":" [0-9]+ )? )? */
159 0 : static timelib_sll read_offset(char **ptr)
160 : {
161 : const char *begin;
162 0 : int bias = read_sign(ptr);
163 0 : int hours = 0;
164 0 : int minutes = 0;
165 0 : int seconds = 0;
166 :
167 0 : begin = *ptr;
168 :
169 : // read through to : or non-digit for hours
170 0 : hours = read_number(ptr);
171 0 : if (hours == TIMELIB_UNSET) {
172 0 : return hours;
173 : }
174 :
175 : // check for optional minutes
176 0 : if (**ptr == ':') {
177 0 : (*ptr)++; // skip ':'
178 0 : minutes = read_number(ptr);
179 0 : if (minutes == TIMELIB_UNSET) {
180 0 : return minutes;
181 : }
182 : }
183 :
184 : // check for optional seconds
185 0 : if (**ptr == ':') {
186 0 : (*ptr)++; // skip ':'
187 0 : seconds = read_number(ptr);
188 0 : if (seconds == TIMELIB_UNSET) {
189 0 : return seconds;
190 : }
191 : }
192 :
193 0 : if (begin == *ptr) {
194 0 : return TIMELIB_UNSET;
195 : }
196 :
197 : // multiplication with -1, because the offset in the identifier is the
198 : // 'wrong' way around as for example EST5 is UTC-5 (and not +5)
199 0 : return -1 * bias * (hours * 3600 + minutes * 60 + seconds);
200 : }
201 :
202 :
203 : // Mw.m.d
204 0 : static timelib_posix_trans_info* read_trans_spec_mwd(char **ptr)
205 : {
206 0 : timelib_posix_trans_info *tmp = timelib_posix_trans_info_ctor();
207 :
208 0 : tmp->type = TIMELIB_POSIX_TRANS_TYPE_MWD;
209 :
210 : // Skip 'M'
211 0 : (*ptr)++;
212 :
213 0 : tmp->mwd.month = read_number(ptr);
214 0 : if (tmp->mwd.month == TIMELIB_UNSET) {
215 0 : goto fail;
216 : }
217 :
218 : // check for '.' and skip it
219 0 : if (**ptr != '.') {
220 0 : goto fail;
221 : }
222 0 : (*ptr)++;
223 :
224 0 : tmp->mwd.week = read_number(ptr);
225 0 : if (tmp->mwd.week == TIMELIB_UNSET) {
226 0 : goto fail;
227 : }
228 :
229 : // check for '.' and skip it
230 0 : if (**ptr != '.') {
231 0 : goto fail;
232 : }
233 0 : (*ptr)++;
234 :
235 0 : tmp->mwd.dow = read_number(ptr);
236 0 : if (tmp->mwd.dow == TIMELIB_UNSET) {
237 0 : goto fail;
238 : }
239 :
240 0 : return tmp;
241 :
242 0 : fail:
243 0 : timelib_posix_trans_info_dtor(tmp);
244 0 : return NULL;
245 : }
246 :
247 : // (Jn | n | Mw.m.d) ( /time )?
248 0 : static timelib_posix_trans_info* read_transition_spec(char **ptr)
249 : {
250 : timelib_posix_trans_info *tmp;
251 :
252 0 : if (**ptr == 'M') {
253 0 : tmp = read_trans_spec_mwd(ptr);
254 0 : if (!tmp) {
255 0 : return NULL;
256 : }
257 : } else {
258 0 : tmp = timelib_posix_trans_info_ctor();
259 :
260 0 : if (**ptr == 'J') {
261 0 : tmp->type = TIMELIB_POSIX_TRANS_TYPE_JULIAN_NO_FEB29;
262 0 : (*ptr)++;
263 : }
264 :
265 0 : tmp->days = read_number(ptr);
266 0 : if (tmp->days == TIMELIB_UNSET) {
267 0 : goto fail;
268 : }
269 : }
270 :
271 : // Check for the optional hour
272 0 : if (**ptr == '/') {
273 0 : (*ptr)++;
274 0 : tmp->hour = read_offset(ptr);
275 0 : if (tmp->hour == TIMELIB_UNSET) {
276 0 : goto fail;
277 : }
278 : // as the bias for normal offsets = -1, we need to reverse it here
279 0 : tmp->hour = -tmp->hour;
280 : }
281 :
282 0 : return tmp;
283 :
284 0 : fail:
285 0 : timelib_posix_trans_info_dtor(tmp);
286 0 : return NULL;
287 : }
288 :
289 0 : static timelib_posix_trans_info* timelib_posix_trans_info_ctor(void)
290 : {
291 : timelib_posix_trans_info *tmp;
292 :
293 0 : tmp = timelib_calloc(1, sizeof(timelib_posix_trans_info));
294 0 : tmp->type = TIMELIB_POSIX_TRANS_TYPE_JULIAN_FEB29;
295 0 : tmp->hour = 2 * 3600;
296 :
297 0 : return tmp;
298 : }
299 :
300 0 : static void timelib_posix_trans_info_dtor(timelib_posix_trans_info* ts)
301 : {
302 0 : timelib_free(ts);
303 0 : }
304 :
305 0 : void timelib_posix_str_dtor(timelib_posix_str *ps)
306 : {
307 0 : if (ps->std) {
308 0 : timelib_free(ps->std);
309 : }
310 0 : if (ps->dst) {
311 0 : timelib_free(ps->dst);
312 : }
313 0 : if (ps->dst_begin) {
314 0 : timelib_posix_trans_info_dtor(ps->dst_begin);
315 : }
316 0 : if (ps->dst_end) {
317 0 : timelib_posix_trans_info_dtor(ps->dst_end);
318 : }
319 :
320 0 : timelib_free(ps);
321 0 : }
322 :
323 0 : timelib_posix_str* timelib_parse_posix_str(const char *posix)
324 : {
325 0 : timelib_posix_str *tmp = timelib_calloc(1, sizeof(timelib_posix_str));
326 0 : char *ptr = (char*) posix;
327 :
328 : // read standard description (ie. EST or <-03>)
329 0 : tmp->std = read_description(&ptr);
330 0 : if (!tmp->std) {
331 0 : timelib_posix_str_dtor(tmp);
332 0 : return NULL;
333 : }
334 :
335 : // read required offset
336 0 : tmp->std_offset = read_offset(&ptr);
337 0 : if (tmp->std_offset == TIMELIB_UNSET) {
338 0 : timelib_posix_str_dtor(tmp);
339 0 : return NULL;
340 : }
341 :
342 : // if we're at the end return, otherwise we'll continue to try to parse
343 : // the dst abbreviation and spec
344 0 : if (*ptr == '\0') {
345 0 : return tmp;
346 : }
347 :
348 : // assume dst is there, and initialise offset
349 0 : tmp->dst_offset = tmp->std_offset + 3600;
350 :
351 0 : tmp->dst = read_description(&ptr);
352 0 : if (!tmp->dst) {
353 0 : timelib_posix_str_dtor(tmp);
354 0 : return NULL;
355 : }
356 :
357 : // if we have a "," here, then the dst offset is the standard offset +
358 : // 3600 seconds, otherwise, try to parse the dst offset
359 0 : if (*ptr != ',' && *ptr != '\0') {
360 0 : tmp->dst_offset = read_offset(&ptr);
361 0 : if (tmp->dst_offset == TIMELIB_UNSET) {
362 0 : timelib_posix_str_dtor(tmp);
363 0 : return NULL;
364 : }
365 : }
366 :
367 : // if we *don't* have a "," here, we're missing the dst transitions
368 : // ,start[/time],end[/time]
369 0 : if (*ptr != ',') {
370 0 : timelib_posix_str_dtor(tmp);
371 0 : return NULL;
372 : }
373 :
374 0 : ptr++; // skip ','
375 :
376 : // start[/time]
377 0 : tmp->dst_begin = read_transition_spec(&ptr);
378 0 : if (!tmp->dst_begin) {
379 0 : timelib_posix_str_dtor(tmp);
380 0 : return NULL;
381 : }
382 :
383 : // if we *don't* have a "," here, we're missing the dst end transition
384 : // ,end[/time]
385 0 : if (*ptr != ',') {
386 0 : timelib_posix_str_dtor(tmp);
387 0 : return NULL;
388 : }
389 :
390 0 : ptr++; // skip ','
391 :
392 : // end[/time]
393 0 : tmp->dst_end = read_transition_spec(&ptr);
394 0 : if (!tmp->dst_end) {
395 0 : timelib_posix_str_dtor(tmp);
396 0 : return NULL;
397 : }
398 :
399 : // make sure there is no trailing data
400 0 : if (*ptr != '\0') {
401 0 : timelib_posix_str_dtor(tmp);
402 0 : return NULL;
403 : }
404 :
405 0 : return tmp;
406 : }
407 :
408 : static const int month_lengths[2][MONTHS_PER_YEAR] = {
409 : { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, // normal year
410 : { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } // leap year
411 : };
412 :
413 : /* This function is adapted from the 'localtime.c' function 'transtime' as bundled with the 'tzcode' project
414 : * from IANA, and is public domain licensed. */
415 0 : static timelib_sll calc_transition(timelib_posix_trans_info *psi, timelib_sll year)
416 : {
417 0 : int leap_year = timelib_is_leap(year);
418 :
419 0 : switch (psi->type) {
420 0 : case TIMELIB_POSIX_TRANS_TYPE_JULIAN_NO_FEB29: {
421 0 : timelib_sll value = (psi->days - 1);
422 :
423 0 : if (leap_year && psi->days >= 60) {
424 0 : value++;
425 : }
426 :
427 0 : return value * SECS_PER_DAY;
428 : }
429 :
430 0 : case TIMELIB_POSIX_TRANS_TYPE_JULIAN_FEB29: {
431 0 : return psi->days * SECS_PER_DAY;
432 : }
433 :
434 0 : case TIMELIB_POSIX_TRANS_TYPE_MWD: {
435 : /*
436 : * Mm.n.d - nth "dth day" of month m.
437 : */
438 :
439 : int i, d, m1, yy0, yy1, yy2, dow;
440 0 : timelib_sll value = 0;
441 :
442 : /* Use Zeller's Congruence to get day-of-week of first day of
443 : * month. */
444 0 : m1 = (psi->mwd.month + 9) % 12 + 1;
445 0 : yy0 = (psi->mwd.month <= 2) ? (year - 1) : year;
446 0 : yy1 = yy0 / 100;
447 0 : yy2 = yy0 % 100;
448 0 : dow = ((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
449 0 : if (dow < 0) {
450 0 : dow += DAYS_PER_WEEK;
451 : }
452 :
453 : /* "dow" is the day-of-week of the first day of the month. Get the
454 : * day-of-month (zero-origin) of the first "dow" day of the month. */
455 0 : d = psi->mwd.dow - dow;
456 0 : if (d < 0) {
457 0 : d += DAYS_PER_WEEK;
458 : }
459 0 : for (i = 1; i < psi->mwd.week; ++i) {
460 0 : if (d + DAYS_PER_WEEK >= month_lengths[leap_year][psi->mwd.month - 1]) {
461 0 : break;
462 : }
463 0 : d += DAYS_PER_WEEK;
464 : }
465 :
466 : /* "d" is the day-of-month (zero-origin) of the day we want. */
467 0 : value = d * SECS_PER_DAY;
468 0 : for (i = 0; i < psi->mwd.month - 1; ++i) {
469 0 : value += month_lengths[leap_year][i] * SECS_PER_DAY;
470 : }
471 :
472 0 : return value;
473 : } break;
474 : }
475 :
476 0 : return 0;
477 : }
478 :
479 0 : static timelib_sll count_leap_years(timelib_sll y)
480 : {
481 : /* Because we want this for Jan 1, the leap day hasn't happend yet, so
482 : * subtract one of year before we calculate */
483 0 : y--;
484 :
485 0 : return (y/4) - (y/100) + (y/400);
486 : }
487 :
488 0 : timelib_sll timelib_ts_at_start_of_year(timelib_sll year)
489 : {
490 0 : timelib_sll epoch_leap_years = count_leap_years(1970);
491 0 : timelib_sll current_leap_years = count_leap_years(year);
492 :
493 0 : return SECS_PER_DAY * (
494 0 : ((year-1970) * DAYS_PER_YEAR)
495 0 : + current_leap_years
496 0 : - epoch_leap_years
497 : );
498 : }
499 :
500 0 : void timelib_get_transitions_for_year(timelib_tzinfo *tz, timelib_sll year, timelib_posix_transitions *transitions)
501 : {
502 : timelib_sll trans_begin; /* Since start of the year */
503 : timelib_sll trans_end;
504 0 : timelib_sll year_begin_ts = timelib_ts_at_start_of_year(year);
505 :
506 0 : trans_begin = year_begin_ts;
507 0 : trans_begin += calc_transition(tz->posix_info->dst_begin, year);
508 0 : trans_begin += tz->posix_info->dst_begin->hour;
509 0 : trans_begin -= tz->posix_info->std_offset;
510 :
511 0 : trans_end = year_begin_ts;
512 0 : trans_end += calc_transition(tz->posix_info->dst_end, year);
513 0 : trans_end += tz->posix_info->dst_end->hour;
514 0 : trans_end -= tz->posix_info->dst_offset;
515 :
516 0 : if (trans_begin < trans_end) {
517 0 : transitions->times[transitions->count ] = trans_begin;
518 0 : transitions->times[transitions->count+1] = trans_end;
519 0 : transitions->types[transitions->count ] = tz->posix_info->type_index_dst_type;
520 0 : transitions->types[transitions->count+1] = tz->posix_info->type_index_std_type;
521 : } else {
522 0 : transitions->times[transitions->count+1] = trans_begin;
523 0 : transitions->times[transitions->count ] = trans_end;
524 0 : transitions->types[transitions->count+1] = tz->posix_info->type_index_dst_type;
525 0 : transitions->types[transitions->count ] = tz->posix_info->type_index_std_type;
526 : }
527 :
528 0 : transitions->count += 2;
529 0 : }
530 :
531 1320 : ttinfo* timelib_fetch_posix_timezone_offset(timelib_tzinfo *tz, timelib_sll ts, timelib_sll *transition_time)
532 : {
533 : timelib_sll year;
534 : timelib_time dummy;
535 1320 : timelib_posix_transitions transitions = { 0 };
536 : size_t i;
537 :
538 : /* If there is no second (dst_end) information, the UTC offset is valid for the whole year, so no need to
539 : * do clever logic */
540 1320 : if (!tz->posix_info->dst_end) {
541 1320 : if (transition_time) {
542 144 : *transition_time = tz->trans[tz->bit64.timecnt - 1];
543 : }
544 1320 : return &(tz->type[tz->posix_info->type_index_std_type]);
545 : }
546 :
547 : /* Find 'year' (UTC) for 'ts' */
548 0 : timelib_unixtime2gmt(&dummy, ts);
549 0 : year = dummy.y;
550 :
551 : /* Calculate transition times for 'year-1', 'year', and 'year+1' */
552 0 : timelib_get_transitions_for_year(tz, year - 1, &transitions);
553 0 : timelib_get_transitions_for_year(tz, year, &transitions);
554 0 : timelib_get_transitions_for_year(tz, year + 1, &transitions);
555 :
556 : /* Check where the 'ts' falls in the 4 transitions */
557 0 : for (i = 1; i < transitions.count; i++) {
558 0 : if (ts < transitions.times[i]) {
559 0 : if (transition_time) {
560 0 : *transition_time = transitions.times[i - 1];
561 : }
562 0 : return &(tz->type[transitions.types[i - 1]]);
563 : }
564 : }
565 :
566 0 : return NULL;
567 : }
|