scc

simple c99 compiler
git clone git://git.simple-cc.org/scc
Log | Files | Refs | README | LICENSE

0078-time.c (3719B)


      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <string.h>
      4 #include <time.h>
      5 
      6 /*
      7 output:
      8 testing
      9 50592 dates compared, all OK
     10 done
     11 end:
     12 */
     13 
     14 
     15 #ifndef __unix__
     16 #define putenv(x) (x)
     17 /*
     18  * Ok, this is by definition undefined behaviour because
     19  * we are using the name of a library function. The reallity
     20  * is that if you use a static linker then this would
     21  * overload the libc getenv and it will work as expected,
     22  * but if you use a dynamic linker then the internal
     23  * references will be tied to the internal getenv function
     24  * and this would not work. For our main use case that is
     25  * testing scc libc this is good enough, and with the
     26  * fallback to putenv and unsetenv this will work in
     27  * the systems we are interested on
     28  */
     29 char *
     30 getenv(const char *name)
     31 {
     32 	if (strcmp(name, "TZ") != 0)
     33 		return NULL;
     34 	return "UTC";
     35 }
     36 #endif
     37 
     38 static long long
     39 datetounix(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;
     48 
     49 	/* 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 years
     63 		   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;
     90 
     91 		/* 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;
    102 
    103 	return t;
    104 }
    105 
    106 void
    107 testtm(struct tm *tm)
    108 {
    109 	time_t t1, t2; /* assume time_t is signed 64-bit */
    110 	long long ll;
    111 
    112 	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 	}
    117 
    118 	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;
    120 
    121 	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 }
    128 
    129 int
    130 main(void)
    131 {
    132 	struct tm tm;
    133 	int y, mon, d;
    134 	unsigned counter = 0;
    135 
    136 	puts("testing");
    137 	putenv("TZ=UTC");
    138 
    139 	for (y = 1902; y < 2038; y++) {
    140 		for (mon = 0; mon < 12; mon++) {
    141 			for (d = 1; d <= 31; d++) {
    142 
    143 				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;
    151 
    152 				testtm(&tm);
    153 				counter++;
    154 			}
    155 		}
    156 	}
    157 	printf("%u dates compared, all OK\n", counter);
    158 	puts("done");
    159 
    160 	return 0;
    161 }