Skip to content

Commit

Permalink
xtime: Fix some xgetdate() bugs
Browse files Browse the repository at this point in the history
And add some more test cases.
  • Loading branch information
tavianator committed Feb 29, 2024
1 parent c8ab992 commit a9f3cde
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 18 deletions.
33 changes: 24 additions & 9 deletions src/xtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -174,19 +174,29 @@ int xtimegm(struct tm *tm, time_t *timep) {
return -1;
}

/** Parse a decimal digit. */
static int xgetdigit(char c) {
int ret = c - '0';
if (ret < 0 || ret > 9) {
return -1;
} else {
return ret;
}
}

/** Parse some digits from a timestamp. */
static int xgetpart(const char **str, size_t n, int *result) {
char buf[n + 1];
*result = 0;

for (size_t i = 0; i < n; ++i, ++*str) {
char c = **str;
if (c < '0' || c > '9') {
int dig = xgetdigit(**str);
if (dig < 0) {
return -1;
}
buf[i] = c;
*result *= 10;
*result += dig;
}
buf[n] = '\0';

*result = atoi(buf);
return 0;
}

Expand Down Expand Up @@ -239,6 +249,8 @@ int xgetdate(const char *str, struct timespec *result) {
goto end;
} else if (*str == ':') {
++str;
} else if (xgetdigit(*str) < 0) {
goto zone;
}
if (xgetpart(&str, 2, &tm.tm_min) != 0) {
goto invalid;
Expand All @@ -249,11 +261,14 @@ int xgetdate(const char *str, struct timespec *result) {
goto end;
} else if (*str == ':') {
++str;
} else if (xgetdigit(*str) < 0) {
goto zone;
}
if (xgetpart(&str, 2, &tm.tm_sec) != 0) {
goto invalid;
}

zone:
if (!*str) {
goto end;
} else if (*str == 'Z') {
Expand Down Expand Up @@ -296,11 +311,11 @@ int xgetdate(const char *str, struct timespec *result) {
goto error;
}

int offset = 60 * tz_hour + tz_min;
int offset = (tz_hour * 60 + tz_min) * 60;
if (tz_negative) {
result->tv_sec -= offset;
} else {
result->tv_sec += offset;
} else {
result->tv_sec -= offset;
}
}

Expand Down
102 changes: 93 additions & 9 deletions tests/xtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

#include "tests.h"
#include "../src/xtime.h"
#include "../src/bfstd.h"
#include "../src/config.h"
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
Expand Down Expand Up @@ -49,22 +51,92 @@ static void tm_print(FILE *file, const struct tm *tm) {
tm->tm_isdst ? (tm->tm_isdst < 0 ? " (DST?)" : " (DST)") : "");
}

bool check_xtime(void) {
if (setenv("TZ", "UTC0", true) != 0) {
perror("setenv()");
/** Check one xgetdate() result. */
static bool compare_xgetdate(const char *str, int error, time_t expected) {
struct timespec ts;
int ret = xgetdate(str, &ts);

if (error) {
if (ret != -1 || errno != error) {
fprintf(stderr, "xgetdate('%s'): %s\n", str, xstrerror(errno));
return false;
}
} else if (ret != 0) {
fprintf(stderr, "xgetdate('%s'): %s\n", str, xstrerror(errno));
return false;
} else if (ts.tv_sec != expected || ts.tv_nsec != 0) {
fprintf(stderr, "xgetdate('%s'): %jd.%09jd != %jd\n", str, (intmax_t)ts.tv_sec, (intmax_t)ts.tv_nsec, (intmax_t)expected);
return false;
}

return true;
}

/** xgetdate() tests. */
static bool check_xgetdate(void) {
return compare_xgetdate("", EINVAL, 0)
& compare_xgetdate("????", EINVAL, 0)
& compare_xgetdate("1991", EINVAL, 0)
& compare_xgetdate("1991-??", EINVAL, 0)
& compare_xgetdate("1991-12", EINVAL, 0)
& compare_xgetdate("1991-12-", EINVAL, 0)
& compare_xgetdate("1991-12-??", EINVAL, 0)
& compare_xgetdate("1991-12-14", 0, 692668800)
& compare_xgetdate("1991-12-14-", EINVAL, 0)
& compare_xgetdate("1991-12-14T", EINVAL, 0)
& compare_xgetdate("1991-12-14T??", EINVAL, 0)
& compare_xgetdate("1991-12-14T10", 0, 692704800)
& compare_xgetdate("1991-12-14T10:??", EINVAL, 0)
& compare_xgetdate("1991-12-14T10:11", 0, 692705460)
& compare_xgetdate("1991-12-14T10:11:??", EINVAL, 0)
& compare_xgetdate("1991-12-14T10:11:12", 0, 692705472)
& compare_xgetdate("1991-12-14T10Z", 0, 692704800)
& compare_xgetdate("1991-12-14T10:11Z", 0, 692705460)
& compare_xgetdate("1991-12-14T10:11:12Z", 0, 692705472)
& compare_xgetdate("1991-12-14T10:11:12?", EINVAL, 0)
& compare_xgetdate("1991-12-14T02-08", 0, 692704800)
& compare_xgetdate("1991-12-14T06:41-03:30", 0, 692705460)
& compare_xgetdate("1991-12-14T02:11:12-08:00", 0, 692705472)
& compare_xgetdate("19911214 021112-0800", 0, 692705472);
}

/** xmktime() tests. */
static bool check_xmktime(void) {
for (time_t time = -10; time <= 10; ++time) {
struct tm tm;
if (xlocaltime(&time, &tm) != 0) {
perror("xlocaltime()");
return false;
}

time_t made;
if (xmktime(&tm, &made) != 0) {
perror("xmktime()");
return false;
}

if (time != made) {
fprintf(stderr, "Mismatch: %jd != %jd\n", (intmax_t)time, (intmax_t)made);
tm_print(stderr, &tm);
return false;
}
}

return true;
}

/** xtimegm() tests. */
static bool check_xtimegm(void) {
struct tm tm = {
.tm_isdst = -1,
};

for (tm.tm_year = 10; tm.tm_year <= 200; tm.tm_year += 10)
for (tm.tm_mon = -3; tm.tm_mon <= 15; tm.tm_mon += 3)
for (tm.tm_mday = -31; tm.tm_mday <= 61; tm.tm_mday += 4)
for (tm.tm_hour = -1; tm.tm_hour <= 24; tm.tm_hour += 5)
for (tm.tm_min = -1; tm.tm_min <= 60; tm.tm_min += 31)
for (tm.tm_sec = -60; tm.tm_sec <= 120; tm.tm_sec += 5) {
for (tm.tm_year = 10; tm.tm_year <= 200; tm.tm_year += 10)
for (tm.tm_mon = -3; tm.tm_mon <= 15; tm.tm_mon += 3)
for (tm.tm_mday = -31; tm.tm_mday <= 61; tm.tm_mday += 4)
for (tm.tm_hour = -1; tm.tm_hour <= 24; tm.tm_hour += 5)
for (tm.tm_min = -1; tm.tm_min <= 60; tm.tm_min += 31)
for (tm.tm_sec = -60; tm.tm_sec <= 120; tm.tm_sec += 5) {
struct tm tma = tm, tmb = tm;
time_t ta, tb;
ta = mktime(&tma);
Expand Down Expand Up @@ -93,3 +165,15 @@ bool check_xtime(void) {

return true;
}

bool check_xtime(void) {
if (setenv("TZ", "UTC0", true) != 0) {
perror("setenv()");
return false;
}
tzset();

return check_xgetdate()
& check_xmktime()
& check_xtimegm();
}

0 comments on commit a9f3cde

Please sign in to comment.