LCOV - differential code coverage report
Current view: top level - src/timezone - strftime.c (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage HEAD vs 15 Lines: 30.2 % 232 70 162 70
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 5 5 5
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  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 CBC      350781 : pg_strftime(char *s, size_t maxsize, const char *format, const struct pg_tm *t)
     129                 : {
     130                 :     char       *p;
     131          350781 :     int         saved_errno = errno;
     132          350781 :     enum warn   warn = IN_NONE;
     133                 : 
     134          350781 :     p = _fmt(format, t, s, s + maxsize, &warn);
     135          350781 :     if (!p)
     136                 :     {
     137 UBC           0 :         errno = EOVERFLOW;
     138               0 :         return 0;
     139                 :     }
     140 CBC      350781 :     if (p == s + maxsize)
     141                 :     {
     142 UBC           0 :         errno = ERANGE;
     143               0 :         return 0;
     144                 :     }
     145 CBC      350781 :     *p = '\0';
     146          350781 :     errno = saved_errno;
     147          350781 :     return p - s;
     148                 : }
     149                 : 
     150                 : static char *
     151          350781 : _fmt(const char *format, const struct pg_tm *t, char *pt,
     152                 :      const char *ptlim, enum warn *warnp)
     153                 : {
     154         5837249 :     for (; *format; ++format)
     155                 :     {
     156         5486468 :         if (*format == '%')
     157                 :         {
     158         2456258 :     label:
     159         2456258 :             switch (*++format)
     160                 :             {
     161 UBC           0 :                 case '\0':
     162               0 :                     --format;
     163               0 :                     break;
     164               0 :                 case 'A':
     165               0 :                     pt = _add((t->tm_wday < 0 ||
     166               0 :                                t->tm_wday >= DAYSPERWEEK) ?
     167               0 :                               "?" : Locale->weekday[t->tm_wday],
     168                 :                               pt, ptlim);
     169               0 :                     continue;
     170 CBC         400 :                 case 'a':
     171             800 :                     pt = _add((t->tm_wday < 0 ||
     172             400 :                                t->tm_wday >= DAYSPERWEEK) ?
     173             400 :                               "?" : Locale->wday[t->tm_wday],
     174                 :                               pt, ptlim);
     175             400 :                     continue;
     176 UBC           0 :                 case 'B':
     177               0 :                     pt = _add((t->tm_mon < 0 ||
     178               0 :                                t->tm_mon >= MONSPERYEAR) ?
     179               0 :                               "?" : Locale->month[t->tm_mon],
     180                 :                               pt, ptlim);
     181               0 :                     continue;
     182 CBC         400 :                 case 'b':
     183                 :                 case 'h':
     184             800 :                     pt = _add((t->tm_mon < 0 ||
     185             400 :                                t->tm_mon >= MONSPERYEAR) ?
     186             400 :                               "?" : Locale->mon[t->tm_mon],
     187                 :                               pt, ptlim);
     188             400 :                     continue;
     189 UBC           0 :                 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                 :                                 true, false, pt, ptlim);
     198               0 :                     continue;
     199               0 :                 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                 :                     }
     209               0 :                     continue;
     210               0 :                 case 'D':
     211               0 :                     pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp);
     212               0 :                     continue;
     213 CBC      350781 :                 case 'd':
     214          350781 :                     pt = _conv(t->tm_mday, "%02d", pt, ptlim);
     215          350781 :                     continue;
     216 UBC           0 :                 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               0 :                 case 'e':
     227               0 :                     pt = _conv(t->tm_mday, "%2d", pt, ptlim);
     228               0 :                     continue;
     229               0 :                 case 'F':
     230               0 :                     pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
     231               0 :                     continue;
     232 CBC      350781 :                 case 'H':
     233          350781 :                     pt = _conv(t->tm_hour, "%02d", pt, ptlim);
     234          350781 :                     continue;
     235 UBC           0 :                 case 'I':
     236               0 :                     pt = _conv((t->tm_hour % 12) ?
     237               0 :                                (t->tm_hour % 12) : 12,
     238                 :                                "%02d", pt, ptlim);
     239               0 :                     continue;
     240               0 :                 case 'j':
     241               0 :                     pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim);
     242               0 :                     continue;
     243               0 :                 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               0 :                 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                 :                                "%2d", pt, ptlim);
     274               0 :                     continue;
     275 CBC      350781 :                 case 'M':
     276          350781 :                     pt = _conv(t->tm_min, "%02d", pt, ptlim);
     277          350781 :                     continue;
     278          350381 :                 case 'm':
     279          350381 :                     pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim);
     280          350381 :                     continue;
     281 UBC           0 :                 case 'n':
     282               0 :                     pt = _add("\n", pt, ptlim);
     283               0 :                     continue;
     284               0 :                 case 'p':
     285               0 :                     pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
     286                 :                               Locale->pm :
     287                 :                               Locale->am,
     288                 :                               pt, ptlim);
     289               0 :                     continue;
     290               0 :                 case 'R':
     291               0 :                     pt = _fmt("%H:%M", t, pt, ptlim, warnp);
     292               0 :                     continue;
     293               0 :                 case 'r':
     294               0 :                     pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp);
     295               0 :                     continue;
     296 CBC      350781 :                 case 'S':
     297          350781 :                     pt = _conv(t->tm_sec, "%02d", pt, ptlim);
     298          350781 :                     continue;
     299 UBC           0 :                 case 'T':
     300               0 :                     pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp);
     301               0 :                     continue;
     302               0 :                 case 't':
     303               0 :                     pt = _add("\t", pt, ptlim);
     304               0 :                     continue;
     305               0 :                 case 'U':
     306               0 :                     pt = _conv((t->tm_yday + DAYSPERWEEK -
     307               0 :                                 t->tm_wday) / DAYSPERWEEK,
     308                 :                                "%02d", pt, ptlim);
     309               0 :                     continue;
     310               0 :                 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                 :                                DAYSPERWEEK : t->tm_wday,
     319                 :                                "%d", pt, ptlim);
     320               0 :                     continue;
     321               0 :                 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                 :                         int         year;
     344                 :                         int         base;
     345                 :                         int         yday;
     346                 :                         int         wday;
     347                 :                         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                 :                         for (;;)
     354               0 :                         {
     355                 :                             int         len;
     356                 :                             int         bot;
     357                 :                             int         top;
     358                 : 
     359               0 :                             len = isleap_sum(year, base) ?
     360               0 :                                 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                 :                                    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               0 :                                 DAYSPERLYEAR :
     393                 :                                 DAYSPERNYEAR;
     394                 :                         }
     395               0 :                         if (*format == 'V')
     396               0 :                             pt = _conv(w, "%02d",
     397                 :                                        pt, ptlim);
     398               0 :                         else if (*format == 'g')
     399                 :                         {
     400               0 :                             *warnp = IN_ALL;
     401               0 :                             pt = _yconv(year, base,
     402                 :                                         false, true,
     403                 :                                         pt, ptlim);
     404                 :                         }
     405                 :                         else
     406               0 :                             pt = _yconv(year, base,
     407                 :                                         true, true,
     408                 :                                         pt, ptlim);
     409                 :                     }
     410               0 :                     continue;
     411               0 :                 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               0 :                 case 'W':
     420               0 :                     pt = _conv((t->tm_yday + DAYSPERWEEK -
     421               0 :                                 (t->tm_wday ?
     422               0 :                                  (t->tm_wday - 1) :
     423                 :                                  (DAYSPERWEEK - 1))) / DAYSPERWEEK,
     424                 :                                "%02d", pt, ptlim);
     425               0 :                     continue;
     426               0 :                 case 'w':
     427               0 :                     pt = _conv(t->tm_wday, "%d", pt, ptlim);
     428               0 :                     continue;
     429               0 :                 case 'X':
     430               0 :                     pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp);
     431               0 :                     continue;
     432               0 :                 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                 :                     }
     442               0 :                     continue;
     443               0 :                 case 'y':
     444               0 :                     *warnp = IN_ALL;
     445               0 :                     pt = _yconv(t->tm_year, TM_YEAR_BASE,
     446                 :                                 false, true,
     447                 :                                 pt, ptlim);
     448               0 :                     continue;
     449 CBC      350781 :                 case 'Y':
     450          350781 :                     pt = _yconv(t->tm_year, TM_YEAR_BASE,
     451                 :                                 true, true,
     452                 :                                 pt, ptlim);
     453          350781 :                     continue;
     454          350772 :                 case 'Z':
     455          350772 :                     if (t->tm_zone != NULL)
     456          350772 :                         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          350772 :                     continue;
     464 UBC           0 :                 case 'z':
     465                 :                     {
     466                 :                         long        diff;
     467                 :                         char const *sign;
     468                 :                         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                 :                         }
     479               0 :                         if (negative)
     480                 :                         {
     481               0 :                             sign = "-";
     482               0 :                             diff = -diff;
     483                 :                         }
     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                 :                     }
     492               0 :                     continue;
     493               0 :                 case '+':
     494               0 :                     pt = _fmt(Locale->date_fmt, t, pt, ptlim,
     495                 :                               warnp);
     496               0 :                     continue;
     497 CBC         400 :                 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             400 :                     break;
     506                 :             }
     507                 :         }
     508         3030610 :         if (pt == ptlim)
     509 UBC           0 :             break;
     510 CBC     3030610 :         *pt++ = *format;
     511                 :     }
     512          350781 :     return pt;
     513                 : }
     514                 : 
     515                 : static char *
     516         2455067 : _conv(int n, const char *format, char *pt, const char *ptlim)
     517                 : {
     518                 :     char        buf[INT_STRLEN_MAXIMUM(int) + 1];
     519                 : 
     520         2455067 :     sprintf(buf, format, n);
     521         2455067 :     return _add(buf, pt, ptlim);
     522                 : }
     523                 : 
     524                 : static char *
     525         2806639 : _add(const char *str, char *pt, const char *ptlim)
     526                 : {
     527         8771489 :     while (pt < ptlim && (*pt = *str++) != '\0')
     528         5964850 :         ++pt;
     529         2806639 :     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          350781 : _yconv(int a, int b, bool convert_top, bool convert_yy,
     542                 :        char *pt, const char *ptlim)
     543                 : {
     544                 :     int         lead;
     545                 :     int         trail;
     546                 : 
     547                 : #define DIVISOR 100
     548          350781 :     trail = a % DIVISOR + b % DIVISOR;
     549          350781 :     lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
     550          350781 :     trail %= DIVISOR;
     551          350781 :     if (trail < 0 && lead > 0)
     552                 :     {
     553 UBC           0 :         trail += DIVISOR;
     554               0 :         --lead;
     555                 :     }
     556 CBC      350781 :     else if (lead < 0 && trail > 0)
     557                 :     {
     558 UBC           0 :         trail -= DIVISOR;
     559               0 :         ++lead;
     560                 :     }
     561 CBC      350781 :     if (convert_top)
     562                 :     {
     563          350781 :         if (lead == 0 && trail < 0)
     564 UBC           0 :             pt = _add("-0", pt, ptlim);
     565                 :         else
     566 CBC      350781 :             pt = _conv(lead, "%02d", pt, ptlim);
     567                 :     }
     568          350781 :     if (convert_yy)
     569          350781 :         pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim);
     570          350781 :     return pt;
     571                 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a