summaryrefslogtreecommitdiff
path: root/src/timezone/localtime.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/timezone/localtime.c')
-rw-r--r--src/timezone/localtime.c114
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.
*/