0,0 → 1,287 |
/* |
* mktime.c |
* Original Author: G. Haley |
* |
* Converts the broken-down time, expressed as local time, in the structure |
* pointed to by tim_p into a calendar time value. The original values of the |
* tm_wday and tm_yday fields of the structure are ignored, and the original |
* values of the other fields have no restrictions. On successful completion |
* the fields of the structure are set to represent the specified calendar |
* time. Returns the specified calendar time. If the calendar time can not be |
* represented, returns the value (time_t) -1. |
* |
* Modifications: Fixed tm_isdst usage - 27 August 2008 Craig Howland. |
*/ |
|
/* |
FUNCTION |
<<mktime>>---convert time to arithmetic representation |
|
INDEX |
mktime |
|
ANSI_SYNOPSIS |
#include <time.h> |
time_t mktime(struct tm *<[timp]>); |
|
TRAD_SYNOPSIS |
#include <time.h> |
time_t mktime(<[timp]>) |
struct tm *<[timp]>; |
|
DESCRIPTION |
<<mktime>> assumes the time at <[timp]> is a local time, and converts |
its representation from the traditional representation defined by |
<<struct tm>> into a representation suitable for arithmetic. |
|
<<localtime>> is the inverse of <<mktime>>. |
|
RETURNS |
If the contents of the structure at <[timp]> do not form a valid |
calendar time representation, the result is <<-1>>. Otherwise, the |
result is the time, converted to a <<time_t>> value. |
|
PORTABILITY |
ANSI C requires <<mktime>>. |
|
<<mktime>> requires no supporting OS subroutines. |
*/ |
|
#include <stdlib.h> |
#include <time.h> |
#include "local.h" |
|
#define _SEC_IN_MINUTE 60L |
#define _SEC_IN_HOUR 3600L |
#define _SEC_IN_DAY 86400L |
|
static _CONST int DAYS_IN_MONTH[12] = |
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; |
|
#define _DAYS_IN_MONTH(x) ((x == 1) ? days_in_feb : DAYS_IN_MONTH[x]) |
|
static _CONST int _DAYS_BEFORE_MONTH[12] = |
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; |
|
#define _ISLEAP(y) (((y) % 4) == 0 && (((y) % 100) != 0 || (((y)+1900) % 400) == 0)) |
#define _DAYS_IN_YEAR(year) (_ISLEAP(year) ? 366 : 365) |
|
static void |
_DEFUN(validate_structure, (tim_p), |
struct tm *tim_p) |
{ |
div_t res; |
int days_in_feb = 28; |
|
/* calculate time & date to account for out of range values */ |
if (tim_p->tm_sec < 0 || tim_p->tm_sec > 59) |
{ |
res = div (tim_p->tm_sec, 60); |
tim_p->tm_min += res.quot; |
if ((tim_p->tm_sec = res.rem) < 0) |
{ |
tim_p->tm_sec += 60; |
--tim_p->tm_min; |
} |
} |
|
if (tim_p->tm_min < 0 || tim_p->tm_min > 59) |
{ |
res = div (tim_p->tm_min, 60); |
tim_p->tm_hour += res.quot; |
if ((tim_p->tm_min = res.rem) < 0) |
{ |
tim_p->tm_min += 60; |
--tim_p->tm_hour; |
} |
} |
|
if (tim_p->tm_hour < 0 || tim_p->tm_hour > 23) |
{ |
res = div (tim_p->tm_hour, 24); |
tim_p->tm_mday += res.quot; |
if ((tim_p->tm_hour = res.rem) < 0) |
{ |
tim_p->tm_hour += 24; |
--tim_p->tm_mday; |
} |
} |
|
if (tim_p->tm_mon < 0 || tim_p->tm_mon > 11) |
{ |
res = div (tim_p->tm_mon, 12); |
tim_p->tm_year += res.quot; |
if ((tim_p->tm_mon = res.rem) < 0) |
{ |
tim_p->tm_mon += 12; |
--tim_p->tm_year; |
} |
} |
|
if (_DAYS_IN_YEAR (tim_p->tm_year) == 366) |
days_in_feb = 29; |
|
if (tim_p->tm_mday <= 0) |
{ |
while (tim_p->tm_mday <= 0) |
{ |
if (--tim_p->tm_mon == -1) |
{ |
tim_p->tm_year--; |
tim_p->tm_mon = 11; |
days_in_feb = |
((_DAYS_IN_YEAR (tim_p->tm_year) == 366) ? |
29 : 28); |
} |
tim_p->tm_mday += _DAYS_IN_MONTH (tim_p->tm_mon); |
} |
} |
else |
{ |
while (tim_p->tm_mday > _DAYS_IN_MONTH (tim_p->tm_mon)) |
{ |
tim_p->tm_mday -= _DAYS_IN_MONTH (tim_p->tm_mon); |
if (++tim_p->tm_mon == 12) |
{ |
tim_p->tm_year++; |
tim_p->tm_mon = 0; |
days_in_feb = |
((_DAYS_IN_YEAR (tim_p->tm_year) == 366) ? |
29 : 28); |
} |
} |
} |
} |
|
time_t |
_DEFUN(mktime, (tim_p), |
struct tm *tim_p) |
{ |
time_t tim = 0; |
long days = 0; |
int year, isdst=0; |
__tzinfo_type *tz = __gettzinfo (); |
|
/* validate structure */ |
validate_structure (tim_p); |
|
/* compute hours, minutes, seconds */ |
tim += tim_p->tm_sec + (tim_p->tm_min * _SEC_IN_MINUTE) + |
(tim_p->tm_hour * _SEC_IN_HOUR); |
|
/* compute days in year */ |
days += tim_p->tm_mday - 1; |
days += _DAYS_BEFORE_MONTH[tim_p->tm_mon]; |
if (tim_p->tm_mon > 1 && _DAYS_IN_YEAR (tim_p->tm_year) == 366) |
days++; |
|
/* compute day of the year */ |
tim_p->tm_yday = days; |
|
if (tim_p->tm_year > 10000 || tim_p->tm_year < -10000) |
return (time_t) -1; |
|
/* compute days in other years */ |
if ((year = tim_p->tm_year) > 70) |
{ |
for (year = 70; year < tim_p->tm_year; year++) |
days += _DAYS_IN_YEAR (year); |
} |
else if (year < 70) |
{ |
for (year = 69; year > tim_p->tm_year; year--) |
days -= _DAYS_IN_YEAR (year); |
days -= _DAYS_IN_YEAR (year); |
} |
|
/* compute total seconds */ |
tim += (days * _SEC_IN_DAY); |
|
TZ_LOCK; |
|
if (_daylight) |
{ |
int tm_isdst; |
int y = tim_p->tm_year + YEAR_BASE; |
/* Convert user positive into 1 */ |
tm_isdst = tim_p->tm_isdst > 0 ? 1 : tim_p->tm_isdst; |
isdst = tm_isdst; |
|
if (y == tz->__tzyear || __tzcalc_limits (y)) |
{ |
/* calculate start of dst in dst local time and |
start of std in both std local time and dst local time */ |
time_t startdst_dst = tz->__tzrule[0].change |
- (time_t) tz->__tzrule[1].offset; |
time_t startstd_dst = tz->__tzrule[1].change |
- (time_t) tz->__tzrule[1].offset; |
time_t startstd_std = tz->__tzrule[1].change |
- (time_t) tz->__tzrule[0].offset; |
/* if the time is in the overlap between dst and std local times */ |
if (tim >= startstd_std && tim < startstd_dst) |
; /* we let user decide or leave as -1 */ |
else |
{ |
isdst = (tz->__tznorth |
? (tim >= startdst_dst && tim < startstd_std) |
: (tim >= startdst_dst || tim < startstd_std)); |
/* if user committed and was wrong, perform correction, but not |
* if the user has given a negative value (which |
* asks mktime() to determine if DST is in effect or not) */ |
if (tm_isdst >= 0 && (isdst ^ tm_isdst) == 1) |
{ |
/* we either subtract or add the difference between |
time zone offsets, depending on which way the user got it |
wrong. The diff is typically one hour, or 3600 seconds, |
and should fit in a 16-bit int, even though offset |
is a long to accomodate 12 hours. */ |
int diff = (int) (tz->__tzrule[0].offset |
- tz->__tzrule[1].offset); |
if (!isdst) |
diff = -diff; |
tim_p->tm_sec += diff; |
tim += diff; /* we also need to correct our current time calculation */ |
int mday = tim_p->tm_mday; |
validate_structure (tim_p); |
mday = tim_p->tm_mday - mday; |
/* roll over occurred */ |
if (mday) { |
/* compensate for month roll overs */ |
if (mday > 1) |
mday = -1; |
else if (mday < -1) |
mday = 1; |
/* update days for wday calculation */ |
days += mday; |
/* handle yday */ |
if ((tim_p->tm_yday += mday) < 0) { |
--year; |
tim_p->tm_yday = _DAYS_IN_YEAR(year) - 1; |
} else { |
mday = _DAYS_IN_YEAR(year); |
if (tim_p->tm_yday > (mday - 1)) |
tim_p->tm_yday -= mday; |
} |
} |
} |
} |
} |
} |
|
/* add appropriate offset to put time in gmt format */ |
if (isdst == 1) |
tim += (time_t) tz->__tzrule[1].offset; |
else /* otherwise assume std time */ |
tim += (time_t) tz->__tzrule[0].offset; |
|
TZ_UNLOCK; |
|
/* reset isdst flag to what we have calculated */ |
tim_p->tm_isdst = isdst; |
|
/* compute day of the week */ |
if ((tim_p->tm_wday = (days + 4) % 7) < 0) |
tim_p->tm_wday += 7; |
|
return tim; |
} |