| /* |
| * 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; |
| } |