summaryrefslogtreecommitdiff
path: root/StdLib/LibC/Time/Time.c
diff options
context:
space:
mode:
Diffstat (limited to 'StdLib/LibC/Time/Time.c')
-rw-r--r--StdLib/LibC/Time/Time.c780
1 files changed, 780 insertions, 0 deletions
diff --git a/StdLib/LibC/Time/Time.c b/StdLib/LibC/Time/Time.c
new file mode 100644
index 0000000000..3192696f4a
--- /dev/null
+++ b/StdLib/LibC/Time/Time.c
@@ -0,0 +1,780 @@
+/**
+ Definitions and Implementation for <time.h>.
+
+ Copyright (c) 2010, 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)
+{
+ clock_t temp;
+
+#ifdef NT32dvm
+ temp = 0;
+#else
+ temp = (clock_t)GetPerformanceCounter();
+#endif /* NT32dvm */
+
+ return temp - gMD->AppStartTime;
+}
+
+/**
+**/
+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;
+}