123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567 |
- /*
- * The MIT License (MIT)
- *
- * Copyright (c) 2021 MongoDB, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
- #include "timelib.h"
- #include "timelib_private.h"
- // This section adds the missing 'strndup' implementation on Windows.
- #if TIMELIB_USE_BUILTIN_STRNDUP == 1
- # include <stdlib.h>
- # include <string.h>
- /**
- * char* timelib_strndup(const char* s, size_t n)
- *
- * Returns a pointer to a copy of 's' with at most 'n' characters
- * in memory obtained from 'malloc', or 'NULL' if insufficient
- * memory was available. The result is always 'NULL' terminated.
- */
- static char* timelib_strndup(const char* s, size_t n)
- {
- char* result;
- size_t len = strlen(s);
- if (n < len) {
- len = n;
- }
- result = (char*)malloc(len + 1);
- if (!result) {
- return 0;
- }
- result[len] = '\0';
- return (char*)memcpy(result, s, len);
- }
- #endif
- /* Forwards declrations */
- static timelib_posix_trans_info *timelib_posix_trans_info_ctor(void);
- static void timelib_posix_trans_info_dtor(timelib_posix_trans_info* ts);
- /* "<" [+-]? .+? ">" */
- static char *read_description_numeric_abbr(char **ptr)
- {
- const char *begin = *ptr + 1;
- // skip '<'
- (*ptr)++;
- while (**ptr != '\0' && **ptr != '>') {
- (*ptr)++;
- }
- if (**ptr == '\0') {
- return NULL;
- }
- if (**ptr == '>') {
- (*ptr)++;
- }
- // Abbreviation may not be empty
- if (*ptr - begin - 1 < 1) {
- return NULL;
- }
- return timelib_strndup(begin, *ptr - begin - 1);
- }
- /* [A-Z]+ */
- static char *read_description_abbr(char **ptr)
- {
- const char *begin = *ptr;
- // Find the end
- while ((**ptr >= 'A' && **ptr <= 'Z') || (**ptr >= 'a' && **ptr <= 'z')) {
- (*ptr)++;
- }
- // Abbreviation may not be empty
- if (*ptr - begin < 1) {
- return NULL;
- }
- return timelib_strndup(begin, *ptr - begin);
- }
- /* "<" [+-]? .+? ">" | [A-Z]+ */
- static char *read_description(char **ptr)
- {
- if (**ptr == '<') {
- return read_description_numeric_abbr(ptr);
- } else {
- return read_description_abbr(ptr);
- }
- }
- /* [+-]? */
- static int read_sign(char **ptr)
- {
- int bias = 1;
- if (**ptr == '+') {
- (*ptr)++;
- } else if (**ptr == '-') {
- bias = -1;
- (*ptr)++;
- }
- return bias;
- }
- /* [0-9]+ */
- static timelib_sll read_number(char **ptr)
- {
- const char *begin = *ptr;
- int acc = 0;
- // skip leading 0's
- while (**ptr == '0') {
- (*ptr)++;
- }
- while (**ptr >= '0' && **ptr <= '9') {
- acc = acc * 10;
- acc += (**ptr) - '0';
- (*ptr)++;
- }
- if (begin == *ptr) {
- return TIMELIB_UNSET;
- }
- return acc;
- }
- /* [+-]? [0-9]+ ( ":" [0-9]+ ( ":" [0-9]+ )? )? */
- static timelib_sll read_offset(char **ptr)
- {
- const char *begin;
- int bias = read_sign(ptr);
- int hours = 0;
- int minutes = 0;
- int seconds = 0;
- begin = *ptr;
- // read through to : or non-digit for hours
- hours = read_number(ptr);
- if (hours == TIMELIB_UNSET) {
- return hours;
- }
- // check for optional minutes
- if (**ptr == ':') {
- (*ptr)++; // skip ':'
- minutes = read_number(ptr);
- if (minutes == TIMELIB_UNSET) {
- return minutes;
- }
- }
- // check for optional seconds
- if (**ptr == ':') {
- (*ptr)++; // skip ':'
- seconds = read_number(ptr);
- if (seconds == TIMELIB_UNSET) {
- return seconds;
- }
- }
- if (begin == *ptr) {
- return TIMELIB_UNSET;
- }
- // multiplication with -1, because the offset in the identifier is the
- // 'wrong' way around as for example EST5 is UTC-5 (and not +5)
- return -1 * bias * (hours * 3600 + minutes * 60 + seconds);
- }
- // Mw.m.d
- static timelib_posix_trans_info* read_trans_spec_mwd(char **ptr)
- {
- timelib_posix_trans_info *tmp = timelib_posix_trans_info_ctor();
- tmp->type = TIMELIB_POSIX_TRANS_TYPE_MWD;
- // Skip 'M'
- (*ptr)++;
- tmp->mwd.month = read_number(ptr);
- if (tmp->mwd.month == TIMELIB_UNSET) {
- goto fail;
- }
- // check for '.' and skip it
- if (**ptr != '.') {
- goto fail;
- }
- (*ptr)++;
- tmp->mwd.week = read_number(ptr);
- if (tmp->mwd.week == TIMELIB_UNSET) {
- goto fail;
- }
- // check for '.' and skip it
- if (**ptr != '.') {
- goto fail;
- }
- (*ptr)++;
- tmp->mwd.dow = read_number(ptr);
- if (tmp->mwd.dow == TIMELIB_UNSET) {
- goto fail;
- }
- return tmp;
- fail:
- timelib_posix_trans_info_dtor(tmp);
- return NULL;
- }
- // (Jn | n | Mw.m.d) ( /time )?
- static timelib_posix_trans_info* read_transition_spec(char **ptr)
- {
- timelib_posix_trans_info *tmp;
- if (**ptr == 'M') {
- tmp = read_trans_spec_mwd(ptr);
- if (!tmp) {
- return NULL;
- }
- } else {
- tmp = timelib_posix_trans_info_ctor();
- if (**ptr == 'J') {
- tmp->type = TIMELIB_POSIX_TRANS_TYPE_JULIAN_NO_FEB29;
- (*ptr)++;
- }
- tmp->days = read_number(ptr);
- if (tmp->days == TIMELIB_UNSET) {
- goto fail;
- }
- }
- // Check for the optional hour
- if (**ptr == '/') {
- (*ptr)++;
- tmp->hour = read_offset(ptr);
- if (tmp->hour == TIMELIB_UNSET) {
- goto fail;
- }
- // as the bias for normal offsets = -1, we need to reverse it here
- tmp->hour = -tmp->hour;
- }
- return tmp;
- fail:
- timelib_posix_trans_info_dtor(tmp);
- return NULL;
- }
- static timelib_posix_trans_info* timelib_posix_trans_info_ctor(void)
- {
- timelib_posix_trans_info *tmp;
- tmp = timelib_calloc(1, sizeof(timelib_posix_trans_info));
- tmp->type = TIMELIB_POSIX_TRANS_TYPE_JULIAN_FEB29;
- tmp->hour = 2 * 3600;
- return tmp;
- }
- static void timelib_posix_trans_info_dtor(timelib_posix_trans_info* ts)
- {
- timelib_free(ts);
- }
- void timelib_posix_str_dtor(timelib_posix_str *ps)
- {
- if (ps->std) {
- timelib_free(ps->std);
- }
- if (ps->dst) {
- timelib_free(ps->dst);
- }
- if (ps->dst_begin) {
- timelib_posix_trans_info_dtor(ps->dst_begin);
- }
- if (ps->dst_end) {
- timelib_posix_trans_info_dtor(ps->dst_end);
- }
- timelib_free(ps);
- }
- timelib_posix_str* timelib_parse_posix_str(const char *posix)
- {
- timelib_posix_str *tmp = timelib_calloc(1, sizeof(timelib_posix_str));
- char *ptr = (char*) posix;
- // read standard description (ie. EST or <-03>)
- tmp->std = read_description(&ptr);
- if (!tmp->std) {
- timelib_posix_str_dtor(tmp);
- return NULL;
- }
- // read required offset
- tmp->std_offset = read_offset(&ptr);
- if (tmp->std_offset == TIMELIB_UNSET) {
- timelib_posix_str_dtor(tmp);
- return NULL;
- }
- // if we're at the end return, otherwise we'll continue to try to parse
- // the dst abbreviation and spec
- if (*ptr == '\0') {
- return tmp;
- }
- // assume dst is there, and initialise offset
- tmp->dst_offset = tmp->std_offset + 3600;
- tmp->dst = read_description(&ptr);
- if (!tmp->dst) {
- timelib_posix_str_dtor(tmp);
- return NULL;
- }
- // if we have a "," here, then the dst offset is the standard offset +
- // 3600 seconds, otherwise, try to parse the dst offset
- if (*ptr != ',' && *ptr != '\0') {
- tmp->dst_offset = read_offset(&ptr);
- if (tmp->dst_offset == TIMELIB_UNSET) {
- timelib_posix_str_dtor(tmp);
- return NULL;
- }
- }
- // if we *don't* have a "," here, we're missing the dst transitions
- // ,start[/time],end[/time]
- if (*ptr != ',') {
- timelib_posix_str_dtor(tmp);
- return NULL;
- }
- ptr++; // skip ','
- // start[/time]
- tmp->dst_begin = read_transition_spec(&ptr);
- if (!tmp->dst_begin) {
- timelib_posix_str_dtor(tmp);
- return NULL;
- }
- // if we *don't* have a "," here, we're missing the dst end transition
- // ,end[/time]
- if (*ptr != ',') {
- timelib_posix_str_dtor(tmp);
- return NULL;
- }
- ptr++; // skip ','
- // end[/time]
- tmp->dst_end = read_transition_spec(&ptr);
- if (!tmp->dst_end) {
- timelib_posix_str_dtor(tmp);
- return NULL;
- }
- // make sure there is no trailing data
- if (*ptr != '\0') {
- timelib_posix_str_dtor(tmp);
- return NULL;
- }
- return tmp;
- }
- static const int month_lengths[2][MONTHS_PER_YEAR] = {
- { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, // normal year
- { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } // leap year
- };
- /* This function is adapted from the 'localtime.c' function 'transtime' as bundled with the 'tzcode' project
- * from IANA, and is public domain licensed. */
- static timelib_sll calc_transition(timelib_posix_trans_info *psi, timelib_sll year)
- {
- int leap_year = timelib_is_leap(year);
- switch (psi->type) {
- case TIMELIB_POSIX_TRANS_TYPE_JULIAN_NO_FEB29: {
- timelib_sll value = (psi->days - 1);
- if (leap_year && psi->days >= 60) {
- value++;
- }
- return value * SECS_PER_DAY;
- }
- case TIMELIB_POSIX_TRANS_TYPE_JULIAN_FEB29: {
- return psi->days * SECS_PER_DAY;
- }
- case TIMELIB_POSIX_TRANS_TYPE_MWD: {
- /*
- * Mm.n.d - nth "dth day" of month m.
- */
- int i, d, m1, yy0, yy1, yy2, dow;
- timelib_sll value = 0;
- /* Use Zeller's Congruence to get day-of-week of first day of
- * month. */
- m1 = (psi->mwd.month + 9) % 12 + 1;
- yy0 = (psi->mwd.month <= 2) ? (year - 1) : year;
- yy1 = yy0 / 100;
- yy2 = yy0 % 100;
- dow = ((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
- if (dow < 0) {
- dow += DAYS_PER_WEEK;
- }
- /* "dow" is the day-of-week of the first day of the month. Get the
- * day-of-month (zero-origin) of the first "dow" day of the month. */
- d = psi->mwd.dow - dow;
- if (d < 0) {
- d += DAYS_PER_WEEK;
- }
- for (i = 1; i < psi->mwd.week; ++i) {
- if (d + DAYS_PER_WEEK >= month_lengths[leap_year][psi->mwd.month - 1]) {
- break;
- }
- d += DAYS_PER_WEEK;
- }
- /* "d" is the day-of-month (zero-origin) of the day we want. */
- value = d * SECS_PER_DAY;
- for (i = 0; i < psi->mwd.month - 1; ++i) {
- value += month_lengths[leap_year][i] * SECS_PER_DAY;
- }
- return value;
- } break;
- }
- return 0;
- }
- static timelib_sll count_leap_years(timelib_sll y)
- {
- /* Because we want this for Jan 1, the leap day hasn't happend yet, so
- * subtract one of year before we calculate */
- y--;
- return (y/4) - (y/100) + (y/400);
- }
- timelib_sll timelib_ts_at_start_of_year(timelib_sll year)
- {
- timelib_sll epoch_leap_years = count_leap_years(1970);
- timelib_sll current_leap_years = count_leap_years(year);
- return SECS_PER_DAY * (
- ((year-1970) * DAYS_PER_YEAR)
- + current_leap_years
- - epoch_leap_years
- );
- }
- void timelib_get_transitions_for_year(timelib_tzinfo *tz, timelib_sll year, timelib_posix_transitions *transitions)
- {
- timelib_sll trans_begin; /* Since start of the year */
- timelib_sll trans_end;
- timelib_sll year_begin_ts = timelib_ts_at_start_of_year(year);
- trans_begin = year_begin_ts;
- trans_begin += calc_transition(tz->posix_info->dst_begin, year);
- trans_begin += tz->posix_info->dst_begin->hour;
- trans_begin -= tz->posix_info->std_offset;
- trans_end = year_begin_ts;
- trans_end += calc_transition(tz->posix_info->dst_end, year);
- trans_end += tz->posix_info->dst_end->hour;
- trans_end -= tz->posix_info->dst_offset;
- if (trans_begin < trans_end) {
- transitions->times[transitions->count ] = trans_begin;
- transitions->times[transitions->count+1] = trans_end;
- transitions->types[transitions->count ] = tz->posix_info->type_index_dst_type;
- transitions->types[transitions->count+1] = tz->posix_info->type_index_std_type;
- } else {
- transitions->times[transitions->count+1] = trans_begin;
- transitions->times[transitions->count ] = trans_end;
- transitions->types[transitions->count+1] = tz->posix_info->type_index_dst_type;
- transitions->types[transitions->count ] = tz->posix_info->type_index_std_type;
- }
- transitions->count += 2;
- }
- ttinfo* timelib_fetch_posix_timezone_offset(timelib_tzinfo *tz, timelib_sll ts, timelib_sll *transition_time)
- {
- timelib_sll year;
- timelib_time dummy;
- timelib_posix_transitions transitions = { 0 };
- size_t i;
- /* If there is no second (dst_end) information, the UTC offset is valid for the whole year, so no need to
- * do clever logic */
- if (!tz->posix_info->dst_end) {
- if (transition_time) {
- *transition_time = tz->trans[tz->bit64.timecnt - 1];
- }
- return &(tz->type[tz->posix_info->type_index_std_type]);
- }
- /* Find 'year' (UTC) for 'ts' */
- timelib_unixtime2gmt(&dummy, ts);
- year = dummy.y;
- /* Calculate transition times for 'year-1', 'year', and 'year+1' */
- timelib_get_transitions_for_year(tz, year - 1, &transitions);
- timelib_get_transitions_for_year(tz, year, &transitions);
- timelib_get_transitions_for_year(tz, year + 1, &transitions);
- /* Check where the 'ts' falls in the 4 transitions */
- for (i = 1; i < transitions.count; i++) {
- if (ts < transitions.times[i]) {
- if (transition_time) {
- *transition_time = transitions.times[i - 1];
- }
- return &(tz->type[transitions.types[i - 1]]);
- }
- }
- return NULL;
- }
|