Skip to content

Instantly share code, notes, and snippets.

@richlowe
Created August 7, 2021 22:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save richlowe/8a84030dfd360fabd2b508da26a4db06 to your computer and use it in GitHub Desktop.
Save richlowe/8a84030dfd360fabd2b508da26a4db06 to your computer and use it in GitHub Desktop.
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#ifndef _LP64
#error "This only works 64bit"
#endif
#ifdef __linux__
#define UTC "GMT"
#else
#define UTC "UTC"
#endif /* __linux__ */
bool
tm_eql(struct tm *a, struct tm *b) {
bool ret = true;
#define CMP_FIELD(a, b, field, fmt) \
if (a->field != b->field) { \
printf(" " #field " differs " fmt " v. " fmt "\n", a->field, b->field); \
ret = false; \
}
CMP_FIELD(a, b, tm_sec, "%d");
CMP_FIELD(a, b, tm_min, "%d");
CMP_FIELD(a, b, tm_hour, "%d");
CMP_FIELD(a, b, tm_mday, "%d");
CMP_FIELD(a, b, tm_mon, "%d");
CMP_FIELD(a, b, tm_year, "%d");
CMP_FIELD(a, b, tm_wday, "%d");
CMP_FIELD(a, b, tm_yday, "%d");
CMP_FIELD(a, b, tm_isdst, "%d");
#ifdef NEW_FIELDS
CMP_FIELD(a, b, tm_gmtoff, "%ld");
if ((a->tm_zone == NULL) || (b->tm_zone == NULL)) {
if (a->tm_zone != b->tm_zone) {
printf(" tm_zone differs %s v. %s\n", a->tm_zone, b->tm_zone);
ret = false;
}
} else if (strcmp(a->tm_zone, b->tm_zone) != 0) {
printf(" tm_zone differs %s v. %s\n", a->tm_zone, b->tm_zone);
ret = false;
}
#endif
return (ret);
}
/*
* XXX: Force timezones as necessary
*
* A time in summer time
*
* timezones to test:
* UTC
* EST
*
* check times that work based on UTC and local times -in- UTC agree
*/
int
test_eastern(void)
{
int ret = 0;
time_t t;
struct tm res;
char canary[1024] = {0};
setenv("TZ", "EST5EDT", 1);
tzset();
/* Arbitrary */
printf("EST Arbitrary localtime\n");
t = 1582764890;
bzero(&res, sizeof (struct tm));
if (localtime_r(&t, &res) == NULL) {
perror(" error");
ret = 1;
} else {
struct tm ref = {
.tm_sec = 50,
.tm_min = 54,
.tm_hour = 19,
.tm_mday = 26,
.tm_mon = 1,
.tm_year = 120,
.tm_wday = 3,
.tm_yday = 56,
.tm_isdst = 0,
#ifdef NEW_FIELDS
.tm_gmtoff = -18000,
.tm_zone = "EST",
#endif
};
if (!tm_eql(&ref, &res))
ret = 1;
}
printf("EST Arbitrary gmtime\n");
t = 1582764890;
bzero(&res, sizeof (struct tm));
if (gmtime_r(&t, &res) == NULL) {
perror(" error");
ret = 1;
} else {
struct tm ref = {
.tm_sec = 50,
.tm_min = 54,
.tm_hour = 0,
.tm_mday = 27,
.tm_mon = 1,
.tm_year = 120,
.tm_wday = 4,
.tm_yday = 57,
.tm_isdst = 0,
#ifdef NEW_FIELDS
.tm_gmtoff = 0,
.tm_zone = UTC,
#endif
};
if (!tm_eql(&ref, &res))
ret = 1;
}
/* Epoch */
printf("EST Epoch localtime\n");
t = 0;
bzero(&res, sizeof (struct tm));
if (localtime_r(&t, &res) == NULL) {
perror(" error");
ret = 1;
} else {
struct tm ref = {
.tm_sec = 0,
.tm_min = 0,
.tm_hour = 19,
.tm_mday = 31,
.tm_mon = 11,
.tm_year = 69,
.tm_wday = 3,
.tm_yday = 364,
.tm_isdst = 0,
#ifdef NEW_FIELDS
.tm_gmtoff = -18000,
.tm_zone = "EST",
#endif
};
if (!tm_eql(&ref, &res))
ret = 1;
}
printf("EST Epoch gmtime\n");
t = 0;
bzero(&res, sizeof (struct tm));
if (gmtime_r(&t, &res) == NULL) {
perror(" error");
ret = 1;
} else {
struct tm ref = {
.tm_sec = 0,
.tm_min = 0,
.tm_hour = 0,
.tm_mday = 1,
.tm_mon = 0,
.tm_year = 70,
.tm_wday = 4,
.tm_yday = 0,
.tm_isdst = 0,
#ifdef NEW_FIELDS
.tm_gmtoff = 0,
.tm_zone = UTC,
#endif
};
if (!tm_eql(&ref, &res))
ret = 1;
}
/* Minimal tm */
printf("EST Minimal (tm) localtime\n");
t = -67768040609722800;
bzero(&res, sizeof (struct tm));
if (localtime_r(&t, &res) == NULL) {
perror(" error");
ret = 1;
} else {
struct tm ref = {
.tm_sec = 0,
.tm_min = 0,
.tm_hour = 0,
.tm_mday = 1,
.tm_mon = 0,
.tm_year = -2147483648,
.tm_wday = 4,
.tm_yday = 0,
.tm_isdst = 0,
#ifdef NEW_FIELDS
.tm_gmtoff = -18000,
.tm_zone = "EST",
#endif
};
if (!tm_eql(&ref, &res))
ret = 1;
}
printf("EST Minimal (tm) gmtime\n");
t = -67768040609740800;
bzero(&res, sizeof (struct tm));
if (gmtime_r(&t, &res) == NULL) {
perror(" error");
ret = 1;
} else {
struct tm ref = {
.tm_sec = 0,
.tm_min = 0,
.tm_hour = 0,
.tm_mday = 1,
.tm_mon = 0,
.tm_year = INT_MIN,
.tm_wday = 4,
.tm_yday = 0,
.tm_isdst = 0,
#ifdef NEW_FIELDS
.tm_gmtoff = 0,
.tm_zone = UTC,
#endif
};
if (!tm_eql(&ref, &res))
ret = 1;
}
/* Minimal 32bit */
printf("EST Minimal (32bit) localtime\n");
t = (time_t)INT_MIN;
bzero(&res, sizeof (struct tm));
if (localtime_r(&t, &res) == NULL) {
perror(" error");
ret = 1;
} else {
struct tm ref = {
.tm_sec = 52,
.tm_min = 45,
.tm_hour = 15,
.tm_mday = 13,
.tm_mon = 11,
.tm_year = 1,
.tm_wday = 5,
.tm_yday = 346,
.tm_isdst = 0,
#ifdef NEW_FIELDS
.tm_gmtoff = -18000,
.tm_zone = "EST",
#endif
};
if (!tm_eql(&ref, &res))
ret = 1;
}
printf("EST Minimal (32bit) gmtime\n");
t = (time_t)INT_MIN;
bzero(&res, sizeof (struct tm));
if (gmtime_r(&t, &res) == NULL) {
perror(" error");
ret = 1;
} else {
struct tm ref = {
.tm_sec = 52,
.tm_min = 45,
.tm_hour = 20,
.tm_mday = 13,
.tm_mon = 11,
.tm_year = 1,
.tm_wday = 5,
.tm_yday = 346,
.tm_isdst = 0,
#ifdef NEW_FIELDS
.tm_gmtoff = 0,
.tm_zone = UTC,
#endif
};
if (!tm_eql(&ref, &res))
ret = 1;
}
/* Maximal tm*/
printf("EST Maximal (tm) localtime\n");
//t = 67768036160158799;
t = 67768036191694799;
bzero(&res, sizeof (struct tm));
if (localtime_r(&t, &res) == NULL) {
perror(" error");
ret = 1;
} else {
struct tm ref = {
.tm_sec = 59,
.tm_min = 59,
.tm_hour = 23,
.tm_mday = 31,
.tm_mon = 11,
.tm_year = INT_MAX,
.tm_wday = 3,
.tm_yday = 364,
.tm_isdst = 0,
#ifdef NEW_FIELDS
.tm_gmtoff = -18000,
.tm_zone = "EST",
#endif
};
if (!tm_eql(&ref, &res))
ret = 1;
}
printf("EST Maximal (tm) gmtime\n");
t = 67768036191676799;
bzero(&res, sizeof (struct tm));
if (gmtime_r(&t, &res) == NULL) {
perror(" error");
ret = 1;
} else {
struct tm ref = {
.tm_sec = 59,
.tm_min = 59,
.tm_hour = 23,
.tm_mday = 31,
.tm_mon = 11,
.tm_year = INT_MAX,
.tm_wday = 3,
.tm_yday = 364,
.tm_isdst = 0,
#ifdef NEW_FIELDS
.tm_gmtoff = 0,
.tm_zone = UTC,
#endif
};
if (!tm_eql(&ref, &res))
ret = 1;
}
/* Maximal 32bit*/
printf("EST Maximal (32bit) localtime\n");
t = (time_t)INT_MAX;
bzero(&res, sizeof (struct tm));
if (localtime_r(&t, &res) == NULL) {
perror(" error");
ret = 1;
} else {
struct tm ref = {
.tm_sec = 7,
.tm_min = 14,
.tm_hour = 22,
.tm_mday = 18,
.tm_mon = 0,
.tm_year = 138,
.tm_wday = 1,
.tm_yday = 17,
.tm_isdst = 0,
#ifdef NEW_FIELDS
.tm_gmtoff = -18000,
.tm_zone = "EST",
#endif
};
if (!tm_eql(&ref, &res))
ret = 1;
}
printf("EST Maximal (32bit) gmtime\n");
t = (time_t)INT_MAX;
bzero(&res, sizeof (struct tm));
if (gmtime_r(&t, &res) == NULL) {
perror(" error");
ret = 1;
} else {
struct tm ref = {
.tm_sec = 7,
.tm_min = 14,
.tm_hour = 3,
.tm_mday = 19,
.tm_mon = 0,
.tm_year = 138,
.tm_wday = 2,
.tm_yday = 18,
.tm_isdst = 0,
#ifdef NEW_FIELDS
.tm_gmtoff = 0,
.tm_zone = UTC,
#endif
};
if (!tm_eql(&ref, &res))
ret = 1;
}
/* A leap day */
printf("EST Feb 29th localtime\n");
t = 1582977600;
bzero(&res, sizeof (struct tm));
if (localtime_r(&t, &res) == NULL) {
perror(" error");
ret = 1;
} else {
struct tm ref = {
.tm_hour = 7,
.tm_mday = 29,
.tm_mon = 1,
.tm_year = 120,
.tm_wday = 6,
.tm_yday = 59,
.tm_isdst = 0,
#ifdef NEW_FIELDS
.tm_gmtoff = -18000,
.tm_zone = "EST"
#endif
};
if (!tm_eql(&ref, &res))
ret = 1;
}
printf("EST Feb 29th gmtime\n");
t = 1582977600;
bzero(&res, sizeof (struct tm));
if (gmtime_r(&t, &res) == NULL) {
perror(" error");
ret = 1;
} else {
struct tm ref = {
.tm_hour = 12,
.tm_mday = 29,
.tm_mon = 1,
.tm_year = 120,
.tm_wday = 6,
.tm_yday = 59,
.tm_isdst = 0,
#ifdef NEW_FIELDS
.tm_gmtoff = 0,
.tm_zone = UTC
#endif
};
if (!tm_eql(&ref, &res))
ret = 1;
}
/* Summertime */
printf("EST June 21st localtime\n");
t = 1592715600;
bzero(&res, sizeof (struct tm));
if (localtime_r(&t, &res) == NULL) {
perror(" error");
ret = 1;
} else {
struct tm ref = {
.tm_hour = 1,
.tm_mday = 21,
.tm_mon = 5,
.tm_year = 120,
.tm_wday = 0,
.tm_yday = 172,
.tm_isdst = 1,
#ifdef NEW_FIELDS
.tm_gmtoff = -14400,
.tm_zone = "EDT"
#endif
};
if (!tm_eql(&ref, &res))
ret = 1;
}
printf("EST June 21st gmtime\n");
t = 1592715600;
bzero(&res, sizeof (struct tm));
if (gmtime_r(&t, &res) == NULL) {
perror(" error");
ret = 1;
} else {
struct tm ref = {
.tm_hour = 5,
.tm_mday = 21,
.tm_mon = 5,
.tm_year = 120,
.tm_wday = 0,
.tm_yday = 172,
.tm_isdst = 0,
#ifdef NEW_FIELDS
.tm_gmtoff = 0,
.tm_zone = UTC
#endif
};
if (!tm_eql(&ref, &res))
ret = 1;
}
for (int i = 0; i < 1024; i++) {
if (canary[i] != 0)
printf("canary garbled at byte %d\n", i);
}
return (ret);
}
int
main(int argc, char **argv)
{
int ret;
ret = test_eastern();
return (ret);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment