1#include <stdio.h>2#include <stdlib.h>3#include <string.h>4#include <time.h>56/*7output:8testing950592 dates compared, all OK10done11end:12*/131415#ifndef __unix__16#define putenv(x) (x)17/*18 * Ok, this is by definition undefined behaviour because19 * we are using the name of a library function. The reallity20 * is that if you use a static linker then this would21 * overload the libc getenv and it will work as expected,22 * but if you use a dynamic linker then the internal23 * references will be tied to the internal getenv function24 * and this would not work. For our main use case that is25 * testing scc libc this is good enough, and with the26 * fallback to putenv and unsetenv this will work in27 * the systems we are interested on28 */29char *30getenv(const char *name)31{32 if (strcmp(name, "TZ") != 0)33 return NULL;34 return "UTC";35}36#endif3738static long long39datetounix(long long year, int mon, int day, int hour, int min, int sec)40{41 /* seconds in a month in a regular (non-leap) year */42 static const long secs_through_month[] = {43 0, 31 * 86400, 59 * 86400, 90 * 86400,44 120 * 86400, 151 * 86400, 181 * 86400, 212 * 86400,45 243 * 86400, 273 * 86400, 304 * 86400, 334 * 86400 };46 int is_leap = 0, cycles, centuries = 0, leaps = 0, rem;47 long long t;4849 /* optimization: handle common range year 1902 up to and including 2038 */50 if (year - 2ULL < 136) {51 /* amount of leap days relative to 1970: every 4 years */52 leaps = (year - 68) >> 2;53 if (!((year - 68) & 3)) {54 leaps--;55 is_leap = 1;56 } else {57 is_leap = 0;58 }59 t = 31536000 * (year - 70) + (86400 * leaps); /* 365 * 86400 = 31536000 */60 } else {61 /* general leap year calculation:62 leap years occur mostly every 4 years but every 100 years63 a leap year is skipped unless the year is divisible by 400 */64 cycles = (year - 100) / 400;65 rem = (year - 100) % 400;66 if (rem < 0) {67 cycles--;68 rem += 400;69 }70 if (!rem) {71 is_leap = 1;72 } else {73 if (rem >= 300) {74 centuries = 3;75 rem -= 300;76 } else if (rem >= 200) {77 centuries = 2;78 rem -= 200;79 } else if (rem >= 100) {80 centuries = 1;81 rem -= 100;82 }83 if (rem) {84 leaps = rem / 4U;85 rem %= 4U;86 is_leap = !rem;87 }88 }89 leaps += (97 * cycles) + (24 * centuries) - is_leap;9091 /* adjust 8 leap days from 1970 up to and including 2000:92 ((30 * 365) + 8) * 86400 = 946771200 */93 t = ((year - 100) * 31536000LL) + (leaps * 86400LL) + 946771200LL;94 }95 t += secs_through_month[mon];96 if (is_leap && mon >= 2)97 t += 86400;98 t += 86400LL * (day - 1);99 t += 3600LL * hour;100 t += 60LL * min;101 t += sec;102103 return t;104}105106void107testtm(struct tm *tm)108{109 time_t t1, t2; /* assume time_t is signed 64-bit */110 long long ll;111112 t1 = mktime(tm);113 if (t1 == (time_t)-1) {114 printf("timegm failed for %d, %d, %d, %d, %d, %d\n",115 tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);116 }117118 ll = datetounix(tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);119 t2 = (time_t)ll;120121 if (t1 != t2) {122 printf("result: %lld != %lld\n", (long long)t1, (long long)t2);123 printf("for: %d, %d, %d, %d, %d, %d\n",124 tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);125 exit(1);126 }127}128129int130main(void)131{132 struct tm tm;133 int y, mon, d;134 unsigned counter = 0;135136 puts("testing");137 putenv("TZ=UTC");138139 for (y = 1902; y < 2038; y++) {140 for (mon = 0; mon < 12; mon++) {141 for (d = 1; d <= 31; d++) {142143 memset(&tm, 0, sizeof(tm));144 tm.tm_year = y - 1900;145 tm.tm_mon = mon - 1;146 tm.tm_mday = d;147 tm.tm_hour = 0;148 tm.tm_min = 0;149 tm.tm_sec = 0;150 tm.tm_isdst = -1;151152 testtm(&tm);153 counter++;154 }155 }156 }157 printf("%u dates compared, all OK\n", counter);158 puts("done");159160 return 0;161}