LCOV - code coverage report
Current view: top level - src/timezone - pgtz.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 90.4 % 187 169
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 10 10
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 67.0 % 100 67

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * pgtz.c
       4                 :             :  *        Timezone Library Integration Functions
       5                 :             :  *
       6                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7                 :             :  *
       8                 :             :  * IDENTIFICATION
       9                 :             :  *        src/timezone/pgtz.c
      10                 :             :  *
      11                 :             :  *-------------------------------------------------------------------------
      12                 :             :  */
      13                 :             : #include "postgres.h"
      14                 :             : 
      15                 :             : #include <ctype.h>
      16                 :             : #include <fcntl.h>
      17                 :             : #include <time.h>
      18                 :             : 
      19                 :             : #include "common/file_utils.h"
      20                 :             : #include "datatype/timestamp.h"
      21                 :             : #include "miscadmin.h"
      22                 :             : #include "pgtz.h"
      23                 :             : #include "storage/fd.h"
      24                 :             : #include "utils/hsearch.h"
      25                 :             : 
      26                 :             : 
      27                 :             : /* Current session timezone (controlled by TimeZone GUC) */
      28                 :             : pg_tz      *session_timezone = NULL;
      29                 :             : 
      30                 :             : /* Current log timezone (controlled by log_timezone GUC) */
      31                 :             : pg_tz      *log_timezone = NULL;
      32                 :             : 
      33                 :             : 
      34                 :             : static bool scan_directory_ci(const char *dirname,
      35                 :             :                                                           const char *fname, int fnamelen,
      36                 :             :                                                           char *canonname, int canonnamelen);
      37                 :             : 
      38                 :             : 
      39                 :             : /*
      40                 :             :  * Return full pathname of timezone data directory
      41                 :             :  */
      42                 :             : static const char *
      43                 :        2090 : pg_TZDIR(void)
      44                 :             : {
      45                 :             : #ifndef SYSTEMTZDIR
      46                 :             :         /* normal case: timezone stuff is under our share dir */
      47                 :             :         static bool done_tzdir = false;
      48                 :             :         static char tzdir[MAXPGPATH];
      49                 :             : 
      50         [ +  + ]:        2090 :         if (done_tzdir)
      51                 :        2086 :                 return tzdir;
      52                 :             : 
      53                 :           4 :         get_share_path(my_exec_path, tzdir);
      54                 :           4 :         strlcpy(tzdir + strlen(tzdir), "/timezone", MAXPGPATH - strlen(tzdir));
      55                 :             : 
      56                 :           4 :         done_tzdir = true;
      57                 :           4 :         return tzdir;
      58                 :             : #else
      59                 :             :         /* we're configured to use system's timezone database */
      60                 :             :         return SYSTEMTZDIR;
      61                 :             : #endif
      62                 :        2090 : }
      63                 :             : 
      64                 :             : 
      65                 :             : /*
      66                 :             :  * Given a timezone name, open() the timezone data file.  Return the
      67                 :             :  * file descriptor if successful, -1 if not.
      68                 :             :  *
      69                 :             :  * The input name is searched for case-insensitively (we assume that the
      70                 :             :  * timezone database does not contain case-equivalent names).
      71                 :             :  *
      72                 :             :  * If "canonname" is not NULL, then on success the canonical spelling of the
      73                 :             :  * given name is stored there (the buffer must be > TZ_STRLEN_MAX bytes!).
      74                 :             :  */
      75                 :             : int
      76                 :        2088 : pg_open_tzfile(const char *name, char *canonname)
      77                 :             : {
      78                 :        2088 :         const char *fname;
      79                 :        2088 :         char            fullname[MAXPGPATH];
      80                 :        2088 :         int                     fullnamelen;
      81                 :        2088 :         int                     orignamelen;
      82                 :             : 
      83                 :             :         /* Initialize fullname with base name of tzdata directory */
      84                 :        2088 :         strlcpy(fullname, pg_TZDIR(), sizeof(fullname));
      85                 :        2088 :         orignamelen = fullnamelen = strlen(fullname);
      86                 :             : 
      87         [ -  + ]:        2088 :         if (fullnamelen + 1 + strlen(name) >= MAXPGPATH)
      88                 :           0 :                 return -1;                              /* not gonna fit */
      89                 :             : 
      90                 :             :         /*
      91                 :             :          * If the caller doesn't need the canonical spelling, first just try to
      92                 :             :          * open the name as-is.  This can be expected to succeed if the given name
      93                 :             :          * is already case-correct, or if the filesystem is case-insensitive; and
      94                 :             :          * we don't need to distinguish those situations if we aren't tasked with
      95                 :             :          * reporting the canonical spelling.
      96                 :             :          */
      97         [ +  + ]:        2088 :         if (canonname == NULL)
      98                 :             :         {
      99                 :        1201 :                 int                     result;
     100                 :             : 
     101                 :        1201 :                 fullname[fullnamelen] = '/';
     102                 :             :                 /* test above ensured this will fit: */
     103                 :        1201 :                 strcpy(fullname + fullnamelen + 1, name);
     104                 :        1201 :                 result = open(fullname, O_RDONLY | PG_BINARY, 0);
     105         [ +  - ]:        1201 :                 if (result >= 0)
     106                 :        1201 :                         return result;
     107                 :             :                 /* If that didn't work, fall through to do it the hard way */
     108                 :           0 :                 fullname[fullnamelen] = '\0';
     109         [ +  - ]:        1201 :         }
     110                 :             : 
     111                 :             :         /*
     112                 :             :          * Loop to split the given name into directory levels; for each level,
     113                 :             :          * search using scan_directory_ci().
     114                 :             :          */
     115                 :         887 :         fname = name;
     116                 :        1715 :         for (;;)
     117                 :             :         {
     118                 :        1715 :                 const char *slashptr;
     119                 :        1715 :                 int                     fnamelen;
     120                 :             : 
     121                 :        1715 :                 slashptr = strchr(fname, '/');
     122         [ +  + ]:        1715 :                 if (slashptr)
     123                 :         833 :                         fnamelen = slashptr - fname;
     124                 :             :                 else
     125                 :         882 :                         fnamelen = strlen(fname);
     126   [ +  +  +  + ]:        3430 :                 if (!scan_directory_ci(fullname, fname, fnamelen,
     127                 :        1715 :                                                            fullname + fullnamelen + 1,
     128                 :        1715 :                                                            MAXPGPATH - fullnamelen - 1))
     129                 :          59 :                         return -1;
     130                 :        1656 :                 fullname[fullnamelen++] = '/';
     131                 :        1656 :                 fullnamelen += strlen(fullname + fullnamelen);
     132         [ +  + ]:        1656 :                 if (slashptr)
     133                 :         828 :                         fname = slashptr + 1;
     134                 :             :                 else
     135                 :         828 :                         break;
     136      [ +  +  + ]:        1715 :         }
     137                 :             : 
     138         [ -  + ]:         828 :         if (canonname)
     139                 :         828 :                 strlcpy(canonname, fullname + orignamelen + 1, TZ_STRLEN_MAX + 1);
     140                 :             : 
     141                 :         828 :         return open(fullname, O_RDONLY | PG_BINARY, 0);
     142                 :        2088 : }
     143                 :             : 
     144                 :             : 
     145                 :             : /*
     146                 :             :  * Scan specified directory for a case-insensitive match to fname
     147                 :             :  * (of length fnamelen --- fname may not be null terminated!).  If found,
     148                 :             :  * copy the actual filename into canonname and return true.
     149                 :             :  */
     150                 :             : static bool
     151                 :        1715 : scan_directory_ci(const char *dirname, const char *fname, int fnamelen,
     152                 :             :                                   char *canonname, int canonnamelen)
     153                 :             : {
     154                 :        1715 :         bool            found = false;
     155                 :        1715 :         DIR                *dirdesc;
     156                 :        1715 :         struct dirent *direntry;
     157                 :             : 
     158                 :        1715 :         dirdesc = AllocateDir(dirname);
     159                 :             : 
     160         [ +  + ]:       94057 :         while ((direntry = ReadDirExtended(dirdesc, dirname, LOG)) != NULL)
     161                 :             :         {
     162                 :             :                 /*
     163                 :             :                  * Ignore . and .., plus any other "hidden" files.  This is a security
     164                 :             :                  * measure to prevent access to files outside the timezone directory.
     165                 :             :                  */
     166         [ +  + ]:       93998 :                 if (direntry->d_name[0] == '.')
     167                 :        3430 :                         continue;
     168                 :             : 
     169   [ +  +  +  + ]:       90568 :                 if (strlen(direntry->d_name) == fnamelen &&
     170                 :        8643 :                         pg_strncasecmp(direntry->d_name, fname, fnamelen) == 0)
     171                 :             :                 {
     172                 :             :                         /* Found our match */
     173                 :        1656 :                         strlcpy(canonname, direntry->d_name, canonnamelen);
     174                 :        1656 :                         found = true;
     175                 :        1656 :                         break;
     176                 :             :                 }
     177                 :             :         }
     178                 :             : 
     179                 :        1715 :         FreeDir(dirdesc);
     180                 :             : 
     181                 :        3430 :         return found;
     182                 :        1715 : }
     183                 :             : 
     184                 :             : 
     185                 :             : /*
     186                 :             :  * We keep loaded timezones in a hashtable so we don't have to
     187                 :             :  * load and parse the TZ definition file every time one is selected.
     188                 :             :  * Because we want timezone names to be found case-insensitively,
     189                 :             :  * the hash key is the uppercased name of the zone.
     190                 :             :  */
     191                 :             : typedef struct
     192                 :             : {
     193                 :             :         /* tznameupper contains the all-upper-case name of the timezone */
     194                 :             :         char            tznameupper[TZ_STRLEN_MAX + 1];
     195                 :             :         pg_tz           tz;
     196                 :             : } pg_tz_cache;
     197                 :             : 
     198                 :             : static HTAB *timezone_cache = NULL;
     199                 :             : 
     200                 :             : 
     201                 :             : static bool
     202                 :           6 : init_timezone_hashtable(void)
     203                 :             : {
     204                 :           6 :         HASHCTL         hash_ctl;
     205                 :             : 
     206                 :           6 :         hash_ctl.keysize = TZ_STRLEN_MAX + 1;
     207                 :           6 :         hash_ctl.entrysize = sizeof(pg_tz_cache);
     208                 :             : 
     209                 :           6 :         timezone_cache = hash_create("Timezones",
     210                 :             :                                                                  4,
     211                 :             :                                                                  &hash_ctl,
     212                 :             :                                                                  HASH_ELEM | HASH_STRINGS);
     213         [ +  - ]:           6 :         if (!timezone_cache)
     214                 :           0 :                 return false;
     215                 :             : 
     216                 :           6 :         return true;
     217                 :           6 : }
     218                 :             : 
     219                 :             : /*
     220                 :             :  * Load a timezone from file or from cache.
     221                 :             :  * Does not verify that the timezone is acceptable!
     222                 :             :  *
     223                 :             :  * "GMT" is always interpreted as the tzparse() definition, without attempting
     224                 :             :  * to load a definition from the filesystem.  This has a number of benefits:
     225                 :             :  * 1. It's guaranteed to succeed, so we don't have the failure mode wherein
     226                 :             :  * the bootstrap default timezone setting doesn't work (as could happen if
     227                 :             :  * the OS attempts to supply a leap-second-aware version of "GMT").
     228                 :             :  * 2. Because we aren't accessing the filesystem, we can safely initialize
     229                 :             :  * the "GMT" zone definition before my_exec_path is known.
     230                 :             :  * 3. It's quick enough that we don't waste much time when the bootstrap
     231                 :             :  * default timezone setting is later overridden from postgresql.conf.
     232                 :             :  */
     233                 :             : pg_tz *
     234                 :        2565 : pg_tzset(const char *tzname)
     235                 :             : {
     236                 :        2565 :         pg_tz_cache *tzp;
     237                 :        2565 :         struct state tzstate;
     238                 :        2565 :         char            uppername[TZ_STRLEN_MAX + 1];
     239                 :        2565 :         char            canonname[TZ_STRLEN_MAX + 1];
     240                 :        2565 :         char       *p;
     241                 :             : 
     242         [ -  + ]:        2565 :         if (strlen(tzname) > TZ_STRLEN_MAX)
     243                 :           0 :                 return NULL;                    /* not going to fit */
     244                 :             : 
     245         [ +  + ]:        2565 :         if (!timezone_cache)
     246         [ +  - ]:           6 :                 if (!init_timezone_hashtable())
     247                 :           0 :                         return NULL;
     248                 :             : 
     249                 :             :         /*
     250                 :             :          * Upcase the given name to perform a case-insensitive hashtable search.
     251                 :             :          * (We could alternatively downcase it, but we prefer upcase so that we
     252                 :             :          * can get consistently upcased results from tzparse() in case the name is
     253                 :             :          * a POSIX-style timezone spec.)
     254                 :             :          */
     255                 :        2565 :         p = uppername;
     256         [ +  + ]:       32142 :         while (*tzname)
     257                 :       29577 :                 *p++ = pg_toupper((unsigned char) *tzname++);
     258                 :        2565 :         *p = '\0';
     259                 :             : 
     260                 :        5130 :         tzp = (pg_tz_cache *) hash_search(timezone_cache,
     261                 :        2565 :                                                                           uppername,
     262                 :             :                                                                           HASH_FIND,
     263                 :             :                                                                           NULL);
     264         [ +  + ]:        2565 :         if (tzp)
     265                 :             :         {
     266                 :             :                 /* Timezone found in cache, nothing more to do */
     267                 :        1672 :                 return &tzp->tz;
     268                 :             :         }
     269                 :             : 
     270                 :             :         /*
     271                 :             :          * "GMT" is always sent to tzparse(), as per discussion above.
     272                 :             :          */
     273         [ +  + ]:         893 :         if (strcmp(uppername, "GMT") == 0)
     274                 :             :         {
     275         [ +  - ]:           6 :                 if (!tzparse(uppername, &tzstate, true))
     276                 :             :                 {
     277                 :             :                         /* This really, really should not happen ... */
     278   [ #  #  #  # ]:           0 :                         elog(ERROR, "could not initialize GMT time zone");
     279                 :           0 :                 }
     280                 :             :                 /* Use uppercase name as canonical */
     281                 :           6 :                 strcpy(canonname, uppername);
     282                 :           6 :         }
     283         [ +  + ]:         887 :         else if (tzload(uppername, canonname, &tzstate, true) != 0)
     284                 :             :         {
     285   [ +  -  +  + ]:          59 :                 if (uppername[0] == ':' || !tzparse(uppername, &tzstate, false))
     286                 :             :                 {
     287                 :             :                         /* Unknown timezone. Fail our call instead of loading GMT! */
     288                 :          17 :                         return NULL;
     289                 :             :                 }
     290                 :             :                 /* For POSIX timezone specs, use uppercase name as canonical */
     291                 :          42 :                 strcpy(canonname, uppername);
     292                 :          42 :         }
     293                 :             : 
     294                 :             :         /* Save timezone in the cache */
     295                 :        1752 :         tzp = (pg_tz_cache *) hash_search(timezone_cache,
     296                 :         876 :                                                                           uppername,
     297                 :             :                                                                           HASH_ENTER,
     298                 :             :                                                                           NULL);
     299                 :             : 
     300                 :             :         /* hash_search already copied uppername into the hash key */
     301                 :         876 :         strcpy(tzp->tz.TZname, canonname);
     302                 :         876 :         memcpy(&tzp->tz.state, &tzstate, sizeof(tzstate));
     303                 :             : 
     304                 :         876 :         return &tzp->tz;
     305                 :        2565 : }
     306                 :             : 
     307                 :             : /*
     308                 :             :  * Load a fixed-GMT-offset timezone.
     309                 :             :  * This is used for SQL-spec SET TIME ZONE INTERVAL 'foo' cases.
     310                 :             :  * It's otherwise equivalent to pg_tzset().
     311                 :             :  *
     312                 :             :  * The GMT offset is specified in seconds, positive values meaning west of
     313                 :             :  * Greenwich (ie, POSIX not ISO sign convention).  However, we use ISO
     314                 :             :  * sign convention in the displayable abbreviation for the zone.
     315                 :             :  *
     316                 :             :  * Caution: this can fail (return NULL) if the specified offset is outside
     317                 :             :  * the range allowed by the zic library.
     318                 :             :  */
     319                 :             : pg_tz *
     320                 :          16 : pg_tzset_offset(long gmtoffset)
     321                 :             : {
     322         [ +  + ]:          16 :         long            absoffset = (gmtoffset < 0) ? -gmtoffset : gmtoffset;
     323                 :          16 :         char            offsetstr[64];
     324                 :          16 :         char            tzname[128];
     325                 :             : 
     326                 :          32 :         snprintf(offsetstr, sizeof(offsetstr),
     327                 :          16 :                          "%02ld", absoffset / SECS_PER_HOUR);
     328                 :          16 :         absoffset %= SECS_PER_HOUR;
     329         [ +  + ]:          16 :         if (absoffset != 0)
     330                 :             :         {
     331                 :           6 :                 snprintf(offsetstr + strlen(offsetstr),
     332                 :           3 :                                  sizeof(offsetstr) - strlen(offsetstr),
     333                 :           3 :                                  ":%02ld", absoffset / SECS_PER_MINUTE);
     334                 :           3 :                 absoffset %= SECS_PER_MINUTE;
     335         [ +  - ]:           3 :                 if (absoffset != 0)
     336                 :           0 :                         snprintf(offsetstr + strlen(offsetstr),
     337                 :           0 :                                          sizeof(offsetstr) - strlen(offsetstr),
     338                 :           0 :                                          ":%02ld", absoffset);
     339                 :           3 :         }
     340         [ +  + ]:          16 :         if (gmtoffset > 0)
     341                 :           8 :                 snprintf(tzname, sizeof(tzname), "<-%s>+%s",
     342                 :           4 :                                  offsetstr, offsetstr);
     343                 :             :         else
     344                 :          24 :                 snprintf(tzname, sizeof(tzname), "<+%s>-%s",
     345                 :          12 :                                  offsetstr, offsetstr);
     346                 :             : 
     347                 :          32 :         return pg_tzset(tzname);
     348                 :          16 : }
     349                 :             : 
     350                 :             : 
     351                 :             : /*
     352                 :             :  * Initialize timezone library
     353                 :             :  *
     354                 :             :  * This is called before GUC variable initialization begins.  Its purpose
     355                 :             :  * is to ensure that log_timezone has a valid value before any logging GUC
     356                 :             :  * variables could become set to values that require elog.c to provide
     357                 :             :  * timestamps (e.g., log_line_prefix).  We may as well initialize
     358                 :             :  * session_timezone to something valid, too.
     359                 :             :  */
     360                 :             : void
     361                 :           6 : pg_timezone_initialize(void)
     362                 :             : {
     363                 :             :         /*
     364                 :             :          * We may not yet know where PGSHAREDIR is (in particular this is true in
     365                 :             :          * an EXEC_BACKEND subprocess).  So use "GMT", which pg_tzset forces to be
     366                 :             :          * interpreted without reference to the filesystem.  This corresponds to
     367                 :             :          * the bootstrap default for these variables in guc_parameters.dat,
     368                 :             :          * although in principle it could be different.
     369                 :             :          */
     370                 :           6 :         session_timezone = pg_tzset("GMT");
     371                 :           6 :         log_timezone = session_timezone;
     372                 :           6 : }
     373                 :             : 
     374                 :             : 
     375                 :             : /*
     376                 :             :  * Functions to enumerate available timezones
     377                 :             :  *
     378                 :             :  * Note that pg_tzenumerate_next() will return a pointer into the pg_tzenum
     379                 :             :  * structure, so the data is only valid up to the next call.
     380                 :             :  *
     381                 :             :  * All data is allocated using palloc in the current context.
     382                 :             :  */
     383                 :             : #define MAX_TZDIR_DEPTH 10
     384                 :             : 
     385                 :             : struct pg_tzenum
     386                 :             : {
     387                 :             :         int                     baselen;
     388                 :             :         int                     depth;
     389                 :             :         DIR                *dirdesc[MAX_TZDIR_DEPTH];
     390                 :             :         char       *dirname[MAX_TZDIR_DEPTH];
     391                 :             :         struct pg_tz tz;
     392                 :             : };
     393                 :             : 
     394                 :             : /* typedef pg_tzenum is declared in pgtime.h */
     395                 :             : 
     396                 :             : pg_tzenum *
     397                 :           2 : pg_tzenumerate_start(void)
     398                 :             : {
     399                 :           2 :         pg_tzenum  *ret = palloc0_object(pg_tzenum);
     400                 :           2 :         char       *startdir = pstrdup(pg_TZDIR());
     401                 :             : 
     402                 :           2 :         ret->baselen = strlen(startdir) + 1;
     403                 :           2 :         ret->depth = 0;
     404                 :           2 :         ret->dirname[0] = startdir;
     405                 :           2 :         ret->dirdesc[0] = AllocateDir(startdir);
     406         [ +  - ]:           2 :         if (!ret->dirdesc[0])
     407   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     408                 :             :                                 (errcode_for_file_access(),
     409                 :             :                                  errmsg("could not open directory \"%s\": %m", startdir)));
     410                 :           4 :         return ret;
     411                 :           2 : }
     412                 :             : 
     413                 :             : void
     414                 :           2 : pg_tzenumerate_end(pg_tzenum *dir)
     415                 :             : {
     416         [ -  + ]:           2 :         while (dir->depth >= 0)
     417                 :             :         {
     418                 :           0 :                 FreeDir(dir->dirdesc[dir->depth]);
     419                 :           0 :                 pfree(dir->dirname[dir->depth]);
     420                 :           0 :                 dir->depth--;
     421                 :             :         }
     422                 :           2 :         pfree(dir);
     423                 :           2 : }
     424                 :             : 
     425                 :             : pg_tz *
     426                 :        1198 : pg_tzenumerate_next(pg_tzenum *dir)
     427                 :             : {
     428         [ +  + ]:        1364 :         while (dir->depth >= 0)
     429                 :             :         {
     430                 :        1362 :                 struct dirent *direntry;
     431                 :        1362 :                 char            fullname[MAXPGPATH * 2];
     432                 :             : 
     433                 :        1362 :                 direntry = ReadDir(dir->dirdesc[dir->depth], dir->dirname[dir->depth]);
     434                 :             : 
     435         [ +  + ]:        1362 :                 if (!direntry)
     436                 :             :                 {
     437                 :             :                         /* End of this directory */
     438                 :          42 :                         FreeDir(dir->dirdesc[dir->depth]);
     439                 :          42 :                         pfree(dir->dirname[dir->depth]);
     440                 :          42 :                         dir->depth--;
     441                 :          42 :                         continue;
     442                 :             :                 }
     443                 :             : 
     444         [ +  + ]:        1320 :                 if (direntry->d_name[0] == '.')
     445                 :          84 :                         continue;
     446                 :             : 
     447                 :        2472 :                 snprintf(fullname, sizeof(fullname), "%s/%s",
     448                 :        1236 :                                  dir->dirname[dir->depth], direntry->d_name);
     449                 :             : 
     450         [ +  + ]:        1236 :                 if (get_dirent_type(fullname, direntry, true, ERROR) == PGFILETYPE_DIR)
     451                 :             :                 {
     452                 :             :                         /* Step into the subdirectory */
     453         [ +  - ]:          40 :                         if (dir->depth >= MAX_TZDIR_DEPTH - 1)
     454   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
     455                 :             :                                                 (errmsg_internal("timezone directory stack overflow")));
     456                 :          40 :                         dir->depth++;
     457                 :          40 :                         dir->dirname[dir->depth] = pstrdup(fullname);
     458                 :          40 :                         dir->dirdesc[dir->depth] = AllocateDir(fullname);
     459         [ +  - ]:          40 :                         if (!dir->dirdesc[dir->depth])
     460   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
     461                 :             :                                                 (errcode_for_file_access(),
     462                 :             :                                                  errmsg("could not open directory \"%s\": %m",
     463                 :             :                                                                 fullname)));
     464                 :             : 
     465                 :             :                         /* Start over reading in the new directory */
     466                 :          40 :                         continue;
     467                 :             :                 }
     468                 :             : 
     469                 :             :                 /*
     470                 :             :                  * Load this timezone using tzload() not pg_tzset(), so we don't fill
     471                 :             :                  * the cache.  Also, don't ask for the canonical spelling: we already
     472                 :             :                  * know it, and pg_open_tzfile's way of finding it out is pretty
     473                 :             :                  * inefficient.
     474                 :             :                  */
     475         [ -  + ]:        1196 :                 if (tzload(fullname + dir->baselen, NULL, &dir->tz.state, true) != 0)
     476                 :             :                 {
     477                 :             :                         /* Zone could not be loaded, ignore it */
     478                 :           0 :                         continue;
     479                 :             :                 }
     480                 :             : 
     481         [ +  - ]:        1196 :                 if (!pg_tz_acceptable(&dir->tz))
     482                 :             :                 {
     483                 :             :                         /* Ignore leap-second zones */
     484                 :           0 :                         continue;
     485                 :             :                 }
     486                 :             : 
     487                 :             :                 /* OK, return the canonical zone name spelling. */
     488                 :        1196 :                 strlcpy(dir->tz.TZname, fullname + dir->baselen,
     489                 :             :                                 sizeof(dir->tz.TZname));
     490                 :             : 
     491                 :             :                 /* Timezone loaded OK. */
     492                 :        1196 :                 return &dir->tz;
     493      [ -  +  + ]:        1362 :         }
     494                 :             : 
     495                 :             :         /* Nothing more found */
     496                 :           2 :         return NULL;
     497                 :        1198 : }
        

Generated by: LCOV version 2.3.2-1