LCOV - code coverage report
Current view: top level - src/timezone - strftime.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 29.7 % 236 70
Test Date: 2026-07-03 19:57:34 Functions: 100.0 % 5 5
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 21.0 % 138 29

             Branch data     Line data    Source code
       1                 :             : /* Convert a broken-down timestamp to a string.  */
       2                 :             : 
       3                 :             : /*
       4                 :             :  * Copyright 1989 The Regents of the University of California.
       5                 :             :  * All rights reserved.
       6                 :             :  *
       7                 :             :  * Redistribution and use in source and binary forms, with or without
       8                 :             :  * modification, are permitted provided that the following conditions
       9                 :             :  * are met:
      10                 :             :  * 1. Redistributions of source code must retain the above copyright
      11                 :             :  *    notice, this list of conditions and the following disclaimer.
      12                 :             :  * 2. Redistributions in binary form must reproduce the above copyright
      13                 :             :  *    notice, this list of conditions and the following disclaimer in the
      14                 :             :  *    documentation and/or other materials provided with the distribution.
      15                 :             :  * 3. Neither the name of the University nor the names of its contributors
      16                 :             :  *    may be used to endorse or promote products derived from this software
      17                 :             :  *    without specific prior written permission.
      18                 :             :  *
      19                 :             :  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
      20                 :             :  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      21                 :             :  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      22                 :             :  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
      23                 :             :  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      24                 :             :  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
      25                 :             :  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      26                 :             :  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      27                 :             :  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
      28                 :             :  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
      29                 :             :  * SUCH DAMAGE.
      30                 :             :  */
      31                 :             : 
      32                 :             : /*
      33                 :             :  * Based on the UCB version with the copyright notice appearing above.
      34                 :             :  *
      35                 :             :  * This is ANSIish only when "multibyte character == plain character".
      36                 :             :  *
      37                 :             :  * IDENTIFICATION
      38                 :             :  *    src/timezone/strftime.c
      39                 :             :  */
      40                 :             : 
      41                 :             : #include "postgres.h"
      42                 :             : 
      43                 :             : #include <fcntl.h>
      44                 :             : 
      45                 :             : #include "private.h"
      46                 :             : 
      47                 :             : 
      48                 :             : struct lc_time_T
      49                 :             : {
      50                 :             :     const char *mon[MONSPERYEAR];
      51                 :             :     const char *month[MONSPERYEAR];
      52                 :             :     const char *wday[DAYSPERWEEK];
      53                 :             :     const char *weekday[DAYSPERWEEK];
      54                 :             :     const char *X_fmt;
      55                 :             :     const char *x_fmt;
      56                 :             :     const char *c_fmt;
      57                 :             :     const char *am;
      58                 :             :     const char *pm;
      59                 :             :     const char *date_fmt;
      60                 :             : };
      61                 :             : 
      62                 :             : static const struct lc_time_T C_time_locale = {
      63                 :             :     {
      64                 :             :         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
      65                 :             :         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
      66                 :             :     }, {
      67                 :             :         "January", "February", "March", "April", "May", "June",
      68                 :             :         "July", "August", "September", "October", "November", "December"
      69                 :             :     }, {
      70                 :             :         "Sun", "Mon", "Tue", "Wed",
      71                 :             :         "Thu", "Fri", "Sat"
      72                 :             :     }, {
      73                 :             :         "Sunday", "Monday", "Tuesday", "Wednesday",
      74                 :             :         "Thursday", "Friday", "Saturday"
      75                 :             :     },
      76                 :             : 
      77                 :             :     /* X_fmt */
      78                 :             :     "%H:%M:%S",
      79                 :             : 
      80                 :             :     /*
      81                 :             :      * x_fmt C99 and later require this format. Using just numbers (as here)
      82                 :             :      * makes Quakers happier; it's also compatible with SVR4.
      83                 :             :      */
      84                 :             :     "%m/%d/%y",
      85                 :             : 
      86                 :             :     /*
      87                 :             :      * c_fmt C99 and later require this format. Previously this code used "%D
      88                 :             :      * %X", but we now conform to C99. Note that "%a %b %d %H:%M:%S %Y" is
      89                 :             :      * used by Solaris 2.3.
      90                 :             :      */
      91                 :             :     "%a %b %e %T %Y",
      92                 :             : 
      93                 :             :     /* am */
      94                 :             :     "AM",
      95                 :             : 
      96                 :             :     /* pm */
      97                 :             :     "PM",
      98                 :             : 
      99                 :             :     /* date_fmt */
     100                 :             :     "%a %b %e %H:%M:%S %Z %Y"
     101                 :             : };
     102                 :             : 
     103                 :             : enum warn
     104                 :             : {
     105                 :             :     IN_NONE, IN_SOME, IN_THIS, IN_ALL
     106                 :             : };
     107                 :             : 
     108                 :             : static char *_add(const char *str, char *pt, const char *ptlim);
     109                 :             : static char *_conv(int n, const char *format, char *pt, const char *ptlim);
     110                 :             : static char *_fmt(const char *format, const struct pg_tm *t, char *pt, const char *ptlim,
     111                 :             :                   enum warn *warnp);
     112                 :             : static char *_yconv(int a, int b, bool convert_top, bool convert_yy, char *pt, char const *ptlim);
     113                 :             : 
     114                 :             : 
     115                 :             : /*
     116                 :             :  * Convert timestamp t to string s, a caller-allocated buffer of size maxsize,
     117                 :             :  * using the given format pattern.
     118                 :             :  *
     119                 :             :  * Unlike standard strftime(), we guarantee to provide a null-terminated
     120                 :             :  * result even on failure, so long as maxsize > 0.  If we overrun the buffer,
     121                 :             :  * return an empty string rather than risking mis-encoded multibyte output.
     122                 :             :  * (Since this module only supports C locale, you might think multibyte
     123                 :             :  * characters are impossible --- but the time zone name printed by %Z comes
     124                 :             :  * from outside and could contain such.)
     125                 :             :  *
     126                 :             :  * See also timestamptz_to_str.
     127                 :             :  */
     128                 :             : size_t
     129                 :     1426271 : pg_strftime(char *s, size_t maxsize, const char *format, const struct pg_tm *t)
     130                 :             : {
     131                 :             :     char       *p;
     132                 :     1426271 :     int         saved_errno = errno;
     133                 :     1426271 :     enum warn   warn = IN_NONE;
     134                 :             : 
     135                 :     1426271 :     p = _fmt(format, t, s, s + maxsize, &warn);
     136         [ -  + ]:     1426271 :     if (!p)
     137                 :             :     {
     138                 :           0 :         errno = EOVERFLOW;
     139         [ #  # ]:           0 :         if (maxsize > 0)
     140                 :           0 :             *s = '\0';
     141                 :           0 :         return 0;
     142                 :             :     }
     143         [ -  + ]:     1426271 :     if (p == s + maxsize)
     144                 :             :     {
     145                 :           0 :         errno = ERANGE;
     146         [ #  # ]:           0 :         if (maxsize > 0)
     147                 :           0 :             *s = '\0';
     148                 :           0 :         return 0;
     149                 :             :     }
     150                 :     1426271 :     *p = '\0';
     151                 :     1426271 :     errno = saved_errno;
     152                 :     1426271 :     return p - s;
     153                 :             : }
     154                 :             : 
     155                 :             : static char *
     156                 :     1426271 : _fmt(const char *format, const struct pg_tm *t, char *pt,
     157                 :             :      const char *ptlim, enum warn *warnp)
     158                 :             : {
     159                 :     1426271 :     struct lc_time_T const *Locale = &C_time_locale;
     160                 :             : 
     161         [ +  + ]:    24905453 :     for (; *format; ++format)
     162                 :             :     {
     163         [ +  + ]:    23479182 :         if (*format == '%')
     164                 :             :         {
     165                 :     9979088 :     label:
     166   [ -  -  +  -  :     9979088 :             switch (*++format)
          +  -  -  -  +  
          -  -  -  +  -  
          -  -  -  +  +  
          -  -  -  -  +  
          -  -  -  -  -  
          -  -  -  -  -  
          -  +  +  -  -  
                      - ]
     167                 :             :             {
     168                 :           0 :                 default:
     169                 :             : 
     170                 :             :                     /*
     171                 :             :                      * Output unknown conversion specifiers as-is, to aid
     172                 :             :                      * debugging.  This includes '%' at format end.  This
     173                 :             :                      * conforms to C23 section 7.29.3.5 paragraph 6, which
     174                 :             :                      * says behavior is undefined here.
     175                 :             :                      */
     176                 :           0 :                     --format;
     177                 :           0 :                     break;
     178                 :           0 :                 case 'A':
     179         [ #  # ]:           0 :                     pt = _add((t->tm_wday < 0 ||
     180         [ #  # ]:           0 :                                t->tm_wday >= DAYSPERWEEK) ?
     181                 :           0 :                               "?" : Locale->weekday[t->tm_wday],
     182                 :             :                               pt, ptlim);
     183                 :           0 :                     continue;
     184                 :         800 :                 case 'a':
     185         [ +  - ]:        1600 :                     pt = _add((t->tm_wday < 0 ||
     186         [ +  - ]:         800 :                                t->tm_wday >= DAYSPERWEEK) ?
     187                 :         800 :                               "?" : Locale->wday[t->tm_wday],
     188                 :             :                               pt, ptlim);
     189                 :         800 :                     continue;
     190                 :           0 :                 case 'B':
     191         [ #  # ]:           0 :                     pt = _add((t->tm_mon < 0 ||
     192         [ #  # ]:           0 :                                t->tm_mon >= MONSPERYEAR) ?
     193                 :           0 :                               "?" : Locale->month[t->tm_mon],
     194                 :             :                               pt, ptlim);
     195                 :           0 :                     continue;
     196                 :         800 :                 case 'b':
     197                 :             :                 case 'h':
     198         [ +  - ]:        1600 :                     pt = _add((t->tm_mon < 0 ||
     199         [ +  - ]:         800 :                                t->tm_mon >= MONSPERYEAR) ?
     200                 :         800 :                               "?" : Locale->mon[t->tm_mon],
     201                 :             :                               pt, ptlim);
     202                 :         800 :                     continue;
     203                 :           0 :                 case 'C':
     204                 :             : 
     205                 :             :                     /*
     206                 :             :                      * %C used to do a... _fmt("%a %b %e %X %Y", t);
     207                 :             :                      * ...whereas now POSIX 1003.2 calls for something
     208                 :             :                      * completely different. (ado, 1993-05-24)
     209                 :             :                      */
     210                 :           0 :                     pt = _yconv(t->tm_year, TM_YEAR_BASE,
     211                 :             :                                 true, false, pt, ptlim);
     212                 :           0 :                     continue;
     213                 :           0 :                 case 'c':
     214                 :             :                     {
     215                 :           0 :                         enum warn   warn2 = IN_SOME;
     216                 :             : 
     217                 :           0 :                         pt = _fmt(Locale->c_fmt, t, pt, ptlim, &warn2);
     218         [ #  # ]:           0 :                         if (warn2 == IN_ALL)
     219                 :           0 :                             warn2 = IN_THIS;
     220         [ #  # ]:           0 :                         if (warn2 > *warnp)
     221                 :           0 :                             *warnp = warn2;
     222                 :             :                     }
     223                 :           0 :                     continue;
     224                 :           0 :                 case 'D':
     225                 :           0 :                     pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp);
     226                 :           0 :                     continue;
     227                 :     1425471 :                 case 'd':
     228                 :     1425471 :                     pt = _conv(t->tm_mday, "%02d", pt, ptlim);
     229                 :     1425471 :                     continue;
     230                 :           0 :                 case 'E':
     231                 :             :                 case 'O':
     232                 :             : 
     233                 :             :                     /*
     234                 :             :                      * Locale modifiers of C99 and later. The sequences %Ec
     235                 :             :                      * %EC %Ex %EX %Ey %EY %Od %oe %OH %OI %Om %OM %OS %Ou %OU
     236                 :             :                      * %OV %Ow %OW %Oy are supposed to provide alternative
     237                 :             :                      * representations.
     238                 :             :                      */
     239                 :           0 :                     goto label;
     240                 :           0 :                 case 'e':
     241                 :           0 :                     pt = _conv(t->tm_mday, "%2d", pt, ptlim);
     242                 :           0 :                     continue;
     243                 :           0 :                 case 'F':
     244                 :           0 :                     pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
     245                 :           0 :                     continue;
     246                 :     1425471 :                 case 'H':
     247                 :     1425471 :                     pt = _conv(t->tm_hour, "%02d", pt, ptlim);
     248                 :     1425471 :                     continue;
     249                 :           0 :                 case 'I':
     250         [ #  # ]:           0 :                     pt = _conv((t->tm_hour % 12) ?
     251                 :           0 :                                (t->tm_hour % 12) : 12,
     252                 :             :                                "%02d", pt, ptlim);
     253                 :           0 :                     continue;
     254                 :           0 :                 case 'j':
     255                 :           0 :                     pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim);
     256                 :           0 :                     continue;
     257                 :           0 :                 case 'k':
     258                 :             : 
     259                 :             :                     /*
     260                 :             :                      * This used to be... _conv(t->tm_hour % 12 ? t->tm_hour %
     261                 :             :                      * 12 : 12, 2, ' '); ...and has been changed to the below
     262                 :             :                      * to match SunOS 4.1.1 and Arnold Robbins' strftime
     263                 :             :                      * version 3.0. That is, "%k" and "%l" have been swapped.
     264                 :             :                      * (ado, 1993-05-24)
     265                 :             :                      */
     266                 :           0 :                     pt = _conv(t->tm_hour, "%2d", pt, ptlim);
     267                 :           0 :                     continue;
     268                 :             : #ifdef KITCHEN_SINK
     269                 :             :                 case 'K':
     270                 :             : 
     271                 :             :                     /*
     272                 :             :                      * After all this time, still unclaimed!
     273                 :             :                      */
     274                 :             :                     pt = _add("kitchen sink", pt, ptlim);
     275                 :             :                     continue;
     276                 :             : #endif                          /* defined KITCHEN_SINK */
     277                 :           0 :                 case 'l':
     278                 :             : 
     279                 :             :                     /*
     280                 :             :                      * This used to be... _conv(t->tm_hour, 2, ' '); ...and
     281                 :             :                      * has been changed to the below to match SunOS 4.1.1 and
     282                 :             :                      * Arnold Robbin's strftime version 3.0. That is, "%k" and
     283                 :             :                      * "%l" have been swapped. (ado, 1993-05-24)
     284                 :             :                      */
     285         [ #  # ]:           0 :                     pt = _conv((t->tm_hour % 12) ?
     286                 :           0 :                                (t->tm_hour % 12) : 12,
     287                 :             :                                "%2d", pt, ptlim);
     288                 :           0 :                     continue;
     289                 :     1425471 :                 case 'M':
     290                 :     1425471 :                     pt = _conv(t->tm_min, "%02d", pt, ptlim);
     291                 :     1425471 :                     continue;
     292                 :     1424671 :                 case 'm':
     293                 :     1424671 :                     pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim);
     294                 :     1424671 :                     continue;
     295                 :           0 :                 case 'n':
     296                 :           0 :                     pt = _add("\n", pt, ptlim);
     297                 :           0 :                     continue;
     298                 :           0 :                 case 'p':
     299         [ #  # ]:           0 :                     pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
     300                 :             :                               Locale->pm :
     301                 :             :                               Locale->am,
     302                 :             :                               pt, ptlim);
     303                 :           0 :                     continue;
     304                 :           0 :                 case 'R':
     305                 :           0 :                     pt = _fmt("%H:%M", t, pt, ptlim, warnp);
     306                 :           0 :                     continue;
     307                 :           0 :                 case 'r':
     308                 :           0 :                     pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp);
     309                 :           0 :                     continue;
     310                 :     1425471 :                 case 'S':
     311                 :     1425471 :                     pt = _conv(t->tm_sec, "%02d", pt, ptlim);
     312                 :     1425471 :                     continue;
     313                 :           0 :                 case 'T':
     314                 :           0 :                     pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp);
     315                 :           0 :                     continue;
     316                 :           0 :                 case 't':
     317                 :           0 :                     pt = _add("\t", pt, ptlim);
     318                 :           0 :                     continue;
     319                 :           0 :                 case 'U':
     320                 :           0 :                     pt = _conv((t->tm_yday + DAYSPERWEEK -
     321                 :           0 :                                 t->tm_wday) / DAYSPERWEEK,
     322                 :             :                                "%02d", pt, ptlim);
     323                 :           0 :                     continue;
     324                 :           0 :                 case 'u':
     325                 :             : 
     326                 :             :                     /*
     327                 :             :                      * From Arnold Robbins' strftime version 3.0: "ISO 8601:
     328                 :             :                      * Weekday as a decimal number [1 (Monday) - 7]" (ado,
     329                 :             :                      * 1993-05-24)
     330                 :             :                      */
     331         [ #  # ]:           0 :                     pt = _conv((t->tm_wday == 0) ?
     332                 :             :                                DAYSPERWEEK : t->tm_wday,
     333                 :             :                                "%d", pt, ptlim);
     334                 :           0 :                     continue;
     335                 :           0 :                 case 'V':       /* ISO 8601 week number */
     336                 :             :                 case 'G':       /* ISO 8601 year (four digits) */
     337                 :             :                 case 'g':       /* ISO 8601 year (two digits) */
     338                 :             : /*
     339                 :             :  * From Arnold Robbins' strftime version 3.0: "the week number of the
     340                 :             :  * year (the first Monday as the first day of week 1) as a decimal number
     341                 :             :  * (01-53)."
     342                 :             :  * (ado, 1993-05-24)
     343                 :             :  *
     344                 :             :  * From <https://www.cl.cam.ac.uk/~mgk25/iso-time.html> by Markus Kuhn:
     345                 :             :  * "Week 01 of a year is per definition the first week which has the
     346                 :             :  * Thursday in this year, which is equivalent to the week which contains
     347                 :             :  * the fourth day of January. In other words, the first week of a new year
     348                 :             :  * is the week which has the majority of its days in the new year. Week 01
     349                 :             :  * might also contain days from the previous year and the week before week
     350                 :             :  * 01 of a year is the last week (52 or 53) of the previous year even if
     351                 :             :  * it contains days from the new year. A week starts with Monday (day 1)
     352                 :             :  * and ends with Sunday (day 7). For example, the first week of the year
     353                 :             :  * 1997 lasts from 1996-12-30 to 1997-01-05..."
     354                 :             :  * (ado, 1996-01-02)
     355                 :             :  */
     356                 :             :                     {
     357                 :             :                         int         year;
     358                 :             :                         int         base;
     359                 :             :                         int         yday;
     360                 :             :                         int         wday;
     361                 :             :                         int         w;
     362                 :             : 
     363                 :           0 :                         year = t->tm_year;
     364                 :           0 :                         base = TM_YEAR_BASE;
     365                 :           0 :                         yday = t->tm_yday;
     366                 :           0 :                         wday = t->tm_wday;
     367                 :             :                         for (;;)
     368                 :           0 :                         {
     369                 :             :                             int         len;
     370                 :             :                             int         bot;
     371                 :             :                             int         top;
     372                 :             : 
     373   [ #  #  #  # ]:           0 :                             len = isleap_sum(year, base) ?
     374         [ #  # ]:           0 :                                 DAYSPERLYEAR :
     375                 :             :                                 DAYSPERNYEAR;
     376                 :             : 
     377                 :             :                             /*
     378                 :             :                              * What yday (-3 ... 3) does the ISO year begin
     379                 :             :                              * on?
     380                 :             :                              */
     381                 :           0 :                             bot = ((yday + 11 - wday) %
     382                 :             :                                    DAYSPERWEEK) - 3;
     383                 :             : 
     384                 :             :                             /*
     385                 :             :                              * What yday does the NEXT ISO year begin on?
     386                 :             :                              */
     387                 :           0 :                             top = bot -
     388                 :           0 :                                 (len % DAYSPERWEEK);
     389         [ #  # ]:           0 :                             if (top < -3)
     390                 :           0 :                                 top += DAYSPERWEEK;
     391                 :           0 :                             top += len;
     392         [ #  # ]:           0 :                             if (yday >= top)
     393                 :             :                             {
     394                 :           0 :                                 ++base;
     395                 :           0 :                                 w = 1;
     396                 :           0 :                                 break;
     397                 :             :                             }
     398         [ #  # ]:           0 :                             if (yday >= bot)
     399                 :             :                             {
     400                 :           0 :                                 w = 1 + ((yday - bot) /
     401                 :             :                                          DAYSPERWEEK);
     402                 :           0 :                                 break;
     403                 :             :                             }
     404                 :           0 :                             --base;
     405   [ #  #  #  # ]:           0 :                             yday += isleap_sum(year, base) ?
     406         [ #  # ]:           0 :                                 DAYSPERLYEAR :
     407                 :             :                                 DAYSPERNYEAR;
     408                 :             :                         }
     409         [ #  # ]:           0 :                         if (*format == 'V')
     410                 :           0 :                             pt = _conv(w, "%02d",
     411                 :             :                                        pt, ptlim);
     412         [ #  # ]:           0 :                         else if (*format == 'g')
     413                 :             :                         {
     414                 :           0 :                             *warnp = IN_ALL;
     415                 :           0 :                             pt = _yconv(year, base,
     416                 :             :                                         false, true,
     417                 :             :                                         pt, ptlim);
     418                 :             :                         }
     419                 :             :                         else
     420                 :           0 :                             pt = _yconv(year, base,
     421                 :             :                                         true, true,
     422                 :             :                                         pt, ptlim);
     423                 :             :                     }
     424                 :           0 :                     continue;
     425                 :           0 :                 case 'v':
     426                 :             : 
     427                 :             :                     /*
     428                 :             :                      * From Arnold Robbins' strftime version 3.0: "date as
     429                 :             :                      * dd-bbb-YYYY" (ado, 1993-05-24)
     430                 :             :                      */
     431                 :           0 :                     pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp);
     432                 :           0 :                     continue;
     433                 :           0 :                 case 'W':
     434                 :           0 :                     pt = _conv((t->tm_yday + DAYSPERWEEK -
     435                 :           0 :                                 (t->tm_wday ?
     436         [ #  # ]:           0 :                                  (t->tm_wday - 1) :
     437                 :             :                                  (DAYSPERWEEK - 1))) / DAYSPERWEEK,
     438                 :             :                                "%02d", pt, ptlim);
     439                 :           0 :                     continue;
     440                 :           0 :                 case 'w':
     441                 :           0 :                     pt = _conv(t->tm_wday, "%d", pt, ptlim);
     442                 :           0 :                     continue;
     443                 :           0 :                 case 'X':
     444                 :           0 :                     pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp);
     445                 :           0 :                     continue;
     446                 :           0 :                 case 'x':
     447                 :             :                     {
     448                 :           0 :                         enum warn   warn2 = IN_SOME;
     449                 :             : 
     450                 :           0 :                         pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2);
     451         [ #  # ]:           0 :                         if (warn2 == IN_ALL)
     452                 :           0 :                             warn2 = IN_THIS;
     453         [ #  # ]:           0 :                         if (warn2 > *warnp)
     454                 :           0 :                             *warnp = warn2;
     455                 :             :                     }
     456                 :           0 :                     continue;
     457                 :           0 :                 case 'y':
     458                 :           0 :                     *warnp = IN_ALL;
     459                 :           0 :                     pt = _yconv(t->tm_year, TM_YEAR_BASE,
     460                 :             :                                 false, true,
     461                 :             :                                 pt, ptlim);
     462                 :           0 :                     continue;
     463                 :     1425471 :                 case 'Y':
     464                 :     1425471 :                     pt = _yconv(t->tm_year, TM_YEAR_BASE,
     465                 :             :                                 true, true,
     466                 :             :                                 pt, ptlim);
     467                 :     1425471 :                     continue;
     468                 :     1425462 :                 case 'Z':
     469         [ +  - ]:     1425462 :                     if (t->tm_zone != NULL)
     470                 :     1425462 :                         pt = _add(t->tm_zone, pt, ptlim);
     471                 :             : 
     472                 :             :                     /*
     473                 :             :                      * C99 and later say that %Z must be replaced by the empty
     474                 :             :                      * string if the time zone abbreviation is not
     475                 :             :                      * determinable.
     476                 :             :                      */
     477                 :     1425462 :                     continue;
     478                 :           0 :                 case 'z':
     479                 :             :                     {
     480                 :             :                         long        diff;
     481                 :             :                         char const *sign;
     482                 :             :                         bool        negative;
     483                 :             : 
     484                 :           0 :                         diff = t->TM_GMTOFF;
     485                 :           0 :                         negative = diff < 0;
     486         [ #  # ]:           0 :                         if (diff == 0)
     487                 :             :                         {
     488         [ #  # ]:           0 :                             if (t->tm_zone != NULL)
     489                 :           0 :                                 negative = t->tm_zone[0] == '-';
     490                 :             :                         }
     491         [ #  # ]:           0 :                         if (negative)
     492                 :             :                         {
     493                 :           0 :                             sign = "-";
     494                 :           0 :                             diff = -diff;
     495                 :             :                         }
     496                 :             :                         else
     497                 :           0 :                             sign = "+";
     498                 :           0 :                         pt = _add(sign, pt, ptlim);
     499                 :           0 :                         diff /= SECSPERMIN;
     500                 :           0 :                         diff = (diff / MINSPERHOUR) * 100 +
     501                 :           0 :                             (diff % MINSPERHOUR);
     502                 :           0 :                         pt = _conv(diff, "%04d", pt, ptlim);
     503                 :             :                     }
     504                 :           0 :                     continue;
     505                 :           0 :                 case '+':
     506                 :           0 :                     pt = _fmt(Locale->date_fmt, t, pt, ptlim,
     507                 :             :                               warnp);
     508                 :           0 :                     continue;
     509                 :           0 :                 case '%':
     510                 :           0 :                     break;
     511                 :             :             }
     512                 :             :         }
     513         [ -  + ]:    13500094 :         if (pt == ptlim)
     514                 :           0 :             break;
     515                 :    13500094 :         *pt++ = *format;
     516                 :             :     }
     517                 :     1426271 :     return pt;
     518                 :             : }
     519                 :             : 
     520                 :             : static char *
     521                 :     9977497 : _conv(int n, const char *format, char *pt, const char *ptlim)
     522                 :             : {
     523                 :             :     char        buf[INT_STRLEN_MAXIMUM(int) + 1];
     524                 :             : 
     525                 :     9977497 :     sprintf(buf, format, n);
     526                 :     9977497 :     return _add(buf, pt, ptlim);
     527                 :             : }
     528                 :             : 
     529                 :             : static char *
     530                 :    11404559 : _add(const char *str, char *pt, const char *ptlim)
     531                 :             : {
     532   [ +  -  +  + ]:    35640739 :     while (pt < ptlim && (*pt = *str++) != '\0')
     533                 :    24236180 :         ++pt;
     534                 :    11404559 :     return pt;
     535                 :             : }
     536                 :             : 
     537                 :             : /*
     538                 :             :  * POSIX and the C Standard are unclear or inconsistent about
     539                 :             :  * what %C and %y do if the year is negative or exceeds 9999.
     540                 :             :  * Use the convention that %C concatenated with %y yields the
     541                 :             :  * same output as %Y, and that %Y contains at least 4 bytes,
     542                 :             :  * with more only if necessary.
     543                 :             :  */
     544                 :             : 
     545                 :             : static char *
     546                 :     1425471 : _yconv(int a, int b, bool convert_top, bool convert_yy,
     547                 :             :        char *pt, const char *ptlim)
     548                 :             : {
     549                 :             :     int         lead;
     550                 :             :     int         trail;
     551                 :             : 
     552                 :     1425471 :     int         DIVISOR = 100;
     553                 :             : 
     554                 :     1425471 :     trail = a % DIVISOR + b % DIVISOR;
     555                 :     1425471 :     lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
     556                 :     1425471 :     trail %= DIVISOR;
     557   [ -  +  -  - ]:     1425471 :     if (trail < 0 && lead > 0)
     558                 :             :     {
     559                 :           0 :         trail += DIVISOR;
     560                 :           0 :         --lead;
     561                 :             :     }
     562   [ -  +  -  - ]:     1425471 :     else if (lead < 0 && trail > 0)
     563                 :             :     {
     564                 :           0 :         trail -= DIVISOR;
     565                 :           0 :         ++lead;
     566                 :             :     }
     567         [ +  - ]:     1425471 :     if (convert_top)
     568                 :             :     {
     569   [ -  +  -  - ]:     1425471 :         if (lead == 0 && trail < 0)
     570                 :           0 :             pt = _add("-0", pt, ptlim);
     571                 :             :         else
     572                 :     1425471 :             pt = _conv(lead, "%02d", pt, ptlim);
     573                 :             :     }
     574         [ +  - ]:     1425471 :     if (convert_yy)
     575                 :     1425471 :         pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim);
     576                 :     1425471 :     return pt;
     577                 :             : }
        

Generated by: LCOV version 2.0-1