Created
May 8, 2013 23:35
-
-
Save ryanberthold/5544491 to your computer and use it in GitHub Desktop.
Portability test for timegm replacements. One version is the classic non-thread-safe TZ+mktime, while the other is thread-safe mktime+tm_yday.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
gcc -Wall -pedantic -std=c99 -O2 -o timegm timegm.c | |
Test manual epoch calculation with tm_yday vs TZ+mktime. | |
On Linux, gcc 4.4.7, the manual method is ~5x faster. | |
Usage: | |
./timegm | |
<ctrl-C> to quit | |
*/ | |
#define _XOPEN_SOURCE | |
#define _POSIX_C_SOURCE 200112L | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <time.h> | |
#include <sys/time.h> | |
#include <signal.h> | |
int lf = 1; | |
void sighand(int sig) | |
{ | |
lf = 0; | |
} | |
time_t my_timegm1(struct tm *tm) | |
{ | |
time_t ret; | |
char *tz; | |
tz = getenv("TZ"); | |
setenv("TZ", "", 1); | |
tzset(); | |
ret = mktime(tm); | |
if (tz) | |
setenv("TZ", tz, 1); | |
else | |
unsetenv("TZ"); | |
tzset(); | |
return ret; | |
} | |
time_t my_timegm2(struct tm *tm) | |
{ | |
// make sure tm_yday is filled in | |
mktime(tm); | |
time_t ret = tm->tm_sec + tm->tm_min*60 + tm->tm_hour*3600 + tm->tm_yday*86400; | |
ret += ((time_t)31536000) * (tm->tm_year-70); | |
ret += ((tm->tm_year-69)/4)*86400 - ((tm->tm_year-1)/100)*86400 + ((tm->tm_year+299)/400)*86400; | |
return ret; | |
} | |
int main() | |
{ | |
signal(SIGINT, sighand); | |
srand(time(0)); | |
int checks = 0; | |
double timegm1_s = 0.0; | |
double timegm2_s = 0.0; | |
while(lf) | |
{ | |
char buf[256]; | |
snprintf(buf, sizeof(buf), "%d-%02d-%02dT%02d:%02d:%02d", | |
rand()%1000 + 1971, rand()%12+1, rand()%31+1, | |
rand()%24, rand()%60, rand()%60); | |
struct tm tm, tm1, tm2; | |
memset(&tm, 0, sizeof(tm)); | |
strptime(buf, "%Y-%m-%dT%H:%M:%S", &tm); | |
memcpy(&tm1, &tm, sizeof(tm)); | |
memcpy(&tm2, &tm, sizeof(tm)); | |
struct timeval tv0, tv1, tv2; | |
gettimeofday(&tv0,0); | |
time_t t1 = my_timegm1(&tm1); | |
gettimeofday(&tv1,0); | |
time_t t2 = my_timegm2(&tm2); | |
gettimeofday(&tv2,0); | |
double s0 = 1.0*(tv1.tv_sec-tv0.tv_sec) + 1e-6*(tv1.tv_usec-tv0.tv_usec); | |
double s1 = 1.0*(tv2.tv_sec-tv1.tv_sec) + 1e-6*(tv2.tv_usec-tv1.tv_usec); | |
timegm1_s += s0; | |
timegm2_s += s1; | |
if(t1 <= 0 || t1 != t2) | |
{ | |
printf("\nmismatch: %s: %.18g != %.18g\n", buf, (double)t1, (double)t2); | |
break; | |
} | |
checks++; | |
if((checks % 1000) == 0) | |
{ | |
printf(" %d (%.18g)", checks, (double)t1); | |
fflush(stdout); | |
} | |
} | |
printf("\ndid %d checks.\n", checks); | |
printf("timegm1: %g secs, %g usecs/call.\n", timegm1_s, 1e6*timegm1_s/checks); | |
printf("timegm2: %g secs, %g usecs/call.\n", timegm2_s, 1e6*timegm2_s/checks); | |
printf("\n"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Works fine on OS X with clang: