Skip to content

Instantly share code, notes, and snippets.

@ryanberthold
Created May 8, 2013 23:35
Show Gist options
  • Save ryanberthold/5544491 to your computer and use it in GitHub Desktop.
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.
/*
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;
}
@timj
Copy link

timj commented May 9, 2013

Works fine on OS X with clang:

did 1441380 checks.
timegm1: 66.7086 secs, 46.2811 usecs/call.
timegm2: 6.05786 secs, 4.20282 usecs/call.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment