1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* This code placed in the public domain by Mark W. Eichin */ 3 4 #include "autoconf.h" 5 #include <stdio.h> 6 7 #ifdef HAVE_SYS_TYPES_H 8 #include <sys/types.h> 9 #endif 10 #ifdef HAVE_SYS_TIME_H 11 #include <sys/time.h> 12 #endif 13 #include <time.h> 14 15 #include "k5-gmt_mktime.h" 16 17 #if !HAVE_TIMEGM || TEST_LEAP 18 static time_t gmt_mktime(struct tm *t); 19 #endif 20 21 /* 22 * Use the nonstandard timegm() (if available) to convert broken-down 23 * UTC times into time_t values. Use our custom gmt_mktime() if 24 * timegm() is not available. 25 * 26 * We use gmtime() (or gmtime_r()) when encoding ASN.1 GeneralizedTime 27 * values. On systems where a "right" (leap-second-aware) time zone 28 * is configured, gmtime() adjusts for the presence of accumulated 29 * leap seconds in the input time_t value. POSIX requires that time_t 30 * values omit leap seconds; systems configured to include leap 31 * seconds in their time_t values are non-conforming and will have 32 * difficulties exchanging timestamp information with other systems. 33 * 34 * We use krb5int_gmt_mktime() for decoding ASN.1 GeneralizedTime 35 * values. If timegm() is not available, krb5int_gmt_mktime() won't 36 * be the inverse of gmtime() on a system that counts leap seconds. A 37 * system configured with a "right" time zone probably has timegm() 38 * available; without it, an application would have no reliable way of 39 * converting broken-down UTC times into time_t values. 40 */ 41 time_t 42 krb5int_gmt_mktime(struct tm *t) 43 { 44 #if HAVE_TIMEGM 45 return timegm(t); 46 #else 47 return gmt_mktime(t); 48 #endif 49 } 50 51 #if !HAVE_TIMEGM || TEST_LEAP 52 53 /* take a struct tm, return seconds from GMT epoch */ 54 /* like mktime, this ignores tm_wday and tm_yday. */ 55 /* unlike mktime, this does not set them... it only passes a return value. */ 56 57 static const int days_in_month[12] = { 58 0, /* jan 31 */ 59 31, /* feb 28 */ 60 59, /* mar 31 */ 61 90, /* apr 30 */ 62 120, /* may 31 */ 63 151, /* jun 30 */ 64 181, /* jul 31 */ 65 212, /* aug 31 */ 66 243, /* sep 30 */ 67 273, /* oct 31 */ 68 304, /* nov 30 */ 69 334 /* dec 31 */ 70 }; 71 72 #define hasleapday(year) (year%400?(year%100?(year%4?0:1):0):1) 73 74 static time_t 75 gmt_mktime(struct tm *t) 76 { 77 uint32_t accum; 78 79 #define assert_time(cnd) if(!(cnd)) return (time_t) -1 80 81 /* 82 * For 32-bit unsigned time values starting on 1/1/1970, the range is: 83 * time 0x00000000 -> Thu Jan 1 00:00:00 1970 84 * time 0xffffffff -> Sun Feb 7 06:28:15 2106 85 * 86 * We can't encode all dates in 2106, and we're not doing overflow checking 87 * for such cases. 88 */ 89 assert_time(t->tm_year>=70); 90 assert_time(t->tm_year<=206); 91 92 assert_time(t->tm_mon>=0); 93 assert_time(t->tm_mon<=11); 94 assert_time(t->tm_mday>=1); 95 assert_time(t->tm_mday<=31); 96 assert_time(t->tm_hour>=0); 97 assert_time(t->tm_hour<=23); 98 assert_time(t->tm_min>=0); 99 assert_time(t->tm_min<=59); 100 assert_time(t->tm_sec>=0); 101 assert_time(t->tm_sec<=62); 102 103 #undef assert_time 104 105 106 accum = t->tm_year - 70; 107 accum *= 365; /* 365 days/normal year */ 108 109 /* add in leap day for all previous years */ 110 if (t->tm_year >= 70) 111 accum += (t->tm_year - 69) / 4; 112 else 113 accum -= (72 - t->tm_year) / 4; 114 /* add in leap day for this year */ 115 if(t->tm_mon >= 2) /* march or later */ 116 if(hasleapday((t->tm_year + 1900))) accum += 1; 117 118 accum += days_in_month[t->tm_mon]; 119 accum += t->tm_mday-1; /* days of month are the only 1-based field */ 120 accum *= 24; /* 24 hour/day */ 121 accum += t->tm_hour; 122 accum *= 60; /* 60 minute/hour */ 123 accum += t->tm_min; 124 accum *= 60; /* 60 seconds/minute */ 125 accum += t->tm_sec; 126 127 return accum; 128 } 129 #endif /* !HAVE_TIMEGM || TEST_LEAP */ 130 131 #ifdef TEST_LEAP 132 int 133 main (int argc, char *argv[]) 134 { 135 int yr; 136 time_t t; 137 struct tm tm = { 138 .tm_mon = 0, .tm_mday = 1, 139 .tm_hour = 0, .tm_min = 0, .tm_sec = 0, 140 }; 141 for (yr = 60; yr <= 104; yr++) 142 { 143 printf ("1/1/%d%c -> ", 1900 + yr, hasleapday((1900+yr)) ? '*' : ' '); 144 tm.tm_year = yr; 145 t = gmt_mktime (&tm); 146 if (t == (time_t) -1) 147 printf ("-1\n"); 148 else 149 { 150 long u; 151 if (t % (24 * 60 * 60)) 152 printf ("(not integral multiple of days) "); 153 u = t / (24 * 60 * 60); 154 printf ("%3ld*365%+ld\t0x%08lx\n", 155 (long) (u / 365), (long) (u % 365), 156 (long) t); 157 } 158 } 159 t = 0x80000000, printf ("time 0x%lx -> %s", t, ctime (&t)); 160 t = 0x7fffffff, printf ("time 0x%lx -> %s", t, ctime (&t)); 161 return 0; 162 } 163 #endif 164