LCOV - code coverage report
Current view: top level - src/timezone - strftime.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 28.8 % 236 68
Test Date: 2026-05-14 01:16:26 Functions: 100.0 % 5 5
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.0-1