diff options
Diffstat (limited to 'src/timezone/localtime.c')
-rw-r--r-- | src/timezone/localtime.c | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/src/timezone/localtime.c b/src/timezone/localtime.c index 21516c65082..8eb02ef1469 100644 --- a/src/timezone/localtime.c +++ b/src/timezone/localtime.c @@ -1844,6 +1844,120 @@ pg_interpret_timezone_abbrev(const char *abbrev, } /* + * Detect whether a timezone abbreviation is defined within the given zone. + * + * This is similar to pg_interpret_timezone_abbrev() but is not concerned + * with a specific point in time. We want to know if the abbreviation is + * known at all, and if so whether it has one meaning or several. + * + * Returns true if the abbreviation is known, false if not. + * If the abbreviation is known and has a single meaning (only one value + * of gmtoff/isdst), sets *isfixed = true and sets *gmtoff and *isdst. + * If there are multiple meanings, sets *isfixed = false. + * + * Note: abbrev is matched case-sensitively; it should be all-upper-case. + */ +bool +pg_timezone_abbrev_is_known(const char *abbrev, + bool *isfixed, + long int *gmtoff, + int *isdst, + const pg_tz *tz) +{ + bool result = false; + const struct state *sp = &tz->state; + const char *abbrs; + int abbrind; + + /* + * Locate the abbreviation in the zone's abbreviation list. We assume + * there are not duplicates in the list. + */ + abbrs = sp->chars; + abbrind = 0; + while (abbrind < sp->charcnt) + { + if (strcmp(abbrev, abbrs + abbrind) == 0) + break; + while (abbrs[abbrind] != '\0') + abbrind++; + abbrind++; + } + if (abbrind >= sp->charcnt) + return false; /* definitely not there */ + + /* + * Scan the ttinfo array to find uses of the abbreviation. + */ + for (int i = 0; i < sp->typecnt; i++) + { + const struct ttinfo *ttisp = &sp->ttis[i]; + + if (ttisp->tt_desigidx == abbrind) + { + if (!result) + { + /* First usage */ + *isfixed = true; /* for the moment */ + *gmtoff = ttisp->tt_utoff; + *isdst = ttisp->tt_isdst; + result = true; + } + else + { + /* Second or later usage, does it match? */ + if (*gmtoff != ttisp->tt_utoff || + *isdst != ttisp->tt_isdst) + { + *isfixed = false; + break; /* no point in looking further */ + } + } + } + } + + return result; +} + +/* + * Iteratively fetch all the abbreviations used in the given time zone. + * + * *indx is a state counter that the caller must initialize to zero + * before the first call, and not touch between calls. + * + * Returns the next known abbreviation, or NULL if there are no more. + * + * Note: the caller typically applies pg_interpret_timezone_abbrev() + * to each result. While that nominally results in O(N^2) time spent + * searching the sp->chars[] array, we don't expect any zone to have + * enough abbreviations to make that meaningful. + */ +const char * +pg_get_next_timezone_abbrev(int *indx, + const pg_tz *tz) +{ + const char *result; + const struct state *sp = &tz->state; + const char *abbrs; + int abbrind; + + /* If we're still in range, the result is the current abbrev. */ + abbrs = sp->chars; + abbrind = *indx; + if (abbrind < 0 || abbrind >= sp->charcnt) + return NULL; + result = abbrs + abbrind; + + /* Advance *indx past this abbrev and its trailing null. */ + while (abbrs[abbrind] != '\0') + abbrind++; + abbrind++; + *indx = abbrind; + + return result; +} + +/* * If the given timezone uses only one GMT offset, store that offset * into *gmtoff and return true, else return false. */ |