"Fossies" - the Fresh Open Source Software Archive

Member "strftime.c" (27 Jun 2018, 16103 Bytes) of package /linux/misc/tzcode2018i.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "strftime.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 2018e_vs_2018f.

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