/**
  Definitions and Implementation for <time.h>.

  Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>
  This program and the accompanying materials are licensed and made available under
  the terms and conditions of the BSD License that accompanies this distribution.
  The full text of the license may be found at
  http://opensource.org/licenses/bsd-license.php.

  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

  Portions derived from the NIH time zone package file, localtime.c,
  which contains the following notice:

    This file is in the public domain, so clarified as of
    1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).

  NetBSD: localtime.c,v 1.39 2006/03/22 14:01:30 christos Exp
**/
#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/TimerLib.h>
#include  <Library/BaseLib.h>
#include  <Library/UefiRuntimeServicesTableLib.h>
//#include  <Library/UefiRuntimeLib.h>

#include  <LibConfig.h>

#include  <errno.h>
#include  <limits.h>
#include  <time.h>
#include  <reentrant.h>
#include  "tzfile.h"
#include  "TimeVals.h"
#include  <MainData.h>
#include  <extern.h>      // Library/include/extern.h: Private to implementation

#if defined(_MSC_VER)           /* Handle Microsoft VC++ compiler specifics. */
// Keep compiler quiet about casting from function to data pointers
#pragma warning ( disable : 4054 )
#endif  /* defined(_MSC_VER) */

/* #######################  Private Data  ################################# */

#if 0
static EFI_TIME TimeBuffer;

  static  UINT16   MonthOffs[12] = {
     00,
     31,   59,   90,  120,
    151,  181,  212,  243,
    273,  304,  334
  };
  static  clock_t   y2kOffs = 730485;
#endif

const int  mon_lengths[2][MONSPERYEAR] = {
  { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
  { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
};

const int  year_lengths[2] = {
  DAYSPERNYEAR, DAYSPERLYEAR
};


static const char *wday_name[7] = {
  "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};

static const char *mon_name[12] = {
  "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};

static int    gmt_is_set;

/* ###############  Implementation Functions  ############################ */
// Forward reference
static void
localsub(const time_t * const timep, const long   offset, struct tm * const tmp);

clock_t
EFIAPI
__getCPS(void)
{
  return gMD->ClocksPerSecond;
}

static void
timesub(
  const time_t        * const timep,
  const long                  offset,
  const struct state  * const sp,
        struct tm     * const tmp
  )
{
  const struct lsinfo *  lp;
  time_t /*INTN*/     days;
  time_t /*INTN*/     rem;
  time_t /*INTN*/      y;
  int      yleap;
  const int *    ip;
  time_t /*INTN*/     corr;
  int      hit;
  int      i;

  corr = 0;
  hit = 0;
#ifdef ALL_STATE
  i = (sp == NULL) ? 0 : sp->leapcnt;
#endif /* defined ALL_STATE */
#ifndef ALL_STATE
  i = sp->leapcnt;
#endif /* State Farm */
  while (--i >= 0) {
    lp = &sp->lsis[i];
    if (*timep >= lp->ls_trans) {
      if (*timep == lp->ls_trans) {
        hit = ((i == 0 && lp->ls_corr > 0) ||
               lp->ls_corr > sp->lsis[i - 1].ls_corr);
        if (hit)
          while (i > 0                                                &&
                 sp->lsis[i].ls_trans == sp->lsis[i - 1].ls_trans + 1 &&
                 sp->lsis[i].ls_corr  == sp->lsis[i - 1].ls_corr  + 1 )
          {
            ++hit;
            --i;
          }
      }
      corr = lp->ls_corr;
      break;
    }
  }
  days = *timep / SECSPERDAY;
  rem = *timep % SECSPERDAY;
  rem += (offset - corr);
  while (rem < 0) {
    rem += SECSPERDAY;
    --days;
  }
  while (rem >= SECSPERDAY) {
    rem -= SECSPERDAY;
    ++days;
  }
  tmp->tm_hour = (int) (rem / SECSPERHOUR);
  rem = rem % SECSPERHOUR;
  tmp->tm_min = (int) (rem / SECSPERMIN);
  /*
  ** A positive leap second requires a special
  ** representation.  This uses "... ??:59:60" et seq.
  */
  tmp->tm_sec = (int) (rem % SECSPERMIN) + hit;
  tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK);
  if (tmp->tm_wday < 0)
    tmp->tm_wday += DAYSPERWEEK;
  y = EPOCH_YEAR;
  while (days < 0 || days >= (LONG32) year_lengths[yleap = isleap(y)]) {
    time_t /*INTN*/  newy;

    newy = (y + days / DAYSPERNYEAR);
    if (days < 0)
      --newy;
    days -= (newy - y) * DAYSPERNYEAR +
      LEAPS_THRU_END_OF(newy - 1) -
      LEAPS_THRU_END_OF(y - 1);
    y = newy;
  }
  tmp->tm_year = (int)(y - TM_YEAR_BASE);
  tmp->tm_yday = (int) days;
  ip = mon_lengths[yleap];
  for (tmp->tm_mon = 0; days >= (LONG32) ip[tmp->tm_mon]; ++(tmp->tm_mon))
    days = days - (LONG32) ip[tmp->tm_mon];
  tmp->tm_mday = (int) (days + 1);
  tmp->tm_isdst = 0;
#ifdef TM_GMTOFF
  tmp->TM_GMTOFF = offset;
#endif /* defined TM_GMTOFF */
}

/* ###############  Time Manipulation Functions  ########################## */

/** The clock function determines the processor time used.

    @return   The clock function returns the implementation’s best
              approximation to the processor time used by the program since the
              beginning of an implementation-defined era related only to the
              program invocation.  To determine the time in seconds, the value
              returned by the clock function should be divided by the value of
              the macro CLOCKS_PER_SEC.  If the processor time used is not
              available or its value cannot be represented, the function
              returns the value (clock_t)(-1).

              On IA32 or X64 platforms, the value returned is the number of
              CPU TimeStamp Counter ticks since the appliation started.
**/
clock_t
EFIAPI
clock(void)
{
#ifndef NT32dvm
  clock_t   temp;

  temp = (clock_t)GetPerformanceCounter();

  return temp - gMD->AppStartTime;
#else
  return (clock_t)-1;
#endif  /* NT32dvm */
}

/**
**/
double
EFIAPI
difftime(time_t time1, time_t time0)
{
  return (double)(time1 - time0);
}

/*
** Adapted from code provided by Robert Elz, who writes:
**  The "best" way to do mktime I think is based on an idea of Bob
**  Kridle's (so its said...) from a long time ago.
**  [kridle@xinet.com as of 1996-01-16.]
**  It does a binary search of the time_t space.  Since time_t's are
**  just 32 bits, its a max of 32 iterations (even at 64 bits it
**  would still be very reasonable).
*/

#ifndef WRONG
#define WRONG (-1)
#endif /* !defined WRONG */

/*
** Simplified normalize logic courtesy Paul Eggert (eggert@twinsun.com).
*/

static int
increment_overflow(int * number, int delta)
{
  int number0;

  number0 = *number;
  *number += delta;
  return (*number < number0) != (delta < 0);
}

static int
normalize_overflow(int * const tensptr, int * const unitsptr, const int base)
{
  register int  tensdelta;

  tensdelta = (*unitsptr >= 0)  ?
              (*unitsptr / base) : (-1 - (-1 - *unitsptr) / base);
  *unitsptr -= tensdelta * base;
  return increment_overflow(tensptr, tensdelta);
}

static int
tmcomp(const struct tm * const atmp, const struct tm * const btmp)
{
  register int  result;

  if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&
      (result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
      (result = (atmp->tm_mday - btmp->tm_mday)) == 0 &&
      (result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
      (result = (atmp->tm_min - btmp->tm_min)) == 0)
    result = atmp->tm_sec - btmp->tm_sec;
  return result;
}

static time_t
time2sub(
  struct tm * const tmp,
  void (* const funcp)(const time_t*, long, struct tm*),
  const long offset,
  int * const okayp,
  const int do_norm_secs
  )
{
  register const struct state * sp;
  register int                  dir;
  register int                  bits;
  register int                  i, j ;
  register int                  saved_seconds;
  time_t                        newt;
  time_t                        t;
  struct tm                     yourtm, mytm;

  *okayp = FALSE;
  yourtm = *tmp;    // Create a copy of tmp
  if (do_norm_secs) {
    if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec,
                           SECSPERMIN))
      return WRONG;
  }
  if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR))
    return WRONG;
  if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
    return WRONG;
  if (normalize_overflow(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR))
    return WRONG;
  /*
  ** Turn yourtm.tm_year into an actual year number for now.
  ** It is converted back to an offset from TM_YEAR_BASE later.
  */
  if (increment_overflow(&yourtm.tm_year, TM_YEAR_BASE))
    return WRONG;
  while (yourtm.tm_mday <= 0) {
    if (increment_overflow(&yourtm.tm_year, -1))
      return WRONG;
    i = yourtm.tm_year + (1 < yourtm.tm_mon);
    yourtm.tm_mday += year_lengths[isleap(i)];
  }
  while (yourtm.tm_mday > DAYSPERLYEAR) {
    i = yourtm.tm_year + (1 < yourtm.tm_mon);
    yourtm.tm_mday -= year_lengths[isleap(i)];
    if (increment_overflow(&yourtm.tm_year, 1))
      return WRONG;
  }
  for ( ; ; ) {
    i = mon_lengths[isleap(yourtm.tm_year)][yourtm.tm_mon];
    if (yourtm.tm_mday <= i)
      break;
    yourtm.tm_mday -= i;
    if (++yourtm.tm_mon >= MONSPERYEAR) {
      yourtm.tm_mon = 0;
      if (increment_overflow(&yourtm.tm_year, 1))
        return WRONG;
    }
  }
  if (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE))
    return WRONG;
  if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN)
    saved_seconds = 0;
  else if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) {
    /*
    ** We can't set tm_sec to 0, because that might push the
    ** time below the minimum representable time.
    ** Set tm_sec to 59 instead.
    ** This assumes that the minimum representable time is
    ** not in the same minute that a leap second was deleted from,
    ** which is a safer assumption than using 58 would be.
    */
    if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN))
      return WRONG;
    saved_seconds = yourtm.tm_sec;
    yourtm.tm_sec = SECSPERMIN - 1;
  } else {
    saved_seconds = yourtm.tm_sec;
    yourtm.tm_sec = 0;
  }
  /*
  ** Divide the search space in half
  ** (this works whether time_t is signed or unsigned).
  */
  bits = TYPE_BIT(time_t) - 1;
  /*
  ** Set t to the midpoint of our binary search.
  **
  ** If time_t is signed, then 0 is just above the median,
  ** assuming two's complement arithmetic.
  ** If time_t is unsigned, then (1 << bits) is just above the median.
  */
  t = TYPE_SIGNED(time_t) ? 0 : (((time_t) 1) << bits);
  for ( ; ; ) {
    (*funcp)(&t, offset, &mytm);    // Convert t to broken-down time in mytm
    dir = tmcomp(&mytm, &yourtm);   // Is mytm larger, equal, or less than yourtm?
    if (dir != 0) {                 // If mytm != yourtm...
      if (bits-- < 0)                   // If we have exhausted all the bits..
        return WRONG;                       // Return that we failed
      if (bits < 0)                     // If on the last bit...
        --t; /* may be needed if new t is minimal */
      else if (dir > 0)                 // else if mytm > yourtm...
        t -= ((time_t) 1) << bits;          // subtract half the remaining time-space
      else  t += ((time_t) 1) << bits;      // otherwise add half the remaining time-space
      continue;                     // Repeat for the next half
    }
    if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)
      break;
    /*
    ** Right time, wrong type.
    ** Hunt for right time, right type.
    ** It's okay to guess wrong since the guess
    ** gets checked.
    */
    /*
    ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
    */
    sp = (const struct state *)
      (((void *) funcp == (void *) localsub) ?
       lclptr : gmtptr);
#ifdef ALL_STATE
    if (sp == NULL)
      return WRONG;
#endif /* defined ALL_STATE */
    for (i = sp->typecnt - 1; i >= 0; --i) {
      if (sp->ttis[i].tt_isdst != yourtm.tm_isdst)
        continue;
      for (j = sp->typecnt - 1; j >= 0; --j) {
        if (sp->ttis[j].tt_isdst == yourtm.tm_isdst)
          continue;
        newt = t + sp->ttis[j].tt_gmtoff -
          sp->ttis[i].tt_gmtoff;
        (*funcp)(&newt, offset, &mytm);
        if (tmcomp(&mytm, &yourtm) != 0)
          continue;
        if (mytm.tm_isdst != yourtm.tm_isdst)
          continue;
        /*
        ** We have a match.
        */
        t = newt;
        goto label;
      }
    }
    return WRONG;
  }
  label:
  newt = t + saved_seconds;
  if ((newt < t) != (saved_seconds < 0))
    return WRONG;
  t = newt;
  (*funcp)(&t, offset, tmp);
  *okayp = TRUE;
  return t;
}

static time_t
time2(struct tm * const tmp, void (* const funcp)(const time_t*, long, struct tm*),
      const long offset, int * const okayp)
{
  time_t  t;

  /*
  ** First try without normalization of seconds
  ** (in case tm_sec contains a value associated with a leap second).
  ** If that fails, try with normalization of seconds.
  */
  t = time2sub(tmp, funcp, offset, okayp, FALSE);
  return *okayp ? t : time2sub(tmp, funcp, offset, okayp, TRUE);
}

static time_t
time1(
  struct tm * const tmp,
  void (* const funcp)(const time_t *, long, struct tm *),
  const long offset
  )
{
  register time_t               t;
  register const struct state * sp;
  register int                  samei, otheri;
  register int                  sameind, otherind;
  register int                  i;
  register int                  nseen;
  int                           seen[TZ_MAX_TYPES];
  int                           types[TZ_MAX_TYPES];
  int                           okay;

  if (tmp->tm_isdst > 1)
    tmp->tm_isdst = 1;
  t = time2(tmp, funcp, offset, &okay);
#ifdef PCTS
  /*
  ** PCTS code courtesy Grant Sullivan (grant@osf.org).
  */
  if (okay)
    return t;
  if (tmp->tm_isdst < 0)
    tmp->tm_isdst = 0;  /* reset to std and try again */
#endif /* defined PCTS */
#ifndef PCTS
  if (okay || tmp->tm_isdst < 0)
    return t;
#endif /* !defined PCTS */
  /*
  ** We're supposed to assume that somebody took a time of one type
  ** and did some math on it that yielded a "struct tm" that's bad.
  ** We try to divine the type they started from and adjust to the
  ** type they need.
  */
  /*
  ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
  */
  sp = (const struct state *) (((void *) funcp == (void *) localsub) ?
                               lclptr : gmtptr);
#ifdef ALL_STATE
  if (sp == NULL)
    return WRONG;
#endif /* defined ALL_STATE */
  for (i = 0; i < sp->typecnt; ++i)
    seen[i] = FALSE;
  nseen = 0;
  for (i = sp->timecnt - 1; i >= 0; --i)
    if (!seen[sp->types[i]]) {
    seen[sp->types[i]] = TRUE;
    types[nseen++] = sp->types[i];
    }
    for (sameind = 0; sameind < nseen; ++sameind) {
      samei = types[sameind];
      if (sp->ttis[samei].tt_isdst != tmp->tm_isdst)
        continue;
      for (otherind = 0; otherind < nseen; ++otherind) {
        otheri = types[otherind];
        if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst)
          continue;
        tmp->tm_sec += (int)(sp->ttis[otheri].tt_gmtoff -
                             sp->ttis[samei].tt_gmtoff);
        tmp->tm_isdst = !tmp->tm_isdst;
        t = time2(tmp, funcp, offset, &okay);
        if (okay)
          return t;
        tmp->tm_sec -= (int)(sp->ttis[otheri].tt_gmtoff -
                             sp->ttis[samei].tt_gmtoff);
        tmp->tm_isdst = !tmp->tm_isdst;
      }
    }
    return WRONG;
}

/** The mktime function converts the broken-down time, expressed as local time,
    in the structure pointed to by timeptr into a calendar time value with the
    same encoding as that of the values returned by the time function.  The
    original values of the tm_wday and tm_yday components of the structure are
    ignored, and the original values of the other components are not restricted
    to the ranges indicated above.  Thus, a positive or zero value for tm_isdst
    causes the mktime function to presume initially that Daylight Saving Time,
    respectively, is or is not in effect for the specified time. A negative
    value causes it to attempt to determine whether Daylight Saving Time is in
    effect for the specified time.  On successful completion, the values of the
    tm_wday and tm_yday components of the structure are set appropriately, and
    the other components are set to represent the specified calendar time, but
    with their values forced to the ranges indicated above; the final value of
    tm_mday is not set until tm_mon and tm_year are determined.

    @return   The mktime function returns the specified calendar time encoded
              as a value of type time_t.  If the calendar time cannot be
              represented, the function returns the value (time_t)(-1).
**/
time_t
EFIAPI
mktime(struct tm *timeptr)
{
  /* From NetBSD */
  time_t result;

  rwlock_wrlock(&lcl_lock);
  tzset();
  result = time1(timeptr, &localsub, 0L);
  rwlock_unlock(&lcl_lock);
  return (result);
}

/** The time function determines the current calendar time.  The encoding of
    the value is unspecified.

    @return   The time function returns the implementation’s best approximation
              to the current calendar time.  The value (time_t)(-1) is returned
              if the calendar time is not available.  If timer is not a null
              pointer, the return value is also assigned to the object it
              points to.
**/
time_t
EFIAPI
time(time_t *timer)
{
  time_t      CalTime;
  EFI_STATUS  Status;
  EFI_TIME   *ET;
  struct tm  *BT;

  ET = &gMD->TimeBuffer;
  BT = &gMD->BDTime;

  // Get EFI Time
  Status = gRT->GetTime( ET, NULL);
//  Status = EfiGetTime( ET, NULL);
  EFIerrno = Status;
  if( Status != RETURN_SUCCESS) {
    return (time_t)-1;
  }

  // Convert EFI time to broken-down time.
  Efi2Tm( ET, BT);

  // Convert to time_t
  CalTime  =  mktime(&gMD->BDTime);

  if( timer != NULL) {
    *timer = CalTime;
  }
  return CalTime;   // Return calendar time in microseconds
}

/* #################  Time Conversion Functions  ########################## */
/*
    Except for the strftime function, these functions each return a pointer to
    one of two types of static objects: a broken-down time structure or an
    array of char.  Execution of any of the functions that return a pointer to
    one of these object types may overwrite the information in any object of
    the same type pointed to by the value returned from any previous call to
    any of them.  The implementation shall behave as if no other library
    functions call these functions.
*/

/** The asctime function converts the broken-down time in the structure pointed
    to by timeptr into a string in the form
      Sun Sep 16 01:03:52 1973\n\0
    using the equivalent of the following algorithm.

      char *asctime(const struct tm *timeptr)
      {
        static const char wday_name[7][3] = {
          "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
        };
        static const char mon_name[12][3] = {
          "Jan", "Feb", "Mar", "Apr", "May", "Jun",
          "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
        };
        static char result[26];
        sprintf(result, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n",
                wday_name[timeptr->tm_wday],
                mon_name[timeptr->tm_mon],
                timeptr->tm_mday, timeptr->tm_hour,
                timeptr->tm_min, timeptr->tm_sec,
                1900 + timeptr->tm_year);
        return result;
      }
    @return   The asctime function returns a pointer to the string.
**/
char *
EFIAPI
asctime(const struct tm *timeptr)
{
  register const char * wn;
  register const char * mn;

  if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK)
    wn = "???";
  else  wn = wday_name[timeptr->tm_wday];
  if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR)
    mn = "???";
  else  mn = mon_name[timeptr->tm_mon];
  /*
  ** The X3J11-suggested format is
  **  "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %d\n"
  ** Since the .2 in 02.2d is ignored, we drop it.
  */
  (void)snprintf(gMD->ASasctime,
                 sizeof (char[ASCTIME_BUFLEN]),
                 "%.3s %.3s%3d %02d:%02d:%02d %d\r\n",    // explicit CRLF for EFI
                 wn, mn,
                 timeptr->tm_mday, timeptr->tm_hour,
                 timeptr->tm_min, timeptr->tm_sec,
                 TM_YEAR_BASE + timeptr->tm_year);
  return gMD->ASasctime;
}

/**
**/
char *
EFIAPI
ctime(const time_t *timer)
{
  return asctime(localtime(timer));
}

/*
** gmtsub is to gmtime as localsub is to localtime.
*/
static void
gmtsub(
  const time_t * const  timep,
  const long            offset,
  struct tm    * const  tmp
  )
{
#ifdef _REENTRANT
  static mutex_t gmt_mutex = MUTEX_INITIALIZER;
#endif

  mutex_lock(&gmt_mutex);
  if (!gmt_is_set) {
    gmt_is_set = TRUE;
#ifdef ALL_STATE
    gmtptr = (struct state *) malloc(sizeof *gmtptr);
    if (gmtptr != NULL)
#endif /* defined ALL_STATE */
      gmtload(gmtptr);
  }
  mutex_unlock(&gmt_mutex);
  timesub(timep, offset, gmtptr, tmp);
#ifdef TM_ZONE
  /*
  ** Could get fancy here and deliver something such as
  ** "UTC+xxxx" or "UTC-xxxx" if offset is non-zero,
  ** but this is no time for a treasure hunt.
  */
  if (offset != 0)
    tmp->TM_ZONE = (__aconst char *)__UNCONST(wildabbr);
  else {
#ifdef ALL_STATE
    if (gmtptr == NULL)
      tmp->TM_ZONE = (__aconst char *)__UNCONST(gmt);
    else  tmp->TM_ZONE = gmtptr->chars;
#endif /* defined ALL_STATE */
#ifndef ALL_STATE
    tmp->TM_ZONE = gmtptr->chars;
#endif /* State Farm */
  }
#endif /* defined TM_ZONE */
}

/**
**/
struct tm *
EFIAPI
gmtime(const time_t *timer)
{
  gmtsub(timer, 0L, &gMD->BDTime);
  return &gMD->BDTime;
}

static void
localsub(const time_t * const timep, const long   offset, struct tm * const tmp)
{
  register struct state *   sp;
  register const struct ttinfo *  ttisp;
  register int      i;
  const time_t      t = *timep;

  sp = lclptr;
#ifdef ALL_STATE
  if (sp == NULL) {
    gmtsub(timep, offset, tmp);
    return;
  }
#endif /* defined ALL_STATE */
  if (sp->timecnt == 0 || t < sp->ats[0]) {
    i = 0;
    while (sp->ttis[i].tt_isdst)
      if (++i >= sp->typecnt) {
        i = 0;
        break;
      }
  } else {
    for (i = 1; i < sp->timecnt; ++i)
      if (t < sp->ats[i])
      break;
    i = sp->types[i - 1];
  }
  ttisp = &sp->ttis[i];
  /*
  ** To get (wrong) behavior that's compatible with System V Release 2.0
  ** you'd replace the statement below with
  **  t += ttisp->tt_gmtoff;
  **  timesub(&t, 0L, sp, tmp);
  */
  timesub(&t, ttisp->tt_gmtoff, sp, tmp);
  tmp->tm_isdst = ttisp->tt_isdst;
  tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind];
#ifdef TM_ZONE
  tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind];
#endif /* defined TM_ZONE */
}

/**
**/
struct tm *
EFIAPI
localtime(const time_t *timer)
{
  tzset();
  localsub(timer, 0L, &gMD->BDTime);
  return &gMD->BDTime;
}
