Home | History | Annotate | Download | only in Time
      1 /** @file
      2     Implementation of the strftime function for <time.h>.
      3 
      4     Based on the UCB version with the ID appearing below.
      5     This is ANSIish only when "multibyte character == plain character".
      6 
      7     Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
      8     This program and the accompanying materials are licensed and made available under
      9     the terms and conditions of the BSD License that accompanies this distribution.
     10     The full text of the license may be found at
     11     http://opensource.org/licenses/bsd-license.php.
     12 
     13     THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     14     WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     15 
     16     Copyright (c) 1989, 1993
     17     The Regents of the University of California.  All rights reserved.
     18 
     19     Redistribution and use in source and binary forms, with or without
     20     modification, are permitted provided that the following conditions
     21     are met:
     22     1. Redistributions of source code must retain the above copyright
     23       notice, this list of conditions and the following disclaimer.
     24     2. Redistributions in binary form must reproduce the above copyright
     25       notice, this list of conditions and the following disclaimer in the
     26       documentation and/or other materials provided with the distribution.
     27     3. All advertising materials mentioning features or use of this software
     28       must display the following acknowledgement:
     29     This product includes software developed by the University of
     30     California, Berkeley and its contributors.
     31     4. Neither the name of the University nor the names of its contributors
     32       may be used to endorse or promote products derived from this software
     33       without specific prior written permission.
     34 
     35     THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     36     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     37     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     38     ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     39     FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     40     DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     41     OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     42     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     43     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     44     OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     45     SUCH DAMAGE.
     46 
     47   NetBSD: strftime.c,v 1.17.4.1 2007/08/21 20:08:21 liamjfoy Exp
     48 **/
     49 #include  <LibConfig.h>
     50 #include <sys/EfiCdefs.h>
     51 
     52 #include "namespace.h"
     53 #include <time.h>
     54 #include "tzfile.h"
     55 #include  "TimeVals.h"
     56 #include "fcntl.h"
     57 #include "locale.h"
     58 
     59 #include "sys/localedef.h"
     60 #include  <MainData.h>
     61 
     62 /*
     63 ** We don't use these extensions in strftime operation even when
     64 ** supported by the local tzcode configuration.  A strictly
     65 ** conforming C application may leave them in undefined state.
     66 */
     67 
     68 #ifdef _LIBC
     69 #undef TM_ZONE
     70 #undef TM_GMTOFF
     71 #endif
     72 
     73 #define Locale  _CurrentTimeLocale
     74 
     75 static char * _add(const char *, char *, const char * const);
     76 static char * _conv(const int, const char * const, char * const, const char * const);
     77 static char * _fmt(const char *, const struct tm * const, char *, const char * const, int *);
     78 
     79 #define NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU
     80 
     81 #ifndef YEAR_2000_NAME
     82 #define YEAR_2000_NAME  "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
     83 #endif /* !defined YEAR_2000_NAME */
     84 
     85 
     86 #define IN_NONE 0
     87 #define IN_SOME 1
     88 #define IN_THIS 2
     89 #define IN_ALL  3
     90 
     91 size_t
     92 strftime(
     93   char            * __restrict  s,
     94   size_t                        maxsize,
     95   const char      * __restrict  format,
     96   const struct tm * __restrict  timeptr
     97   )
     98 {
     99   char *  p;
    100   int warn;
    101 
    102   tzset();
    103   warn = IN_NONE;
    104   p = _fmt(((format == NULL) ? "%c" : format), timeptr, s, s + maxsize, &warn);
    105 
    106 #ifndef NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU
    107   if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) {
    108     (void) fprintf(stderr, "\n");
    109     if (format == NULL)
    110       (void) fprintf(stderr, "NULL strftime format ");
    111     else  (void) fprintf(stderr, "strftime format \"%s\" ",
    112         format);
    113     (void) fprintf(stderr, "yields only two digits of years in ");
    114     if (warn == IN_SOME)
    115       (void) fprintf(stderr, "some locales");
    116     else if (warn == IN_THIS)
    117       (void) fprintf(stderr, "the current locale");
    118     else  (void) fprintf(stderr, "all locales");
    119     (void) fprintf(stderr, "\n");
    120   }
    121 #endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */
    122 
    123   if (p == s + maxsize)
    124     return 0;
    125   *p = '\0';
    126   return p - s;
    127 }
    128 
    129 static char *
    130 _fmt(
    131   const char      *       format,
    132   const struct tm * const t,
    133   char            *       pt,
    134   const char      * const ptlim,
    135   int             *       warnp
    136   )
    137 {
    138   for ( ; *format; ++format) {
    139     if (*format == '%') {
    140 label:
    141       switch (*++format) {
    142       case '\0':
    143         --format;
    144         break;
    145       case 'A':
    146         pt = _add((t->tm_wday < 0 ||
    147           t->tm_wday >= DAYSPERWEEK) ?
    148           "?" : Locale->day[t->tm_wday],
    149           pt, ptlim);
    150         continue;
    151       case 'a':
    152         pt = _add((t->tm_wday < 0 ||
    153           t->tm_wday >= DAYSPERWEEK) ?
    154           "?" : Locale->abday[t->tm_wday],
    155           pt, ptlim);
    156         continue;
    157       case 'B':
    158         pt = _add((t->tm_mon < 0 ||
    159           t->tm_mon >= MONSPERYEAR) ?
    160           "?" : Locale->mon[t->tm_mon],
    161           pt, ptlim);
    162         continue;
    163       case 'b':
    164       case 'h':
    165         pt = _add((t->tm_mon < 0 ||
    166           t->tm_mon >= MONSPERYEAR) ?
    167           "?" : Locale->abmon[t->tm_mon],
    168           pt, ptlim);
    169         continue;
    170       case 'C':
    171         /*
    172         ** %C used to do a...
    173         **  _fmt("%a %b %e %X %Y", t);
    174         ** ...whereas now POSIX 1003.2 calls for
    175         ** something completely different.
    176         ** (ado, 1993-05-24)
    177         */
    178         pt = _conv((t->tm_year + TM_YEAR_BASE) / 100,
    179           "%02d", pt, ptlim);
    180         continue;
    181       case 'c':
    182         {
    183         int warn2 = IN_SOME;
    184 
    185         pt = _fmt(Locale->d_t_fmt, t, pt, ptlim, &warn2);
    186         if (warn2 == IN_ALL)
    187           warn2 = IN_THIS;
    188         if (warn2 > *warnp)
    189           *warnp = warn2;
    190         }
    191         continue;
    192       case 'D':
    193         pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp);
    194         continue;
    195       case 'd':
    196         pt = _conv(t->tm_mday, "%02d", pt, ptlim);
    197         continue;
    198       case 'E':
    199       case 'O':
    200         /*
    201         ** C99 locale modifiers.
    202         ** The sequences
    203         **  %Ec %EC %Ex %EX %Ey %EY
    204         **  %Od %oe %OH %OI %Om %OM
    205         **  %OS %Ou %OU %OV %Ow %OW %Oy
    206         ** are supposed to provide alternate
    207         ** representations.
    208         */
    209         goto label;
    210       case 'e':
    211         pt = _conv(t->tm_mday, "%2d", pt, ptlim);
    212         continue;
    213       case 'F':
    214         pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
    215         continue;
    216       case 'H':
    217         pt = _conv(t->tm_hour, "%02d", pt, ptlim);
    218         continue;
    219       case 'I':
    220         pt = _conv((t->tm_hour % 12) ?
    221           (t->tm_hour % 12) : 12,
    222           "%02d", pt, ptlim);
    223         continue;
    224       case 'j':
    225         pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim);
    226         continue;
    227       case 'k':
    228         /*
    229         ** This used to be...
    230         **  _conv(t->tm_hour % 12 ?
    231         **    t->tm_hour % 12 : 12, 2, ' ');
    232         ** ...and has been changed to the below to
    233         ** match SunOS 4.1.1 and Arnold Robbins'
    234         ** strftime version 3.0.  That is, "%k" and
    235         ** "%l" have been swapped.
    236         ** (ado, 1993-05-24)
    237         */
    238         pt = _conv(t->tm_hour, "%2d", pt, ptlim);
    239         continue;
    240 #ifdef KITCHEN_SINK
    241       case 'K':
    242         /*
    243         ** After all this time, still unclaimed!
    244         */
    245         pt = _add("kitchen sink", pt, ptlim);
    246         continue;
    247 #endif /* defined KITCHEN_SINK */
    248       case 'l':
    249         /*
    250         ** This used to be...
    251         **  _conv(t->tm_hour, 2, ' ');
    252         ** ...and has been changed to the below to
    253         ** match SunOS 4.1.1 and Arnold Robbin's
    254         ** strftime version 3.0.  That is, "%k" and
    255         ** "%l" have been swapped.
    256         ** (ado, 1993-05-24)
    257         */
    258         pt = _conv((t->tm_hour % 12) ?
    259           (t->tm_hour % 12) : 12,
    260           "%2d", pt, ptlim);
    261         continue;
    262       case 'M':
    263         pt = _conv(t->tm_min, "%02d", pt, ptlim);
    264         continue;
    265       case 'm':
    266         pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim);
    267         continue;
    268       case 'n':
    269         pt = _add("\n", pt, ptlim);
    270         continue;
    271       case 'p':
    272         pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
    273           Locale->am_pm[1] :
    274           Locale->am_pm[0],
    275           pt, ptlim);
    276         continue;
    277       case 'R':
    278         pt = _fmt("%H:%M", t, pt, ptlim, warnp);
    279         continue;
    280       case 'r':
    281         pt = _fmt(Locale->t_fmt_ampm, t, pt, ptlim,
    282                 warnp);
    283         continue;
    284       case 'S':
    285         pt = _conv(t->tm_sec, "%02d", pt, ptlim);
    286         continue;
    287       case 's':
    288         {
    289           struct tm tm;
    290           char    buf[INT_STRLEN_MAXIMUM(
    291                 time_t) + 1];
    292           time_t    mkt;
    293 
    294           tm = *t;
    295           mkt = mktime(&tm);
    296           /* CONSTCOND */
    297           if (TYPE_SIGNED(time_t))
    298             (void) sprintf(buf, "%ld",
    299               (long) mkt);
    300           else  (void) sprintf(buf, "%lu",
    301               (unsigned long) mkt);
    302           pt = _add(buf, pt, ptlim);
    303         }
    304         continue;
    305       case 'T':
    306         pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp);
    307         continue;
    308       case 't':
    309         pt = _add("\t", pt, ptlim);
    310         continue;
    311       case 'U':
    312         pt = _conv((t->tm_yday + DAYSPERWEEK -
    313           t->tm_wday) / DAYSPERWEEK,
    314           "%02d", pt, ptlim);
    315         continue;
    316       case 'u':
    317         /*
    318         ** From Arnold Robbins' strftime version 3.0:
    319         ** "ISO 8601: Weekday as a decimal number
    320         ** [1 (Monday) - 7]"
    321         ** (ado, 1993-05-24)
    322         */
    323         pt = _conv((t->tm_wday == 0) ?
    324           DAYSPERWEEK : t->tm_wday,
    325           "%d", pt, ptlim);
    326         continue;
    327       case 'V': /* ISO 8601 week number */
    328       case 'G': /* ISO 8601 year (four digits) */
    329       case 'g': /* ISO 8601 year (two digits) */
    330 /*
    331 ** From Arnold Robbins' strftime version 3.0:  "the week number of the
    332 ** year (the first Monday as the first day of week 1) as a decimal number
    333 ** (01-53)."
    334 ** (ado, 1993-05-24)
    335 **
    336 ** From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn:
    337 ** "Week 01 of a year is per definition the first week which has the
    338 ** Thursday in this year, which is equivalent to the week which contains
    339 ** the fourth day of January. In other words, the first week of a new year
    340 ** is the week which has the majority of its days in the new year. Week 01
    341 ** might also contain days from the previous year and the week before week
    342 ** 01 of a year is the last week (52 or 53) of the previous year even if
    343 ** it contains days from the new year. A week starts with Monday (day 1)
    344 ** and ends with Sunday (day 7).  For example, the first week of the year
    345 ** 1997 lasts from 1996-12-30 to 1997-01-05..."
    346 ** (ado, 1996-01-02)
    347 */
    348         {
    349           int year;
    350           int yday;
    351           int wday;
    352           int w;
    353 
    354           year = t->tm_year + TM_YEAR_BASE;
    355           yday = t->tm_yday;
    356           wday = t->tm_wday;
    357           for ( ; ; ) {
    358             int len;
    359             int bot;
    360             int top;
    361 
    362             len = isleap(year) ?
    363               DAYSPERLYEAR :
    364               DAYSPERNYEAR;
    365             /*
    366             ** What yday (-3 ... 3) does
    367             ** the ISO year begin on?
    368             */
    369             bot = ((yday + 11 - wday) %
    370               DAYSPERWEEK) - 3;
    371             /*
    372             ** What yday does the NEXT
    373             ** ISO year begin on?
    374             */
    375             top = bot -
    376               (len % DAYSPERWEEK);
    377             if (top < -3)
    378               top += DAYSPERWEEK;
    379             top += len;
    380             if (yday >= top) {
    381               ++year;
    382               w = 1;
    383               break;
    384             }
    385             if (yday >= bot) {
    386               w = 1 + ((yday - bot) /
    387                 DAYSPERWEEK);
    388               break;
    389             }
    390             --year;
    391             yday += isleap(year) ?
    392               DAYSPERLYEAR :
    393               DAYSPERNYEAR;
    394           }
    395 #ifdef XPG4_1994_04_09
    396           if ((w == 52
    397                && t->tm_mon == TM_JANUARY)
    398               || (w == 1
    399             && t->tm_mon == TM_DECEMBER))
    400             w = 53;
    401 #endif /* defined XPG4_1994_04_09 */
    402           if (*format == 'V')
    403             pt = _conv(w, "%02d",
    404               pt, ptlim);
    405           else if (*format == 'g') {
    406             *warnp = IN_ALL;
    407             pt = _conv(year % 100, "%02d",
    408               pt, ptlim);
    409           } else  pt = _conv(year, "%04d",
    410               pt, ptlim);
    411         }
    412         continue;
    413       case 'v':
    414         /*
    415         ** From Arnold Robbins' strftime version 3.0:
    416         ** "date as dd-bbb-YYYY"
    417         ** (ado, 1993-05-24)
    418         */
    419         pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp);
    420         continue;
    421       case 'W':
    422         pt = _conv((t->tm_yday + DAYSPERWEEK -
    423           (t->tm_wday ?
    424           (t->tm_wday - 1) :
    425           (DAYSPERWEEK - 1))) / DAYSPERWEEK,
    426           "%02d", pt, ptlim);
    427         continue;
    428       case 'w':
    429         pt = _conv(t->tm_wday, "%d", pt, ptlim);
    430         continue;
    431       case 'X':
    432         pt = _fmt(Locale->t_fmt, t, pt, ptlim, warnp);
    433         continue;
    434       case 'x':
    435         {
    436         int warn2 = IN_SOME;
    437 
    438         pt = _fmt(Locale->d_fmt, t, pt, ptlim, &warn2);
    439         if (warn2 == IN_ALL)
    440           warn2 = IN_THIS;
    441         if (warn2 > *warnp)
    442           *warnp = warn2;
    443         }
    444         continue;
    445       case 'y':
    446         *warnp = IN_ALL;
    447         pt = _conv((t->tm_year + TM_YEAR_BASE) % 100,
    448           "%02d", pt, ptlim);
    449         continue;
    450       case 'Y':
    451         pt = _conv(t->tm_year + TM_YEAR_BASE, "%04d",
    452           pt, ptlim);
    453         continue;
    454       case 'Z':
    455 #ifdef TM_ZONE
    456         if (t->TM_ZONE != NULL)
    457           pt = _add(t->TM_ZONE, pt, ptlim);
    458         else
    459 #endif /* defined TM_ZONE */
    460         if (t->tm_isdst >= 0)
    461           pt = _add(tzname[t->tm_isdst != 0],
    462             pt, ptlim);
    463         /*
    464         ** C99 says that %Z must be replaced by the
    465         ** empty string if the time zone is not
    466         ** determinable.
    467         */
    468         continue;
    469       case 'z':
    470         {
    471         int   diff;
    472         char const *  sign;
    473 
    474         if (t->tm_isdst < 0)
    475           continue;
    476 #ifdef TM_GMTOFF
    477         diff = (int)t->TM_GMTOFF;
    478 #else /* !defined TM_GMTOFF */
    479         /*
    480         ** C99 says that the UTC offset must
    481         ** be computed by looking only at
    482         ** tm_isdst.  This requirement is
    483         ** incorrect, since it means the code
    484         ** must rely on magic (in this case
    485         ** altzone and timezone), and the
    486         ** magic might not have the correct
    487         ** offset.  Doing things correctly is
    488         ** tricky and requires disobeying C99;
    489         ** see GNU C strftime for details.
    490         ** For now, punt and conform to the
    491         ** standard, even though it's incorrect.
    492         **
    493         ** C99 says that %z must be replaced by the
    494         ** empty string if the time zone is not
    495         ** determinable, so output nothing if the
    496         ** appropriate variables are not available.
    497         */
    498 #ifndef STD_INSPIRED
    499         if (t->tm_isdst == 0)
    500 #ifdef USG_COMPAT
    501           diff = -timezone;
    502 #else /* !defined USG_COMPAT */
    503           continue;
    504 #endif /* !defined USG_COMPAT */
    505         else
    506 #ifdef ALTZONE
    507           diff = -altzone;
    508 #else /* !defined ALTZONE */
    509           continue;
    510 #endif /* !defined ALTZONE */
    511 #else /* defined STD_INSPIRED */
    512         {
    513           struct tm tmp;
    514           time_t lct, gct;
    515 
    516           /*
    517           ** Get calendar time from t
    518           ** being treated as local.
    519           */
    520           tmp = *t; /* mktime discards const */
    521           lct = mktime(&tmp);
    522 
    523           if (lct == (time_t)-1)
    524             continue;
    525 
    526           /*
    527           ** Get calendar time from t
    528           ** being treated as GMT.
    529           **/
    530           tmp = *t; /* mktime discards const */
    531           gct = timegm(&tmp);
    532 
    533           if (gct == (time_t)-1)
    534             continue;
    535 
    536           /* LINTED difference will fit int */
    537           diff = (intmax_t)gct - (intmax_t)lct;
    538         }
    539 #endif /* defined STD_INSPIRED */
    540 #endif /* !defined TM_GMTOFF */
    541         if (diff < 0) {
    542           sign = "-";
    543           diff = -diff;
    544         } else  sign = "+";
    545         pt = _add(sign, pt, ptlim);
    546         diff /= 60;
    547         pt = _conv((diff/60)*100 + diff%60,
    548           "%04d", pt, ptlim);
    549         }
    550         continue;
    551 #if 0
    552       case '+':
    553         pt = _fmt(Locale->date_fmt, t, pt, ptlim,
    554           warnp);
    555         continue;
    556 #endif
    557       case '%':
    558       /*
    559       ** X311J/88-090 (4.12.3.5): if conversion char is
    560       ** undefined, behavior is undefined.  Print out the
    561       ** character itself as printf(3) also does.
    562       */
    563       default:
    564         break;
    565       }
    566     }
    567     if (pt == ptlim)
    568       break;
    569     *pt++ = *format;
    570   }
    571   return pt;
    572 }
    573 
    574 static char *
    575 _conv(
    576   const int             n,
    577   const char  * const   format,
    578   char        * const   pt,
    579   const char  * const   ptlim
    580 )
    581 {
    582   char  buf[INT_STRLEN_MAXIMUM(int) + 1];
    583 
    584   (void) sprintf(buf, format, n);
    585   return _add(buf, pt, ptlim);
    586 }
    587 
    588 static char *
    589 _add(
    590   const char  *       str,
    591   char        *       pt,
    592   const char  * const ptlim
    593 )
    594 {
    595   while (pt < ptlim && (*pt = *str++) != '\0')
    596     ++pt;
    597   return pt;
    598 }
    599