summaryrefslogtreecommitdiffstats
path: root/private/crt32/time/mktime.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/crt32/time/mktime.c')
-rw-r--r--private/crt32/time/mktime.c315
1 files changed, 315 insertions, 0 deletions
diff --git a/private/crt32/time/mktime.c b/private/crt32/time/mktime.c
new file mode 100644
index 000000000..7eb6e4e11
--- /dev/null
+++ b/private/crt32/time/mktime.c
@@ -0,0 +1,315 @@
+/***
+*mktime.c - Convert struct tm value to time_t value.
+*
+* Copyright (c) 1987-1993, Microsoft Corporation. All rights reserved.
+*
+*Purpose:
+* Defines mktime() and _mkgmtime(), routines to converts a time value
+* in a tm structure (possibly incomplete) into a time_t value, then
+* update (all) the structure fields with "normalized" values.
+*
+*Revision History:
+* 01-14-87 JCR Module created
+* 12-11-87 JCR Added "_LOAD_DS" to declaration
+* 06-15-89 PHG Now allows negative values and does DST by ANSI rules
+* 11-06-89 KRS Added (unsigned) to handle years 2040-2099 correctly.
+* 03-20-90 GJF Replaced _LOAD_DS with _CALLTYPE1, added #include
+* <cruntime.h> and fixed the copyright. Also, cleaned
+* up the formatting a bit.
+* 10-04-90 GJF New-style function declarator. Also, rewrote expr.
+* to avoid using casts as lvalues.
+* 10-26-90 GJF Added ulscount to avoid overflows. Ugly, temporary
+* hack (whole function needs to be revised for ANSI
+* conformance).
+* 01-22-91 GJF ANSI naming.
+* 03-24-93 GJF Propagated changes from 16-bit tree. Modified to
+* expose _mkgmtime() routine.
+* 07-15-93 GJF Replaced _tzset() call with __tzset() call.
+*
+*******************************************************************************/
+
+#include <cruntime.h>
+#include <stddef.h>
+#include <ctime.h>
+#include <time.h>
+#include <internal.h>
+
+/*
+ * ChkAdd evaluates to TRUE if dest = src1 + src2 has overflowed
+ */
+#define ChkAdd(dest, src1, src2) ( ((src1 >= 0L) && (src2 >= 0L) \
+ && (dest < 0L)) || ((src1 < 0L) && (src2 < 0L) && (dest >= 0L)) )
+
+/*
+ * ChkMul evaluates to TRUE if dest = src1 * src2 has overflowed
+ */
+#define ChkMul(dest, src1, src2) ( src1 ? (dest/src1 != src2) : 0 )
+
+
+/*
+ * Core function for both mktime() and _mkgmtime()
+ */
+static time_t _CRTAPI3 _make_time_t( struct tm *, int);
+
+
+/***
+*time_t mktime(tb) - Normalize user time block structure
+*
+*Purpose:
+* Mktime converts a time structure, passed in as an argument, into a
+* calendar time value in internal format (time_t). It also completes
+* and updates the fields the of the passed in structure with 'normalized'
+* values. There are three practical uses for this routine:
+*
+* (1) Convert broken-down time to internal time format (time_t).
+* (2) To have mktime fill in the tm_wday, tm_yday, or tm_isdst fields.
+* (3) To pass in a time structure with 'out of range' values for some
+* fields and have mktime "normalize" them (e.g., pass in 1/35/87 and
+* get back 2/4/87).
+*Entry:
+* struct tm *tb - pointer to a tm time structure to convert and
+* normalize
+*
+*Exit:
+* If successful, mktime returns the specified calender time encoded as
+* a time_t value. Otherwise, (time_t)(-1) is returned to indicate an
+* error.
+*
+*Exceptions:
+* None.
+*
+*******************************************************************************/
+
+
+time_t _CRTAPI1 mktime (
+ struct tm *tb
+ )
+{
+ return( _make_time_t(tb, 1) );
+}
+
+
+/***
+*time_t _mkgmtime(tb) - Convert broken down UTC time to time_t
+*
+*Purpose:
+* Convert a tm structure, passed in as an argument, containing a UTC
+* time value to internal format (time_t). It also completes and updates
+* the fields the of the passed in structure with 'normalized' values.
+
+*Entry:
+* struct tm *tb - pointer to a tm time structure to convert and
+* normalize
+*
+*Exit:
+* If successful, _mkgmtime returns the calender time encoded as time_t
+* Otherwise, (time_t)(-1) is returned to indicate an error.
+*
+*Exceptions:
+* None.
+*
+*******************************************************************************/
+
+time_t _CRTAPI1 _mkgmtime (
+ struct tm *tb
+ )
+{
+ return( _make_time_t(tb, 0) );
+}
+
+
+/***
+*static time_t make_time_t(tb, ultflag) -
+*
+*Purpose:
+* Converts a struct tm value to a time_t value, then updates the struct
+* tm value. Either local time or UTC is supported, based on ultflag.
+* This is the routine that actually does the work for both mktime() and
+* _mkgmtime().
+*
+*Entry:
+* struct tm *tb - pointer to a tm time structure to convert and
+* normalize
+* int ultflag - use local time flag. the tb structure is assumed
+* to represent a local date/time if ultflag > 0.
+* otherwise, UTC is assumed.
+*
+*Exit:
+* If successful, mktime returns the specified calender time encoded as
+* a time_t value. Otherwise, (time_t)(-1) is returned to indicate an
+* error.
+*
+*Exceptions:
+* None.
+*
+*******************************************************************************/
+
+static time_t _CRTAPI3 _make_time_t (
+ struct tm *tb,
+ int ultflag
+ )
+{
+ long tmptm1, tmptm2, tmptm3;
+ struct tm *tbtemp;
+
+ /*
+ * First, make sure tm_year is reasonably close to being in range.
+ */
+ if ( ((tmptm1 = tb->tm_year) < _BASE_YEAR - 1) || (tmptm1 > _MAX_YEAR
+ + 1) )
+ goto err_mktime;
+
+
+ /*
+ * Adjust month value so it is in the range 0 - 11. This is because
+ * we don't know how many days are in months 12, 13, 14, etc.
+ */
+
+ if ( (tmptm2 = tb->tm_mon/12) != 0L ) {
+ /*
+ * no danger of overflow because the range check above.
+ */
+ tmptm1 += tmptm2;
+
+ if ( (tb->tm_mon %= 12) < 0 ) {
+ tb->tm_mon += 12;
+ tmptm1--;
+ }
+
+ /*
+ * Make sure year count is still in range.
+ */
+ if ( (tmptm1 < _BASE_YEAR - 1) || (tmptm1 > _MAX_YEAR + 1) )
+ goto err_mktime;
+ }
+
+ /***** HERE: tmptm1 holds number of elapsed years *****/
+
+ /*
+ * Calculate days elapsed minus one, in the given year, to the given
+ * month. Check for leap year and adjust if necessary.
+ */
+ tmptm2 = _days[tb->tm_mon];
+ if ( !(tmptm1 & 3) && (tb->tm_mon > 1) )
+ tmptm2++;
+
+ /*
+ * Calculate elapsed days since base date (midnight, 1/1/70, UTC)
+ *
+ *
+ * 365 days for each elapsed year since 1970, plus one more day for
+ * each elapsed leap year. no danger of overflow because of the range
+ * check (above) on tmptm1.
+ */
+ tmptm3 = (tmptm1 - _BASE_YEAR) * 365L + ((tmptm1 - 1L) >> 2)
+ - _LEAP_YEAR_ADJUST;
+
+ /*
+ * elapsed days to current month (still no possible overflow)
+ */
+ tmptm3 += tmptm2;
+
+ /*
+ * elapsed days to current date. overflow is now possible.
+ */
+ tmptm1 = tmptm3 + (tmptm2 = (long)(tb->tm_mday));
+ if ( ChkAdd(tmptm1, tmptm3, tmptm2) )
+ goto err_mktime;
+
+ /***** HERE: tmptm1 holds number of elapsed days *****/
+
+ /*
+ * Calculate elapsed hours since base date
+ */
+ tmptm2 = tmptm1 * 24L;
+ if ( ChkMul(tmptm2, tmptm1, 24L) )
+ goto err_mktime;
+
+ tmptm1 = tmptm2 + (tmptm3 = (long)tb->tm_hour);
+ if ( ChkAdd(tmptm1, tmptm2, tmptm3) )
+ goto err_mktime;
+
+ /***** HERE: tmptm1 holds number of elapsed hours *****/
+
+ /*
+ * Calculate elapsed minutes since base date
+ */
+
+ tmptm2 = tmptm1 * 60L;
+ if ( ChkMul(tmptm2, tmptm1, 60L) )
+ goto err_mktime;
+
+ tmptm1 = tmptm2 + (tmptm3 = (long)tb->tm_min);
+ if ( ChkAdd(tmptm1, tmptm2, tmptm3) )
+ goto err_mktime;
+
+ /***** HERE: tmptm1 holds number of elapsed minutes *****/
+
+ /*
+ * Calculate elapsed seconds since base date
+ */
+
+ tmptm2 = tmptm1 * 60L;
+ if ( ChkMul(tmptm2, tmptm1, 60L) )
+ goto err_mktime;
+
+ tmptm1 = tmptm2 + (tmptm3 = (long)tb->tm_sec);
+ if ( ChkAdd(tmptm1, tmptm2, tmptm3) )
+ goto err_mktime;
+
+ /***** HERE: tmptm1 holds number of elapsed seconds *****/
+
+ if ( ultflag ) {
+ /*
+ * Adjust for timezone. No need to check for overflow since
+ * localtime() will check its arg value
+ */
+
+#ifdef _POSIX_
+ tzset();
+#else
+ __tzset();
+#endif
+ tmptm1 += _timezone;
+
+ /*
+ * Convert this second count back into a time block structure.
+ * If localtime returns NULL, return an error.
+ */
+ if ( (tbtemp = localtime(&tmptm1)) == NULL )
+ goto err_mktime;
+
+ /*
+ * Now must compensate for DST. The ANSI rules are to use the
+ * passed-in tm_isdst flag if it is non-negative. Otherwise,
+ * compute if DST applies. Recall that tbtemp has the time without
+ * DST compensation, but has set tm_isdst correctly.
+ */
+ if ( (tb->tm_isdst > 0) || ((tb->tm_isdst < 0) &&
+ (tbtemp->tm_isdst > 0)) ) {
+#ifdef _POSIX_
+ tmptm1 -= _timezone;
+ tmptm1 += _dstoffset;
+#else
+ tmptm1 -= 3600L;
+#endif
+ tbtemp = localtime(&tmptm1); /* reconvert, can't get NULL */
+ }
+ }
+ else {
+ if ( (tbtemp = gmtime(&tmptm1)) == NULL )
+ goto err_mktime;
+ }
+
+ /***** HERE: tmptm1 holds number of elapsed seconds, adjusted *****/
+ /***** for local time if requested *****/
+
+ *tb = *tbtemp;
+ return (time_t)tmptm1;
+
+err_mktime:
+ /*
+ * All errors come to here
+ */
+ return (time_t)(-1);
+}