LCOV - code coverage report
Current view: top level - src/timezone - strftime.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 25.0 % 244 61
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 5 5
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 17.5 % 137 24

             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                 :             : #define Locale  (&C_time_locale)
      63                 :             : 
      64                 :             : static const struct lc_time_T C_time_locale = {
      65                 :             :         {
      66                 :             :                 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
      67                 :             :                 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
      68                 :             :         }, {
      69                 :             :                 "January", "February", "March", "April", "May", "June",
      70                 :             :                 "July", "August", "September", "October", "November", "December"
      71                 :             :         }, {
      72                 :             :                 "Sun", "Mon", "Tue", "Wed",
      73                 :             :                 "Thu", "Fri", "Sat"
      74                 :             :         }, {
      75                 :             :                 "Sunday", "Monday", "Tuesday", "Wednesday",
      76                 :             :                 "Thursday", "Friday", "Saturday"
      77                 :             :         },
      78                 :             : 
      79                 :             :         /* X_fmt */
      80                 :             :         "%H:%M:%S",
      81                 :             : 
      82                 :             :         /*
      83                 :             :          * x_fmt
      84                 :             :          *
      85                 :             :          * C99 and later require this format. Using just numbers (as here) makes
      86                 :             :          * Quakers happier; it's also compatible with SVR4.
      87                 :             :          */
      88                 :             :         "%m/%d/%y",
      89                 :             : 
      90                 :             :         /*
      91                 :             :          * c_fmt
      92                 :             :          *
      93                 :             :          * C99 and later require this format. Previously this code used "%D %X",
      94                 :             :          * but we now conform to C99. Note that "%a %b %d %H:%M:%S %Y" is used by
      95                 :             :          * Solaris 2.3.
      96                 :             :          */
      97                 :             :         "%a %b %e %T %Y",
      98                 :             : 
      99                 :             :         /* am */
     100                 :             :         "AM",
     101                 :             : 
     102                 :             :         /* pm */
     103                 :             :         "PM",
     104                 :             : 
     105                 :             :         /* date_fmt */
     106                 :             :         "%a %b %e %H:%M:%S %Z %Y"
     107                 :             : };
     108                 :             : 
     109                 :             : enum warn
     110                 :             : {
     111                 :             :         IN_NONE, IN_SOME, IN_THIS, IN_ALL
     112                 :             : };
     113                 :             : 
     114                 :             : static char *_add(const char *str, char *pt, const char *ptlim);
     115                 :             : static char *_conv(int n, const char *format, char *pt, const char *ptlim);
     116                 :             : static char *_fmt(const char *format, const struct pg_tm *t, char *pt, const char *ptlim,
     117                 :             :                                   enum warn *warnp);
     118                 :             : static char *_yconv(int a, int b, bool convert_top, bool convert_yy, char *pt, char const *ptlim);
     119                 :             : 
     120                 :             : 
     121                 :             : /*
     122                 :             :  * Convert timestamp t to string s, a caller-allocated buffer of size maxsize,
     123                 :             :  * using the given format pattern.
     124                 :             :  *
     125                 :             :  * See also timestamptz_to_str.
     126                 :             :  */
     127                 :             : size_t
     128                 :       75579 : pg_strftime(char *s, size_t maxsize, const char *format, const struct pg_tm *t)
     129                 :             : {
     130                 :       75579 :         char       *p;
     131                 :       75579 :         int                     saved_errno = errno;
     132                 :       75579 :         enum warn       warn = IN_NONE;
     133                 :             : 
     134                 :       75579 :         p = _fmt(format, t, s, s + maxsize, &warn);
     135         [ +  - ]:       75579 :         if (!p)
     136                 :             :         {
     137                 :           0 :                 errno = EOVERFLOW;
     138                 :           0 :                 return 0;
     139                 :             :         }
     140         [ +  - ]:       75579 :         if (p == s + maxsize)
     141                 :             :         {
     142                 :           0 :                 errno = ERANGE;
     143                 :           0 :                 return 0;
     144                 :             :         }
     145                 :       75579 :         *p = '\0';
     146                 :       75579 :         errno = saved_errno;
     147                 :       75579 :         return p - s;
     148                 :       75579 : }
     149                 :             : 
     150                 :             : static char *
     151                 :       75579 : _fmt(const char *format, const struct pg_tm *t, char *pt,
     152                 :             :          const char *ptlim, enum warn *warnp)
     153                 :             : {
     154         [ +  + ]:     1360414 :         for (; *format; ++format)
     155                 :             :         {
     156         [ +  + ]:     1284835 :                 if (*format == '%')
     157                 :      529053 :                 {
     158                 :             :         label:
     159   [ +  -  +  +  :      529053 :                         switch (*++format)
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  +  -  -  -  
          -  -  +  -  -  
          -  -  -  -  +  
                   +  - ]
     160                 :             :                         {
     161                 :             :                                 case '\0':
     162                 :           0 :                                         --format;
     163                 :           0 :                                         break;
     164                 :             :                                 case 'A':
     165   [ #  #  #  # ]:           0 :                                         pt = _add((t->tm_wday < 0 ||
     166                 :           0 :                                                            t->tm_wday >= DAYSPERWEEK) ?
     167                 :           0 :                                                           "?" : Locale->weekday[t->tm_wday],
     168                 :           0 :                                                           pt, ptlim);
     169                 :           0 :                                         continue;
     170                 :             :                                 case 'a':
     171   [ #  #  #  # ]:           0 :                                         pt = _add((t->tm_wday < 0 ||
     172                 :           0 :                                                            t->tm_wday >= DAYSPERWEEK) ?
     173                 :           0 :                                                           "?" : Locale->wday[t->tm_wday],
     174                 :           0 :                                                           pt, ptlim);
     175                 :           0 :                                         continue;
     176                 :             :                                 case 'B':
     177   [ #  #  #  # ]:           0 :                                         pt = _add((t->tm_mon < 0 ||
     178                 :           0 :                                                            t->tm_mon >= MONSPERYEAR) ?
     179                 :           0 :                                                           "?" : Locale->month[t->tm_mon],
     180                 :           0 :                                                           pt, ptlim);
     181                 :           0 :                                         continue;
     182                 :             :                                 case 'b':
     183                 :             :                                 case 'h':
     184   [ #  #  #  # ]:           0 :                                         pt = _add((t->tm_mon < 0 ||
     185                 :           0 :                                                            t->tm_mon >= MONSPERYEAR) ?
     186                 :           0 :                                                           "?" : Locale->mon[t->tm_mon],
     187                 :           0 :                                                           pt, ptlim);
     188                 :           0 :                                         continue;
     189                 :             :                                 case 'C':
     190                 :             : 
     191                 :             :                                         /*
     192                 :             :                                          * %C used to do a... _fmt("%a %b %e %X %Y", t);
     193                 :             :                                          * ...whereas now POSIX 1003.2 calls for something
     194                 :             :                                          * completely different. (ado, 1993-05-24)
     195                 :             :                                          */
     196                 :           0 :                                         pt = _yconv(t->tm_year, TM_YEAR_BASE,
     197                 :           0 :                                                                 true, false, pt, ptlim);
     198                 :           0 :                                         continue;
     199                 :             :                                 case 'c':
     200                 :             :                                         {
     201                 :           0 :                                                 enum warn       warn2 = IN_SOME;
     202                 :             : 
     203                 :           0 :                                                 pt = _fmt(Locale->c_fmt, t, pt, ptlim, &warn2);
     204         [ #  # ]:           0 :                                                 if (warn2 == IN_ALL)
     205                 :           0 :                                                         warn2 = IN_THIS;
     206         [ #  # ]:           0 :                                                 if (warn2 > *warnp)
     207                 :           0 :                                                         *warnp = warn2;
     208                 :           0 :                                         }
     209                 :           0 :                                         continue;
     210                 :             :                                 case 'D':
     211                 :           0 :                                         pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp);
     212                 :           0 :                                         continue;
     213                 :             :                                 case 'd':
     214                 :       75579 :                                         pt = _conv(t->tm_mday, "%02d", pt, ptlim);
     215                 :       75579 :                                         continue;
     216                 :             :                                 case 'E':
     217                 :             :                                 case 'O':
     218                 :             : 
     219                 :             :                                         /*
     220                 :             :                                          * Locale modifiers of C99 and later. The sequences %Ec
     221                 :             :                                          * %EC %Ex %EX %Ey %EY %Od %oe %OH %OI %Om %OM %OS %Ou %OU
     222                 :             :                                          * %OV %Ow %OW %Oy are supposed to provide alternative
     223                 :             :                                          * representations.
     224                 :             :                                          */
     225                 :           0 :                                         goto label;
     226                 :             :                                 case 'e':
     227                 :           0 :                                         pt = _conv(t->tm_mday, "%2d", pt, ptlim);
     228                 :           0 :                                         continue;
     229                 :             :                                 case 'F':
     230                 :           0 :                                         pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
     231                 :           0 :                                         continue;
     232                 :             :                                 case 'H':
     233                 :       75579 :                                         pt = _conv(t->tm_hour, "%02d", pt, ptlim);
     234                 :       75579 :                                         continue;
     235                 :             :                                 case 'I':
     236         [ #  # ]:           0 :                                         pt = _conv((t->tm_hour % 12) ?
     237                 :           0 :                                                            (t->tm_hour % 12) : 12,
     238                 :           0 :                                                            "%02d", pt, ptlim);
     239                 :           0 :                                         continue;
     240                 :             :                                 case 'j':
     241                 :           0 :                                         pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim);
     242                 :           0 :                                         continue;
     243                 :             :                                 case 'k':
     244                 :             : 
     245                 :             :                                         /*
     246                 :             :                                          * This used to be... _conv(t->tm_hour % 12 ? t->tm_hour %
     247                 :             :                                          * 12 : 12, 2, ' '); ...and has been changed to the below
     248                 :             :                                          * to match SunOS 4.1.1 and Arnold Robbins' strftime
     249                 :             :                                          * version 3.0. That is, "%k" and "%l" have been swapped.
     250                 :             :                                          * (ado, 1993-05-24)
     251                 :             :                                          */
     252                 :           0 :                                         pt = _conv(t->tm_hour, "%2d", pt, ptlim);
     253                 :           0 :                                         continue;
     254                 :             : #ifdef KITCHEN_SINK
     255                 :             :                                 case 'K':
     256                 :             : 
     257                 :             :                                         /*
     258                 :             :                                          * After all this time, still unclaimed!
     259                 :             :                                          */
     260                 :             :                                         pt = _add("kitchen sink", pt, ptlim);
     261                 :             :                                         continue;
     262                 :             : #endif                                                  /* defined KITCHEN_SINK */
     263                 :             :                                 case 'l':
     264                 :             : 
     265                 :             :                                         /*
     266                 :             :                                          * This used to be... _conv(t->tm_hour, 2, ' '); ...and
     267                 :             :                                          * has been changed to the below to match SunOS 4.1.1 and
     268                 :             :                                          * Arnold Robbin's strftime version 3.0. That is, "%k" and
     269                 :             :                                          * "%l" have been swapped. (ado, 1993-05-24)
     270                 :             :                                          */
     271         [ #  # ]:           0 :                                         pt = _conv((t->tm_hour % 12) ?
     272                 :           0 :                                                            (t->tm_hour % 12) : 12,
     273                 :           0 :                                                            "%2d", pt, ptlim);
     274                 :           0 :                                         continue;
     275                 :             :                                 case 'M':
     276                 :       75579 :                                         pt = _conv(t->tm_min, "%02d", pt, ptlim);
     277                 :       75579 :                                         continue;
     278                 :             :                                 case 'm':
     279                 :       75579 :                                         pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim);
     280                 :       75579 :                                         continue;
     281                 :             :                                 case 'n':
     282                 :           0 :                                         pt = _add("\n", pt, ptlim);
     283                 :           0 :                                         continue;
     284                 :             :                                 case 'p':
     285                 :           0 :                                         pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
     286                 :           0 :                                                           Locale->pm :
     287                 :           0 :                                                           Locale->am,
     288                 :           0 :                                                           pt, ptlim);
     289                 :           0 :                                         continue;
     290                 :             :                                 case 'R':
     291                 :           0 :                                         pt = _fmt("%H:%M", t, pt, ptlim, warnp);
     292                 :           0 :                                         continue;
     293                 :             :                                 case 'r':
     294                 :           0 :                                         pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp);
     295                 :           0 :                                         continue;
     296                 :             :                                 case 'S':
     297                 :       75579 :                                         pt = _conv(t->tm_sec, "%02d", pt, ptlim);
     298                 :       75579 :                                         continue;
     299                 :             :                                 case 'T':
     300                 :           0 :                                         pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp);
     301                 :           0 :                                         continue;
     302                 :             :                                 case 't':
     303                 :           0 :                                         pt = _add("\t", pt, ptlim);
     304                 :           0 :                                         continue;
     305                 :             :                                 case 'U':
     306                 :           0 :                                         pt = _conv((t->tm_yday + DAYSPERWEEK -
     307                 :           0 :                                                                 t->tm_wday) / DAYSPERWEEK,
     308                 :           0 :                                                            "%02d", pt, ptlim);
     309                 :           0 :                                         continue;
     310                 :             :                                 case 'u':
     311                 :             : 
     312                 :             :                                         /*
     313                 :             :                                          * From Arnold Robbins' strftime version 3.0: "ISO 8601:
     314                 :             :                                          * Weekday as a decimal number [1 (Monday) - 7]" (ado,
     315                 :             :                                          * 1993-05-24)
     316                 :             :                                          */
     317         [ #  # ]:           0 :                                         pt = _conv((t->tm_wday == 0) ?
     318                 :           0 :                                                            DAYSPERWEEK : t->tm_wday,
     319                 :           0 :                                                            "%d", pt, ptlim);
     320                 :           0 :                                         continue;
     321                 :             :                                 case 'V':               /* ISO 8601 week number */
     322                 :             :                                 case 'G':               /* ISO 8601 year (four digits) */
     323                 :             :                                 case 'g':               /* ISO 8601 year (two digits) */
     324                 :             : /*
     325                 :             :  * From Arnold Robbins' strftime version 3.0: "the week number of the
     326                 :             :  * year (the first Monday as the first day of week 1) as a decimal number
     327                 :             :  * (01-53)."
     328                 :             :  * (ado, 1993-05-24)
     329                 :             :  *
     330                 :             :  * From <https://www.cl.cam.ac.uk/~mgk25/iso-time.html> by Markus Kuhn:
     331                 :             :  * "Week 01 of a year is per definition the first week which has the
     332                 :             :  * Thursday in this year, which is equivalent to the week which contains
     333                 :             :  * the fourth day of January. In other words, the first week of a new year
     334                 :             :  * is the week which has the majority of its days in the new year. Week 01
     335                 :             :  * might also contain days from the previous year and the week before week
     336                 :             :  * 01 of a year is the last week (52 or 53) of the previous year even if
     337                 :             :  * it contains days from the new year. A week starts with Monday (day 1)
     338                 :             :  * and ends with Sunday (day 7). For example, the first week of the year
     339                 :             :  * 1997 lasts from 1996-12-30 to 1997-01-05..."
     340                 :             :  * (ado, 1996-01-02)
     341                 :             :  */
     342                 :             :                                         {
     343                 :           0 :                                                 int                     year;
     344                 :           0 :                                                 int                     base;
     345                 :           0 :                                                 int                     yday;
     346                 :           0 :                                                 int                     wday;
     347                 :           0 :                                                 int                     w;
     348                 :             : 
     349                 :           0 :                                                 year = t->tm_year;
     350                 :           0 :                                                 base = TM_YEAR_BASE;
     351                 :           0 :                                                 yday = t->tm_yday;
     352                 :           0 :                                                 wday = t->tm_wday;
     353                 :           0 :                                                 for (;;)
     354                 :             :                                                 {
     355                 :           0 :                                                         int                     len;
     356                 :           0 :                                                         int                     bot;
     357                 :           0 :                                                         int                     top;
     358                 :             : 
     359   [ #  #  #  # ]:           0 :                                                         len = isleap_sum(year, base) ?
     360                 :             :                                                                 DAYSPERLYEAR :
     361                 :             :                                                                 DAYSPERNYEAR;
     362                 :             : 
     363                 :             :                                                         /*
     364                 :             :                                                          * What yday (-3 ... 3) does the ISO year begin
     365                 :             :                                                          * on?
     366                 :             :                                                          */
     367                 :           0 :                                                         bot = ((yday + 11 - wday) %
     368                 :           0 :                                                                    DAYSPERWEEK) - 3;
     369                 :             : 
     370                 :             :                                                         /*
     371                 :             :                                                          * What yday does the NEXT ISO year begin on?
     372                 :             :                                                          */
     373                 :           0 :                                                         top = bot -
     374                 :           0 :                                                                 (len % DAYSPERWEEK);
     375         [ #  # ]:           0 :                                                         if (top < -3)
     376                 :           0 :                                                                 top += DAYSPERWEEK;
     377                 :           0 :                                                         top += len;
     378         [ #  # ]:           0 :                                                         if (yday >= top)
     379                 :             :                                                         {
     380                 :           0 :                                                                 ++base;
     381                 :           0 :                                                                 w = 1;
     382                 :           0 :                                                                 break;
     383                 :             :                                                         }
     384         [ #  # ]:           0 :                                                         if (yday >= bot)
     385                 :             :                                                         {
     386                 :           0 :                                                                 w = 1 + ((yday - bot) /
     387                 :             :                                                                                  DAYSPERWEEK);
     388                 :           0 :                                                                 break;
     389                 :             :                                                         }
     390                 :           0 :                                                         --base;
     391   [ #  #  #  # ]:           0 :                                                         yday += isleap_sum(year, base) ?
     392                 :             :                                                                 DAYSPERLYEAR :
     393                 :             :                                                                 DAYSPERNYEAR;
     394         [ #  # ]:           0 :                                                 }
     395         [ #  # ]:           0 :                                                 if (*format == 'V')
     396                 :           0 :                                                         pt = _conv(w, "%02d",
     397                 :           0 :                                                                            pt, ptlim);
     398         [ #  # ]:           0 :                                                 else if (*format == 'g')
     399                 :             :                                                 {
     400                 :           0 :                                                         *warnp = IN_ALL;
     401                 :           0 :                                                         pt = _yconv(year, base,
     402                 :             :                                                                                 false, true,
     403                 :           0 :                                                                                 pt, ptlim);
     404                 :           0 :                                                 }
     405                 :             :                                                 else
     406                 :           0 :                                                         pt = _yconv(year, base,
     407                 :             :                                                                                 true, true,
     408                 :           0 :                                                                                 pt, ptlim);
     409                 :           0 :                                         }
     410                 :           0 :                                         continue;
     411                 :             :                                 case 'v':
     412                 :             : 
     413                 :             :                                         /*
     414                 :             :                                          * From Arnold Robbins' strftime version 3.0: "date as
     415                 :             :                                          * dd-bbb-YYYY" (ado, 1993-05-24)
     416                 :             :                                          */
     417                 :           0 :                                         pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp);
     418                 :           0 :                                         continue;
     419                 :             :                                 case 'W':
     420                 :           0 :                                         pt = _conv((t->tm_yday + DAYSPERWEEK -
     421         [ #  # ]:           0 :                                                                 (t->tm_wday ?
     422                 :           0 :                                                                  (t->tm_wday - 1) :
     423                 :           0 :                                                                  (DAYSPERWEEK - 1))) / DAYSPERWEEK,
     424                 :           0 :                                                            "%02d", pt, ptlim);
     425                 :           0 :                                         continue;
     426                 :             :                                 case 'w':
     427                 :           0 :                                         pt = _conv(t->tm_wday, "%d", pt, ptlim);
     428                 :           0 :                                         continue;
     429                 :             :                                 case 'X':
     430                 :           0 :                                         pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp);
     431                 :           0 :                                         continue;
     432                 :             :                                 case 'x':
     433                 :             :                                         {
     434                 :           0 :                                                 enum warn       warn2 = IN_SOME;
     435                 :             : 
     436                 :           0 :                                                 pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2);
     437         [ #  # ]:           0 :                                                 if (warn2 == IN_ALL)
     438                 :           0 :                                                         warn2 = IN_THIS;
     439         [ #  # ]:           0 :                                                 if (warn2 > *warnp)
     440                 :           0 :                                                         *warnp = warn2;
     441                 :           0 :                                         }
     442                 :           0 :                                         continue;
     443                 :             :                                 case 'y':
     444                 :           0 :                                         *warnp = IN_ALL;
     445                 :           0 :                                         pt = _yconv(t->tm_year, TM_YEAR_BASE,
     446                 :             :                                                                 false, true,
     447                 :           0 :                                                                 pt, ptlim);
     448                 :           0 :                                         continue;
     449                 :             :                                 case 'Y':
     450                 :      151158 :                                         pt = _yconv(t->tm_year, TM_YEAR_BASE,
     451                 :             :                                                                 true, true,
     452                 :       75579 :                                                                 pt, ptlim);
     453                 :       75579 :                                         continue;
     454                 :             :                                 case 'Z':
     455         [ -  + ]:       75579 :                                         if (t->tm_zone != NULL)
     456                 :       75579 :                                                 pt = _add(t->tm_zone, pt, ptlim);
     457                 :             : 
     458                 :             :                                         /*
     459                 :             :                                          * C99 and later say that %Z must be replaced by the empty
     460                 :             :                                          * string if the time zone abbreviation is not
     461                 :             :                                          * determinable.
     462                 :             :                                          */
     463                 :       75579 :                                         continue;
     464                 :             :                                 case 'z':
     465                 :             :                                         {
     466                 :           0 :                                                 long            diff;
     467                 :           0 :                                                 char const *sign;
     468                 :           0 :                                                 bool            negative;
     469                 :             : 
     470         [ #  # ]:           0 :                                                 if (t->tm_isdst < 0)
     471                 :           0 :                                                         continue;
     472                 :           0 :                                                 diff = t->tm_gmtoff;
     473                 :           0 :                                                 negative = diff < 0;
     474         [ #  # ]:           0 :                                                 if (diff == 0)
     475                 :             :                                                 {
     476         [ #  # ]:           0 :                                                         if (t->tm_zone != NULL)
     477                 :           0 :                                                                 negative = t->tm_zone[0] == '-';
     478                 :           0 :                                                 }
     479         [ #  # ]:           0 :                                                 if (negative)
     480                 :             :                                                 {
     481                 :           0 :                                                         sign = "-";
     482                 :           0 :                                                         diff = -diff;
     483                 :           0 :                                                 }
     484                 :             :                                                 else
     485                 :           0 :                                                         sign = "+";
     486                 :           0 :                                                 pt = _add(sign, pt, ptlim);
     487                 :           0 :                                                 diff /= SECSPERMIN;
     488                 :           0 :                                                 diff = (diff / MINSPERHOUR) * 100 +
     489                 :           0 :                                                         (diff % MINSPERHOUR);
     490                 :           0 :                                                 pt = _conv(diff, "%04d", pt, ptlim);
     491         [ #  # ]:           0 :                                         }
     492                 :           0 :                                         continue;
     493                 :             :                                 case '+':
     494                 :           0 :                                         pt = _fmt(Locale->date_fmt, t, pt, ptlim,
     495                 :           0 :                                                           warnp);
     496                 :           0 :                                         continue;
     497                 :           0 :                                 case '%':
     498                 :             : 
     499                 :             :                                         /*
     500                 :             :                                          * X311J/88-090 (4.12.3.5): if conversion char is
     501                 :             :                                          * undefined, behavior is undefined. Print out the
     502                 :             :                                          * character itself as printf(3) also does.
     503                 :             :                                          */
     504                 :             :                                 default:
     505                 :           0 :                                         break;
     506                 :             :                         }
     507                 :           0 :                 }
     508         [ -  + ]:      755782 :                 if (pt == ptlim)
     509                 :           0 :                         break;
     510                 :      755782 :                 *pt++ = *format;
     511                 :      755782 :         }
     512                 :       75579 :         return pt;
     513                 :             : }
     514                 :             : 
     515                 :             : static char *
     516                 :      529053 : _conv(int n, const char *format, char *pt, const char *ptlim)
     517                 :             : {
     518                 :      529053 :         char            buf[INT_STRLEN_MAXIMUM(int) + 1];
     519                 :             : 
     520                 :      529053 :         sprintf(buf, format, n);
     521                 :     1058106 :         return _add(buf, pt, ptlim);
     522                 :      529053 : }
     523                 :             : 
     524                 :             : static char *
     525                 :      604632 : _add(const char *str, char *pt, const char *ptlim)
     526                 :             : {
     527   [ -  +  +  + ]:     1889475 :         while (pt < ptlim && (*pt = *str++) != '\0')
     528                 :     1284843 :                 ++pt;
     529                 :      604632 :         return pt;
     530                 :             : }
     531                 :             : 
     532                 :             : /*
     533                 :             :  * POSIX and the C Standard are unclear or inconsistent about
     534                 :             :  * what %C and %y do if the year is negative or exceeds 9999.
     535                 :             :  * Use the convention that %C concatenated with %y yields the
     536                 :             :  * same output as %Y, and that %Y contains at least 4 bytes,
     537                 :             :  * with more only if necessary.
     538                 :             :  */
     539                 :             : 
     540                 :             : static char *
     541                 :       75579 : _yconv(int a, int b, bool convert_top, bool convert_yy,
     542                 :             :            char *pt, const char *ptlim)
     543                 :             : {
     544                 :       75579 :         int                     lead;
     545                 :       75579 :         int                     trail;
     546                 :             : 
     547                 :             : #define DIVISOR 100
     548                 :       75579 :         trail = a % DIVISOR + b % DIVISOR;
     549                 :       75579 :         lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
     550                 :       75579 :         trail %= DIVISOR;
     551   [ -  +  #  # ]:       75579 :         if (trail < 0 && lead > 0)
     552                 :             :         {
     553                 :           0 :                 trail += DIVISOR;
     554                 :           0 :                 --lead;
     555                 :           0 :         }
     556   [ -  +  #  # ]:       75579 :         else if (lead < 0 && trail > 0)
     557                 :             :         {
     558                 :           0 :                 trail -= DIVISOR;
     559                 :           0 :                 ++lead;
     560                 :           0 :         }
     561         [ -  + ]:       75579 :         if (convert_top)
     562                 :             :         {
     563   [ -  +  #  # ]:       75579 :                 if (lead == 0 && trail < 0)
     564                 :           0 :                         pt = _add("-0", pt, ptlim);
     565                 :             :                 else
     566                 :       75579 :                         pt = _conv(lead, "%02d", pt, ptlim);
     567                 :       75579 :         }
     568         [ -  + ]:       75579 :         if (convert_yy)
     569         [ +  - ]:       75579 :                 pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim);
     570                 :      151158 :         return pt;
     571                 :       75579 : }
        

Generated by: LCOV version 2.3.2-1