Time64_T mktime64(struct TM *input_date) { struct tm safe_date; struct TM date; Time64_T time; Year year = input_date->tm_year + 1900; if( date_in_safe_range(input_date, &SYSTEM_MKTIME_MIN, &SYSTEM_MKTIME_MAX) ) { copy_TM64_to_tm(input_date, &safe_date); time = (Time64_T)mktime(&safe_date); /* Correct the possibly out of bound input date */ copy_tm_to_TM64(&safe_date, input_date); TIME64_TRACE2("mktime64: safe year %ld, %"PRId64" seconds\n", (long)year, time); return time; } /* Have to make the year safe in date else it won't fit in safe_date */ date = *input_date; date.tm_year = safe_year(year) - 1900; copy_TM64_to_tm(&date, &safe_date); time = (Time64_T)mktime(&safe_date); TIME64_TRACE1("mktime64: %"PRId64" seconds\n", time); /* Correct the user's possibly out of bound input date */ copy_tm_to_TM64(&safe_date, input_date); time += seconds_between_years(year, (Year)(safe_date.tm_year + 1900)); TIME64_TRACE1("mktime64 => %"PRId64" seconds\n", time); return time; }
static Time64_T seconds_between_years(Year left_year, Year right_year) { int increment = (left_year > right_year) ? 1 : -1; Time64_T seconds = 0; Year cycles; if( left_year > 2400 ) { TIME64_TRACE1("year %"PRId64" > 2400\n", left_year); cycles = (left_year - 2400) / 400; left_year -= cycles * 400; seconds += cycles * seconds_in_gregorian_cycle; } else if( left_year < 1600 ) { TIME64_TRACE1("year %"PRId64" < 1600\n", left_year); cycles = (left_year - 1600) / 400; left_year += cycles * 400; seconds += cycles * seconds_in_gregorian_cycle; } if (increment > 0) { TIME64_TRACE2("year %"PRId64" > safe_year %"PRId64"\n", left_year, right_year); while( left_year != right_year ) { seconds += length_of_year[IS_LEAP_ABS(right_year)] * 60 * 60 * 24; right_year++; } TIME64_TRACE1("adjust by %"PRId64" days\n", seconds / (60 * 60 * 24)); return seconds; } else { TIME64_TRACE2("year %"PRId64" < safe_year %"PRId64"\n", left_year, right_year); while( left_year != right_year ) { seconds -= length_of_year[IS_LEAP_ABS(right_year)] * 60 * 60 * 24; right_year--; } TIME64_TRACE1("adjust by %"PRId64" days\n", seconds / (60 * 60 * 24)); return seconds; } }
struct TM *localtime64_r (const Time64_T *time, struct TM *local_tm) { time_t safe_time; struct tm safe_date; struct TM gm_tm; Year orig_year; int month_diff; assert(local_tm != NULL); /* Use the system localtime() if time_t is small enough */ if( SHOULD_USE_SYSTEM_LOCALTIME(*time) ) { safe_time = (time_t)*time; TIME64_TRACE1("Using system localtime for %lld\n", *time); LOCALTIME_R(&safe_time, &safe_date); copy_tm_to_TM64(&safe_date, local_tm); assert(check_tm(local_tm)); return local_tm; } if( gmtime64_r(time, &gm_tm) == NULL ) { TIME64_TRACE1("gmtime64_r returned null for %lld\n", *time); return NULL; } orig_year = gm_tm.tm_year; if (gm_tm.tm_year > (2037 - 1900) || gm_tm.tm_year < (1970 - 1900) ) { TIME64_TRACE1("Mapping tm_year %lld to safe_year\n", (Year)gm_tm.tm_year); gm_tm.tm_year = safe_year((Year)(gm_tm.tm_year + 1900)) - 1900; } safe_time = (time_t)timegm64(&gm_tm); if( LOCALTIME_R(&safe_time, &safe_date) == NULL ) { TIME64_TRACE1("localtime_r(%d) returned NULL\n", (int)safe_time); return NULL; } copy_tm_to_TM64(&safe_date, local_tm); local_tm->tm_year = orig_year; if( local_tm->tm_year != orig_year ) { TIME64_TRACE2("tm_year overflow: tm_year %lld, orig_year %lld\n", (Year)local_tm->tm_year, (Year)orig_year); #ifdef EOVERFLOW errno = EOVERFLOW; #endif return NULL; } month_diff = local_tm->tm_mon - gm_tm.tm_mon; /* When localtime is Dec 31st previous year and gmtime is Jan 1st next year. */ if( month_diff == 11 ) { local_tm->tm_year--; } /* When localtime is Jan 1st, next year and gmtime is Dec 31st, previous year. */ if( month_diff == -11 ) { local_tm->tm_year++; } /* GMT is Jan 1st, xx01 year, but localtime is still Dec 31st in a non-leap xx00. There is one point in the cycle we can't account for which the safe xx00 year is a leap year. So we need to correct for Dec 31st comming out as the 366th day of the year. */ if( !IS_LEAP(local_tm->tm_year) && local_tm->tm_yday == 365 ) local_tm->tm_yday--; assert(check_tm(local_tm)); return local_tm; }