1/* $NetBSD: timegm.c,v 1.3 2005/05/11 01:01:56 lukem Exp $ */ 2/* from ? */ 3 4#include "tnftp.h" 5 6/* 7 * UTC version of mktime(3) 8 */ 9 10/* 11 * This code is not portable, but works on most Unix-like systems. 12 * If the local timezone has no summer time, using mktime(3) function 13 * and adjusting offset would be usable (adjusting leap seconds 14 * is still required, though), but the assumption is not always true. 15 * 16 * Anyway, no portable and correct implementation of UTC to time_t 17 * conversion exists.... 18 */ 19 20static time_t 21sub_mkgmt(struct tm *tm) 22{ 23 int y, nleapdays; 24 time_t t; 25 /* days before the month */ 26 static const unsigned short moff[12] = { 27 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 28 }; 29 30 /* 31 * XXX: This code assumes the given time to be normalized. 32 * Normalizing here is impossible in case the given time is a leap 33 * second but the local time library is ignorant of leap seconds. 34 */ 35 36 /* minimal sanity checking not to access outside of the array */ 37 if ((unsigned) tm->tm_mon >= 12) 38 return (time_t) -1; 39 if (tm->tm_year < EPOCH_YEAR - TM_YEAR_BASE) 40 return (time_t) -1; 41 42 y = tm->tm_year + TM_YEAR_BASE - (tm->tm_mon < 2); 43 nleapdays = y / 4 - y / 100 + y / 400 - 44 ((EPOCH_YEAR-1) / 4 - (EPOCH_YEAR-1) / 100 + (EPOCH_YEAR-1) / 400); 45 t = ((((time_t) (tm->tm_year - (EPOCH_YEAR - TM_YEAR_BASE)) * 365 + 46 moff[tm->tm_mon] + tm->tm_mday - 1 + nleapdays) * 24 + 47 tm->tm_hour) * 60 + tm->tm_min) * 60 + tm->tm_sec; 48 49 return (t < 0 ? (time_t) -1 : t); 50} 51 52time_t 53timegm(struct tm *tm) 54{ 55 time_t t, t2; 56 struct tm *tm2; 57 int sec; 58 59 /* Do the first guess. */ 60 if ((t = sub_mkgmt(tm)) == (time_t) -1) 61 return (time_t) -1; 62 63 /* save value in case *tm is overwritten by gmtime() */ 64 sec = tm->tm_sec; 65 66 tm2 = gmtime(&t); 67 if ((t2 = sub_mkgmt(tm2)) == (time_t) -1) 68 return (time_t) -1; 69 70 if (t2 < t || tm2->tm_sec != sec) { 71 /* 72 * Adjust for leap seconds. 73 * 74 * real time_t time 75 * | 76 * tm 77 * / ... (a) first sub_mkgmt() conversion 78 * t 79 * | 80 * tm2 81 * / ... (b) second sub_mkgmt() conversion 82 * t2 83 * --->time 84 */ 85 /* 86 * Do the second guess, assuming (a) and (b) are almost equal. 87 */ 88 t += t - t2; 89 tm2 = gmtime(&t); 90 91 /* 92 * Either (a) or (b), may include one or two extra 93 * leap seconds. Try t, t + 2, t - 2, t + 1, and t - 1. 94 */ 95 if (tm2->tm_sec == sec 96 || (t += 2, tm2 = gmtime(&t), tm2->tm_sec == sec) 97 || (t -= 4, tm2 = gmtime(&t), tm2->tm_sec == sec) 98 || (t += 3, tm2 = gmtime(&t), tm2->tm_sec == sec) 99 || (t -= 2, tm2 = gmtime(&t), tm2->tm_sec == sec)) 100 ; /* found */ 101 else { 102 /* 103 * Not found. 104 */ 105 if (sec >= 60) 106 /* 107 * The given time is a leap second 108 * (sec 60 or 61), but the time library 109 * is ignorant of the leap second. 110 */ 111 ; /* treat sec 60 as 59, 112 sec 61 as 0 of the next minute */ 113 else 114 /* The given time may not be normalized. */ 115 t++; /* restore t */ 116 } 117 } 118 119 return (t < 0 ? (time_t) -1 : t); 120} 121