Home | History | Annotate | Download | only in Time
      1 /**
      2   Definitions and Implementation for <time.h>.
      3 
      4   Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>
      5   This program and the accompanying materials are licensed and made available under
      6   the terms and conditions of the BSD License that accompanies this distribution.
      7   The full text of the license may be found at
      8   http://opensource.org/licenses/bsd-license.php.
      9 
     10   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 
     13   Portions derived from the NIH time zone package file, localtime.c,
     14   which contains the following notice:
     15 
     16     This file is in the public domain, so clarified as of
     17     1996-06-05 by Arthur David Olson (arthur_david_olson (at) nih.gov).
     18 
     19   NetBSD: localtime.c,v 1.39 2006/03/22 14:01:30 christos Exp
     20 **/
     21 #include  <Uefi.h>
     22 #include  <Library/UefiLib.h>
     23 #include  <Library/TimerLib.h>
     24 #include  <Library/BaseLib.h>
     25 #include  <Library/UefiRuntimeServicesTableLib.h>
     26 //#include  <Library/UefiRuntimeLib.h>
     27 
     28 #include  <LibConfig.h>
     29 
     30 #include  <errno.h>
     31 #include  <limits.h>
     32 #include  <time.h>
     33 #include  <reentrant.h>
     34 #include  "tzfile.h"
     35 #include  "TimeVals.h"
     36 #include  <MainData.h>
     37 #include  <extern.h>      // Library/include/extern.h: Private to implementation
     38 
     39 #if defined(_MSC_VER)           /* Handle Microsoft VC++ compiler specifics. */
     40 // Keep compiler quiet about casting from function to data pointers
     41 #pragma warning ( disable : 4054 )
     42 #endif  /* defined(_MSC_VER) */
     43 
     44 /* #######################  Private Data  ################################# */
     45 
     46 #if 0
     47 static EFI_TIME TimeBuffer;
     48 
     49   static  UINT16   MonthOffs[12] = {
     50      00,
     51      31,   59,   90,  120,
     52     151,  181,  212,  243,
     53     273,  304,  334
     54   };
     55   static  clock_t   y2kOffs = 730485;
     56 #endif
     57 
     58 const int  mon_lengths[2][MONSPERYEAR] = {
     59   { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
     60   { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
     61 };
     62 
     63 const int  year_lengths[2] = {
     64   DAYSPERNYEAR, DAYSPERLYEAR
     65 };
     66 
     67 
     68 static const char *wday_name[7] = {
     69   "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
     70 };
     71 
     72 static const char *mon_name[12] = {
     73   "Jan", "Feb", "Mar", "Apr", "May", "Jun",
     74   "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
     75 };
     76 
     77 static int    gmt_is_set;
     78 
     79 /* ###############  Implementation Functions  ############################ */
     80 // Forward reference
     81 static void
     82 localsub(const time_t * const timep, const long   offset, struct tm * const tmp);
     83 
     84 clock_t
     85 __getCPS(void)
     86 {
     87   return gMD->ClocksPerSecond;
     88 }
     89 
     90 static void
     91 timesub(
     92   const time_t        * const timep,
     93   const long                  offset,
     94   const struct state  * const sp,
     95         struct tm     * const tmp
     96   )
     97 {
     98   const struct lsinfo *  lp;
     99   time_t /*INTN*/     days;
    100   time_t /*INTN*/     rem;
    101   time_t /*INTN*/      y;
    102   int      yleap;
    103   const int *    ip;
    104   time_t /*INTN*/     corr;
    105   int      hit;
    106   int      i;
    107 
    108   corr = 0;
    109   hit = 0;
    110 #ifdef ALL_STATE
    111   i = (sp == NULL) ? 0 : sp->leapcnt;
    112 #endif /* defined ALL_STATE */
    113 #ifndef ALL_STATE
    114   i = sp->leapcnt;
    115 #endif /* State Farm */
    116   while (--i >= 0) {
    117     lp = &sp->lsis[i];
    118     if (*timep >= lp->ls_trans) {
    119       if (*timep == lp->ls_trans) {
    120         hit = ((i == 0 && lp->ls_corr > 0) ||
    121                lp->ls_corr > sp->lsis[i - 1].ls_corr);
    122         if (hit)
    123           while (i > 0                                                &&
    124                  sp->lsis[i].ls_trans == sp->lsis[i - 1].ls_trans + 1 &&
    125                  sp->lsis[i].ls_corr  == sp->lsis[i - 1].ls_corr  + 1 )
    126           {
    127             ++hit;
    128             --i;
    129           }
    130       }
    131       corr = lp->ls_corr;
    132       break;
    133     }
    134   }
    135   days = *timep / SECSPERDAY;
    136   rem = *timep % SECSPERDAY;
    137   rem += (offset - corr);
    138   while (rem < 0) {
    139     rem += SECSPERDAY;
    140     --days;
    141   }
    142   while (rem >= SECSPERDAY) {
    143     rem -= SECSPERDAY;
    144     ++days;
    145   }
    146   tmp->tm_hour = (int) (rem / SECSPERHOUR);
    147   rem = rem % SECSPERHOUR;
    148   tmp->tm_min = (int) (rem / SECSPERMIN);
    149   /*
    150   ** A positive leap second requires a special
    151   ** representation.  This uses "... ??:59:60" et seq.
    152   */
    153   tmp->tm_sec = (int) (rem % SECSPERMIN) + hit;
    154   tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK);
    155   if (tmp->tm_wday < 0)
    156     tmp->tm_wday += DAYSPERWEEK;
    157   y = EPOCH_YEAR;
    158   while (days < 0 || days >= (LONG32) year_lengths[yleap = isleap(y)]) {
    159     time_t /*INTN*/  newy;
    160 
    161     newy = (y + days / DAYSPERNYEAR);
    162     if (days < 0)
    163       --newy;
    164     days -= (newy - y) * DAYSPERNYEAR +
    165       LEAPS_THRU_END_OF(newy - 1) -
    166       LEAPS_THRU_END_OF(y - 1);
    167     y = newy;
    168   }
    169   tmp->tm_year = (int)(y - TM_YEAR_BASE);
    170   tmp->tm_yday = (int) days;
    171   ip = mon_lengths[yleap];
    172   for (tmp->tm_mon = 0; days >= (LONG32) ip[tmp->tm_mon]; ++(tmp->tm_mon))
    173     days = days - (LONG32) ip[tmp->tm_mon];
    174   tmp->tm_mday = (int) (days + 1);
    175   tmp->tm_isdst = 0;
    176 #ifdef TM_GMTOFF
    177   tmp->TM_GMTOFF = offset;
    178 #endif /* defined TM_GMTOFF */
    179 }
    180 
    181 /* ###############  Time Manipulation Functions  ########################## */
    182 
    183 /**
    184 **/
    185 double
    186 difftime(time_t time1, time_t time0)
    187 {
    188   return (double)(time1 - time0);
    189 }
    190 
    191 /*
    192 ** Adapted from code provided by Robert Elz, who writes:
    193 **  The "best" way to do mktime I think is based on an idea of Bob
    194 **  Kridle's (so its said...) from a long time ago.
    195 **  [kridle (at) xinet.com as of 1996-01-16.]
    196 **  It does a binary search of the time_t space.  Since time_t's are
    197 **  just 32 bits, its a max of 32 iterations (even at 64 bits it
    198 **  would still be very reasonable).
    199 */
    200 
    201 #ifndef WRONG
    202 #define WRONG (-1)
    203 #endif /* !defined WRONG */
    204 
    205 /*
    206 ** Simplified normalize logic courtesy Paul Eggert (eggert (at) twinsun.com).
    207 */
    208 
    209 static int
    210 increment_overflow(int * number, int delta)
    211 {
    212   int number0;
    213 
    214   number0 = *number;
    215   *number += delta;
    216   return (*number < number0) != (delta < 0);
    217 }
    218 
    219 static int
    220 normalize_overflow(int * const tensptr, int * const unitsptr, const int base)
    221 {
    222   register int  tensdelta;
    223 
    224   tensdelta = (*unitsptr >= 0)  ?
    225               (*unitsptr / base) : (-1 - (-1 - *unitsptr) / base);
    226   *unitsptr -= tensdelta * base;
    227   return increment_overflow(tensptr, tensdelta);
    228 }
    229 
    230 static int
    231 tmcomp(const struct tm * const atmp, const struct tm * const btmp)
    232 {
    233   register int  result;
    234 
    235   if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&
    236       (result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
    237       (result = (atmp->tm_mday - btmp->tm_mday)) == 0 &&
    238       (result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
    239       (result = (atmp->tm_min - btmp->tm_min)) == 0)
    240     result = atmp->tm_sec - btmp->tm_sec;
    241   return result;
    242 }
    243 
    244 static time_t
    245 time2sub(
    246   struct tm * const tmp,
    247   void (* const funcp)(const time_t*, long, struct tm*),
    248   const long offset,
    249   int * const okayp,
    250   const int do_norm_secs
    251   )
    252 {
    253   register const struct state * sp;
    254   register int                  dir;
    255   register int                  bits;
    256   register int                  i, j ;
    257   register int                  saved_seconds;
    258   time_t                        newt;
    259   time_t                        t;
    260   struct tm                     yourtm, mytm;
    261 
    262   *okayp = FALSE;
    263   yourtm = *tmp;    // Create a copy of tmp
    264   if (do_norm_secs) {
    265     if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec,
    266                            SECSPERMIN))
    267       return WRONG;
    268   }
    269   if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR))
    270     return WRONG;
    271   if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
    272     return WRONG;
    273   if (normalize_overflow(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR))
    274     return WRONG;
    275   /*
    276   ** Turn yourtm.tm_year into an actual year number for now.
    277   ** It is converted back to an offset from TM_YEAR_BASE later.
    278   */
    279   if (increment_overflow(&yourtm.tm_year, TM_YEAR_BASE))
    280     return WRONG;
    281   while (yourtm.tm_mday <= 0) {
    282     if (increment_overflow(&yourtm.tm_year, -1))
    283       return WRONG;
    284     i = yourtm.tm_year + (1 < yourtm.tm_mon);
    285     yourtm.tm_mday += year_lengths[isleap(i)];
    286   }
    287   while (yourtm.tm_mday > DAYSPERLYEAR) {
    288     i = yourtm.tm_year + (1 < yourtm.tm_mon);
    289     yourtm.tm_mday -= year_lengths[isleap(i)];
    290     if (increment_overflow(&yourtm.tm_year, 1))
    291       return WRONG;
    292   }
    293   for ( ; ; ) {
    294     i = mon_lengths[isleap(yourtm.tm_year)][yourtm.tm_mon];
    295     if (yourtm.tm_mday <= i)
    296       break;
    297     yourtm.tm_mday -= i;
    298     if (++yourtm.tm_mon >= MONSPERYEAR) {
    299       yourtm.tm_mon = 0;
    300       if (increment_overflow(&yourtm.tm_year, 1))
    301         return WRONG;
    302     }
    303   }
    304   if (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE))
    305     return WRONG;
    306   if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN)
    307     saved_seconds = 0;
    308   else if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) {
    309     /*
    310     ** We can't set tm_sec to 0, because that might push the
    311     ** time below the minimum representable time.
    312     ** Set tm_sec to 59 instead.
    313     ** This assumes that the minimum representable time is
    314     ** not in the same minute that a leap second was deleted from,
    315     ** which is a safer assumption than using 58 would be.
    316     */
    317     if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN))
    318       return WRONG;
    319     saved_seconds = yourtm.tm_sec;
    320     yourtm.tm_sec = SECSPERMIN - 1;
    321   } else {
    322     saved_seconds = yourtm.tm_sec;
    323     yourtm.tm_sec = 0;
    324   }
    325   /*
    326   ** Divide the search space in half
    327   ** (this works whether time_t is signed or unsigned).
    328   */
    329   bits = TYPE_BIT(time_t) - 1;
    330   /*
    331   ** Set t to the midpoint of our binary search.
    332   **
    333   ** If time_t is signed, then 0 is just above the median,
    334   ** assuming two's complement arithmetic.
    335   ** If time_t is unsigned, then (1 << bits) is just above the median.
    336   */
    337   t = TYPE_SIGNED(time_t) ? 0 : (((time_t) 1) << bits);
    338   for ( ; ; ) {
    339     (*funcp)(&t, offset, &mytm);    // Convert t to broken-down time in mytm
    340     dir = tmcomp(&mytm, &yourtm);   // Is mytm larger, equal, or less than yourtm?
    341     if (dir != 0) {                 // If mytm != yourtm...
    342       if (bits-- < 0)                   // If we have exhausted all the bits..
    343         return WRONG;                       // Return that we failed
    344       if (bits < 0)                     // If on the last bit...
    345         --t; /* may be needed if new t is minimal */
    346       else if (dir > 0)                 // else if mytm > yourtm...
    347         t -= ((time_t) 1) << bits;          // subtract half the remaining time-space
    348       else  t += ((time_t) 1) << bits;      // otherwise add half the remaining time-space
    349       continue;                     // Repeat for the next half
    350     }
    351     if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)
    352       break;
    353     /*
    354     ** Right time, wrong type.
    355     ** Hunt for right time, right type.
    356     ** It's okay to guess wrong since the guess
    357     ** gets checked.
    358     */
    359     /*
    360     ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
    361     */
    362     sp = (const struct state *)
    363       (((void *) funcp == (void *) localsub) ?
    364        lclptr : gmtptr);
    365 #ifdef ALL_STATE
    366     if (sp == NULL)
    367       return WRONG;
    368 #endif /* defined ALL_STATE */
    369     for (i = sp->typecnt - 1; i >= 0; --i) {
    370       if (sp->ttis[i].tt_isdst != yourtm.tm_isdst)
    371         continue;
    372       for (j = sp->typecnt - 1; j >= 0; --j) {
    373         if (sp->ttis[j].tt_isdst == yourtm.tm_isdst)
    374           continue;
    375         newt = t + sp->ttis[j].tt_gmtoff -
    376           sp->ttis[i].tt_gmtoff;
    377         (*funcp)(&newt, offset, &mytm);
    378         if (tmcomp(&mytm, &yourtm) != 0)
    379           continue;
    380         if (mytm.tm_isdst != yourtm.tm_isdst)
    381           continue;
    382         /*
    383         ** We have a match.
    384         */
    385         t = newt;
    386         goto label;
    387       }
    388     }
    389     return WRONG;
    390   }
    391   label:
    392   newt = t + saved_seconds;
    393   if ((newt < t) != (saved_seconds < 0))
    394     return WRONG;
    395   t = newt;
    396   (*funcp)(&t, offset, tmp);
    397   *okayp = TRUE;
    398   return t;
    399 }
    400 
    401 time_t
    402 time2(struct tm * const tmp, void (* const funcp)(const time_t*, long, struct tm*),
    403       const long offset, int * const okayp)
    404 {
    405   time_t  t;
    406 
    407   /*
    408   ** First try without normalization of seconds
    409   ** (in case tm_sec contains a value associated with a leap second).
    410   ** If that fails, try with normalization of seconds.
    411   */
    412   t = time2sub(tmp, funcp, offset, okayp, FALSE);
    413   return *okayp ? t : time2sub(tmp, funcp, offset, okayp, TRUE);
    414 }
    415 
    416 static time_t
    417 time1(
    418   struct tm * const tmp,
    419   void (* const funcp)(const time_t *, long, struct tm *),
    420   const long offset
    421   )
    422 {
    423   register time_t               t;
    424   register const struct state * sp;
    425   register int                  samei, otheri;
    426   register int                  sameind, otherind;
    427   register int                  i;
    428   register int                  nseen;
    429   int                           seen[TZ_MAX_TYPES];
    430   int                           types[TZ_MAX_TYPES];
    431   int                           okay;
    432 
    433   if (tmp->tm_isdst > 1)
    434     tmp->tm_isdst = 1;
    435   t = time2(tmp, funcp, offset, &okay);
    436 #ifdef PCTS
    437   /*
    438   ** PCTS code courtesy Grant Sullivan (grant (at) osf.org).
    439   */
    440   if (okay)
    441     return t;
    442   if (tmp->tm_isdst < 0)
    443     tmp->tm_isdst = 0;  /* reset to std and try again */
    444 #endif /* defined PCTS */
    445 #ifndef PCTS
    446   if (okay || tmp->tm_isdst < 0)
    447     return t;
    448 #endif /* !defined PCTS */
    449   /*
    450   ** We're supposed to assume that somebody took a time of one type
    451   ** and did some math on it that yielded a "struct tm" that's bad.
    452   ** We try to divine the type they started from and adjust to the
    453   ** type they need.
    454   */
    455   /*
    456   ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
    457   */
    458   sp = (const struct state *) (((void *) funcp == (void *) localsub) ?
    459                                lclptr : gmtptr);
    460 #ifdef ALL_STATE
    461   if (sp == NULL)
    462     return WRONG;
    463 #endif /* defined ALL_STATE */
    464   for (i = 0; i < sp->typecnt; ++i)
    465     seen[i] = FALSE;
    466   nseen = 0;
    467   for (i = sp->timecnt - 1; i >= 0; --i)
    468     if (!seen[sp->types[i]]) {
    469     seen[sp->types[i]] = TRUE;
    470     types[nseen++] = sp->types[i];
    471     }
    472     for (sameind = 0; sameind < nseen; ++sameind) {
    473       samei = types[sameind];
    474       if (sp->ttis[samei].tt_isdst != tmp->tm_isdst)
    475         continue;
    476       for (otherind = 0; otherind < nseen; ++otherind) {
    477         otheri = types[otherind];
    478         if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst)
    479           continue;
    480         tmp->tm_sec += (int)(sp->ttis[otheri].tt_gmtoff -
    481                              sp->ttis[samei].tt_gmtoff);
    482         tmp->tm_isdst = !tmp->tm_isdst;
    483         t = time2(tmp, funcp, offset, &okay);
    484         if (okay)
    485           return t;
    486         tmp->tm_sec -= (int)(sp->ttis[otheri].tt_gmtoff -
    487                              sp->ttis[samei].tt_gmtoff);
    488         tmp->tm_isdst = !tmp->tm_isdst;
    489       }
    490     }
    491     return WRONG;
    492 }
    493 
    494 /** The mktime function converts the broken-down time, expressed as local time,
    495     in the structure pointed to by timeptr into a calendar time value with the
    496     same encoding as that of the values returned by the time function.  The
    497     original values of the tm_wday and tm_yday components of the structure are
    498     ignored, and the original values of the other components are not restricted
    499     to the ranges indicated above.  Thus, a positive or zero value for tm_isdst
    500     causes the mktime function to presume initially that Daylight Saving Time,
    501     respectively, is or is not in effect for the specified time. A negative
    502     value causes it to attempt to determine whether Daylight Saving Time is in
    503     effect for the specified time.  On successful completion, the values of the
    504     tm_wday and tm_yday components of the structure are set appropriately, and
    505     the other components are set to represent the specified calendar time, but
    506     with their values forced to the ranges indicated above; the final value of
    507     tm_mday is not set until tm_mon and tm_year are determined.
    508 
    509     @return   The mktime function returns the specified calendar time encoded
    510               as a value of type time_t.  If the calendar time cannot be
    511               represented, the function returns the value (time_t)(-1).
    512 **/
    513 time_t
    514 mktime(struct tm *timeptr)
    515 {
    516   /* From NetBSD */
    517   time_t result;
    518 
    519   rwlock_wrlock(&lcl_lock);
    520   tzset();
    521   result = time1(timeptr, &localsub, 0L);
    522   rwlock_unlock(&lcl_lock);
    523   return (result);
    524 }
    525 
    526 /** The time function determines the current calendar time.  The encoding of
    527     the value is unspecified.
    528 
    529     @return   The time function returns the implementation's best approximation
    530               to the current calendar time.  The value (time_t)(-1) is returned
    531               if the calendar time is not available.  If timer is not a null
    532               pointer, the return value is also assigned to the object it
    533               points to.
    534 **/
    535 time_t
    536 time(time_t *timer)
    537 {
    538   time_t      CalTime;
    539   EFI_STATUS  Status;
    540   EFI_TIME   *ET;
    541   struct tm  *BT;
    542 
    543   ET = &gMD->TimeBuffer;
    544   BT = &gMD->BDTime;
    545 
    546   // Get EFI Time
    547   Status = gRT->GetTime( ET, NULL);
    548 //  Status = EfiGetTime( ET, NULL);
    549   EFIerrno = Status;
    550   if( Status != RETURN_SUCCESS) {
    551     return (time_t)-1;
    552   }
    553 
    554   // Convert EFI time to broken-down time.
    555   Efi2Tm( ET, BT);
    556 
    557   // Convert to time_t
    558   CalTime  =  mktime(&gMD->BDTime);
    559 
    560   if( timer != NULL) {
    561     *timer = CalTime;
    562   }
    563   return CalTime;   // Return calendar time in microseconds
    564 }
    565 
    566 /** The clock function determines the processor time used.
    567 
    568     @return   The clock function returns the implementation's best
    569               approximation to the processor time used by the program since the
    570               beginning of an implementation-defined era related only to the
    571               program invocation.  To determine the time in seconds, the value
    572               returned by the clock function should be divided by the value of
    573               the macro CLOCKS_PER_SEC.  If the processor time used is not
    574               available or its value cannot be represented, the function
    575               returns the value (clock_t)(-1).
    576 **/
    577 clock_t
    578 clock(void)
    579 {
    580   clock_t   retval;
    581   time_t    temp;
    582 
    583   temp = time(NULL);
    584   retval = ((clock_t)((UINT32)temp)) - gMD->AppStartTime;
    585   return retval;
    586 }
    587 
    588 /* #################  Time Conversion Functions  ########################## */
    589 /*
    590     Except for the strftime function, these functions each return a pointer to
    591     one of two types of static objects: a broken-down time structure or an
    592     array of char.  Execution of any of the functions that return a pointer to
    593     one of these object types may overwrite the information in any object of
    594     the same type pointed to by the value returned from any previous call to
    595     any of them.  The implementation shall behave as if no other library
    596     functions call these functions.
    597 */
    598 
    599 /** The asctime function converts the broken-down time in the structure pointed
    600     to by timeptr into a string in the form
    601       Sun Sep 16 01:03:52 1973\n\0
    602     using the equivalent of the following algorithm.
    603 
    604       char *asctime(const struct tm *timeptr)
    605       {
    606         static const char wday_name[7][3] = {
    607           "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
    608         };
    609         static const char mon_name[12][3] = {
    610           "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    611           "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
    612         };
    613         static char result[26];
    614         sprintf(result, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n",
    615                 wday_name[timeptr->tm_wday],
    616                 mon_name[timeptr->tm_mon],
    617                 timeptr->tm_mday, timeptr->tm_hour,
    618                 timeptr->tm_min, timeptr->tm_sec,
    619                 1900 + timeptr->tm_year);
    620         return result;
    621       }
    622     @return   The asctime function returns a pointer to the string.
    623 **/
    624 char *
    625 asctime(const struct tm *timeptr)
    626 {
    627   register const char * wn;
    628   register const char * mn;
    629 
    630   if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK)
    631     wn = "???";
    632   else  wn = wday_name[timeptr->tm_wday];
    633   if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR)
    634     mn = "???";
    635   else  mn = mon_name[timeptr->tm_mon];
    636   /*
    637   ** The X3J11-suggested format is
    638   **  "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %d\n"
    639   ** Since the .2 in 02.2d is ignored, we drop it.
    640   */
    641   (void)snprintf(gMD->ASasctime,
    642                  sizeof (char[ASCTIME_BUFLEN]),
    643                  "%.3s %.3s%3d %02d:%02d:%02d %d\r\n",    // explicit CRLF for EFI
    644                  wn, mn,
    645                  timeptr->tm_mday, timeptr->tm_hour,
    646                  timeptr->tm_min, timeptr->tm_sec,
    647                  TM_YEAR_BASE + timeptr->tm_year);
    648   return gMD->ASasctime;
    649 }
    650 
    651 /**
    652 **/
    653 char *
    654 ctime(const time_t *timer)
    655 {
    656   return asctime(localtime(timer));
    657 }
    658 
    659 /*
    660 ** gmtsub is to gmtime as localsub is to localtime.
    661 */
    662 void
    663 gmtsub(
    664   const time_t * const  timep,
    665   const long            offset,
    666   struct tm    * const  tmp
    667   )
    668 {
    669 #ifdef _REENTRANT
    670   static mutex_t gmt_mutex = MUTEX_INITIALIZER;
    671 #endif
    672 
    673   mutex_lock(&gmt_mutex);
    674   if (!gmt_is_set) {
    675     gmt_is_set = TRUE;
    676 #ifdef ALL_STATE
    677     gmtptr = (struct state *) malloc(sizeof *gmtptr);
    678     if (gmtptr != NULL)
    679 #endif /* defined ALL_STATE */
    680       gmtload(gmtptr);
    681   }
    682   mutex_unlock(&gmt_mutex);
    683   timesub(timep, offset, gmtptr, tmp);
    684 #ifdef TM_ZONE
    685   /*
    686   ** Could get fancy here and deliver something such as
    687   ** "UTC+xxxx" or "UTC-xxxx" if offset is non-zero,
    688   ** but this is no time for a treasure hunt.
    689   */
    690   if (offset != 0)
    691     tmp->TM_ZONE = (__aconst char *)__UNCONST(wildabbr);
    692   else {
    693 #ifdef ALL_STATE
    694     if (gmtptr == NULL)
    695       tmp->TM_ZONE = (__aconst char *)__UNCONST(gmt);
    696     else  tmp->TM_ZONE = gmtptr->chars;
    697 #endif /* defined ALL_STATE */
    698 #ifndef ALL_STATE
    699     tmp->TM_ZONE = gmtptr->chars;
    700 #endif /* State Farm */
    701   }
    702 #endif /* defined TM_ZONE */
    703 }
    704 
    705 /**
    706 **/
    707 struct tm *
    708 gmtime(const time_t *timer)
    709 {
    710   gmtsub(timer, 0L, &gMD->BDTime);
    711   return &gMD->BDTime;
    712 }
    713 
    714 static void
    715 localsub(const time_t * const timep, const long   offset, struct tm * const tmp)
    716 {
    717   register struct state *   sp;
    718   register const struct ttinfo *  ttisp;
    719   register int      i;
    720   const time_t      t = *timep;
    721 
    722   sp = lclptr;
    723 #ifdef ALL_STATE
    724   if (sp == NULL) {
    725     gmtsub(timep, offset, tmp);
    726     return;
    727   }
    728 #endif /* defined ALL_STATE */
    729   if (sp->timecnt == 0 || t < sp->ats[0]) {
    730     i = 0;
    731     while (sp->ttis[i].tt_isdst)
    732       if (++i >= sp->typecnt) {
    733         i = 0;
    734         break;
    735       }
    736   } else {
    737     for (i = 1; i < sp->timecnt; ++i)
    738       if (t < sp->ats[i])
    739       break;
    740     i = sp->types[i - 1];
    741   }
    742   ttisp = &sp->ttis[i];
    743   /*
    744   ** To get (wrong) behavior that's compatible with System V Release 2.0
    745   ** you'd replace the statement below with
    746   **  t += ttisp->tt_gmtoff;
    747   **  timesub(&t, 0L, sp, tmp);
    748   */
    749   timesub(&t, ttisp->tt_gmtoff, sp, tmp);
    750   tmp->tm_isdst = ttisp->tt_isdst;
    751   tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind];
    752 #ifdef TM_ZONE
    753   tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind];
    754 #endif /* defined TM_ZONE */
    755 }
    756 
    757 /**
    758 **/
    759 struct tm *
    760 localtime(const time_t *timer)
    761 {
    762   tzset();
    763   localsub(timer, 0L, &gMD->BDTime);
    764   return &gMD->BDTime;
    765 }
    766