diff --git a/deps/c-timestamp/.travis.yml b/deps/c-timestamp/.travis.yml new file mode 100644 index 0000000000000000000000000000000000000000..289fc2ba202cd28b08e45692294992b80fe69288 --- /dev/null +++ b/deps/c-timestamp/.travis.yml @@ -0,0 +1,16 @@ +language: perl +install: "perl -V" +before_script: + - "cpanm -n Test::Harness" + - sudo pip install cpp-coveralls --use-mirrors +script: "make test" +after_success: + - "make clean" + - "make gcov" + - coveralls --exclude t +notifications: + recipients: + - chansen@cpan.org + email: + on_success: change + on_failure: always diff --git a/deps/c-timestamp/Makefile b/deps/c-timestamp/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..e4afb3f926fd1c42468768f9b5a654544ff3cdae --- /dev/null +++ b/deps/c-timestamp/Makefile @@ -0,0 +1,85 @@ +CC = cc +GCOV = gcov +CFLAGS = $(DCFLAGS) -Wall -I. -I.. +LDFLAGS += -lc $(DLDFLAGS) + +SOURCES = \ + timestamp_compare.c \ + timestamp_format.c \ + timestamp_parse.c \ + timestamp_valid.c \ + timestamp_tm.c + +OBJECTS = \ + timestamp_compare.o \ + timestamp_format.o \ + timestamp_parse.o \ + timestamp_valid.o \ + timestamp_tm.o + +HARNESS_OBJS = \ + t/valid.o \ + t/compare.o \ + t/format.o \ + t/parse_wellformed.o \ + t/parse_malformed.o \ + t/tm.o + +HARNESS_EXES = \ + t/valid.t \ + t/compare.t \ + t/format.t \ + t/parse_wellformed.t \ + t/parse_malformed.t \ + t/tm.t + +HARNESS_DEPS = \ + $(OBJECTS) \ + t/tap.o + +.SUFFIXES: +.SUFFIXES: .o .c .t + +.PHONY: check-asan test gcov cover clean + +.o.t: + $(CC) $(LDFLAGS) $< $(HARNESS_DEPS) -o $@ + +t/valid.o: \ + $(HARNESS_DEPS) t/valid.c + +t/compare.o: \ + $(HARNESS_DEPS) t/compare.c + +t/format.o: \ + $(HARNESS_DEPS) t/format.c + +t/parse_wellformed.o: \ + $(HARNESS_DEPS) t/parse_wellformed.c + +t/parse_malformed.o: \ + $(HARNESS_DEPS) t/parse_malformed.c + +t/tm.o: \ + $(HARNESS_DEPS) t/tm.c + +test: $(HARNESS_EXES) + @prove $(HARNESS_EXES) + +check-asan: + @$(MAKE) DCFLAGS="-O1 -g -fsanitize=address -fno-omit-frame-pointer" \ + DLDFLAGS="-g -fsanitize=address" test + +gcov: + @$(MAKE) DCFLAGS="-O0 -g -coverage" DLDFLAGS="-coverage" test + @$(GCOV) $(SOURCES) + +cover: + @$(MAKE) DCFLAGS="-O0 -g --coverage" DLDFLAGS="-coverage" test + @$(GCOV) -abc $(SOURCES) + @gcov2perl *.gcov + @cover --no-gcov + +clean: + rm -f $(HARNESS_DEPS) $(HARNESS_OBJS) $(HARNESS_EXES) *.gc{ov,da,no} t/*.gc{ov,da,no} + diff --git a/deps/c-timestamp/README.md b/deps/c-timestamp/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e038b4c238eef254bd424a646cc592997f844ac1 --- /dev/null +++ b/deps/c-timestamp/README.md @@ -0,0 +1,25 @@ +[](https://travis-ci.org/chansen/c-timestamp) [](https://coveralls.io/r/chansen/c-timestamp) + +timestamp +========= + + +```c + +typedef struct { + int64_t sec; /* Number of seconds since the epoch of 1970-01-01T00:00:00Z */ + int32_t nsec; /* Nanoseconds [0, 999999999] */ + int16_t offset; /* Offset from UTC in minutes [-1439, 1439] */ +} timestamp_t; + +int timestamp_parse (const char *str, size_t len, timestamp_t *tsp); +size_t timestamp_format (char *dst, size_t len, const timestamp_t *tsp); +size_t timestamp_format_precision (char *dst, size_t len, const timestamp_t *tsp, int precision); +int timestamp_compare (const timestamp_t *tsp1, const timestamp_t *tsp2); +bool timestamp_valid (const timestamp_t *tsp); +struct tm * timestamp_to_tm_utc (const timestamp_t *tsp, struct tm *tmp); +struct tm * timestamp_to_tm_local (const timestamp_t *tsp, struct tm *tmp); + + +``` + diff --git a/deps/c-timestamp/t/compare.c b/deps/c-timestamp/t/compare.c new file mode 100644 index 0000000000000000000000000000000000000000..85690b5a9f12c86a0d0711526a9c171255f3a161 --- /dev/null +++ b/deps/c-timestamp/t/compare.c @@ -0,0 +1,25 @@ +#include <string.h> +#include "timestamp.h" +#include "tap.h" + +int +main() { + timestamp_t t1, t2; + + t1.sec = t2.sec = 0; + t1.nsec = t2.nsec = 0; + t1.offset = t2.offset = 0; + cmp_ok(timestamp_compare(&t1, &t2), "==", 0, "t1 == t2"); + + t1.sec = 1; + cmp_ok(timestamp_compare(&t1, &t2), ">", 0, "t1 > t2"); + cmp_ok(timestamp_compare(&t2, &t1), "<", 0, "t1 < t2"); + + t1.sec = 0; + t1.nsec = 1; + cmp_ok(timestamp_compare(&t1, &t2), ">", 0, "t1 > t2"); + cmp_ok(timestamp_compare(&t2, &t1), "<", 0, "t2 < t1"); + + done_testing(); +} + diff --git a/deps/c-timestamp/t/format.c b/deps/c-timestamp/t/format.c new file mode 100644 index 0000000000000000000000000000000000000000..b32f22f2ecade8c58545ceeac1e462ce0a491528 --- /dev/null +++ b/deps/c-timestamp/t/format.c @@ -0,0 +1,168 @@ +#include <string.h> +#include "timestamp.h" +#include "tap.h" + +const struct test_t { + timestamp_t ts; + int precision; + const char *exp; +} tests[] = { + { { INT64_C(-62135596800), 0, 0 }, 0, "0001-01-01T00:00:00Z" }, + { { INT64_C(-62135683140), 0, 1439 }, 0, "0001-01-01T00:00:00+23:59" }, + { { INT64_C(-62135510460), 0, -1439 }, 0, "0001-01-01T00:00:00-23:59" }, + { { INT64_C(253402300799), 0, 0 }, 0, "9999-12-31T23:59:59Z" }, + { { INT64_C(253402214459), 0, 1439 }, 0, "9999-12-31T23:59:59+23:59" }, + { { INT64_C(253402387139), 0, -1439 }, 0, "9999-12-31T23:59:59-23:59" }, + { { 0, 0, 0 }, 0, "1970-01-01T00:00:00Z" }, + { { 1, 0, 0 }, 0, "1970-01-01T00:00:01Z" }, + { { 10, 0, 0 }, 0, "1970-01-01T00:00:10Z" }, + { { 60, 0, 0 }, 0, "1970-01-01T00:01:00Z" }, + { { 600, 0, 0 }, 0, "1970-01-01T00:10:00Z" }, + { { 3600, 0, 0 }, 0, "1970-01-01T01:00:00Z" }, + { { 36000, 0, 0 }, 0, "1970-01-01T10:00:00Z" }, + { { 0, 123456789, 0 }, 9, "1970-01-01T00:00:00.123456789Z" }, + { { 0, 123456780, 0 }, 9, "1970-01-01T00:00:00.123456780Z" }, + { { 0, 123456700, 0 }, 9, "1970-01-01T00:00:00.123456700Z" }, + { { 0, 123456000, 0 }, 6, "1970-01-01T00:00:00.123456Z" }, + { { 0, 123450000, 0 }, 6, "1970-01-01T00:00:00.123450Z" }, + { { 0, 123400000, 0 }, 6, "1970-01-01T00:00:00.123400Z" }, + { { 0, 123000000, 0 }, 3, "1970-01-01T00:00:00.123Z" }, + { { 0, 120000000, 0 }, 3, "1970-01-01T00:00:00.120Z" }, + { { 0, 100000000, 0 }, 3, "1970-01-01T00:00:00.100Z" }, + { { 0, 10000000, 0 }, 3, "1970-01-01T00:00:00.010Z" }, + { { 0, 1000000, 0 }, 3, "1970-01-01T00:00:00.001Z" }, + { { 0, 100000, 0 }, 6, "1970-01-01T00:00:00.000100Z" }, + { { 0, 10000, 0 }, 6, "1970-01-01T00:00:00.000010Z" }, + { { 0, 1000, 0 }, 6, "1970-01-01T00:00:00.000001Z" }, + { { 0, 100, 0 }, 9, "1970-01-01T00:00:00.000000100Z" }, + { { 0, 10, 0 }, 9, "1970-01-01T00:00:00.000000010Z" }, + { { 0, 1, 0 }, 9, "1970-01-01T00:00:00.000000001Z" }, + { { 0, 9, 0 }, 9, "1970-01-01T00:00:00.000000009Z" }, + { { 0, 90, 0 }, 9, "1970-01-01T00:00:00.000000090Z" }, + { { 0, 900, 0 }, 9, "1970-01-01T00:00:00.000000900Z" }, + { { 0, 9000, 0 }, 6, "1970-01-01T00:00:00.000009Z" }, + { { 0, 90000, 0 }, 6, "1970-01-01T00:00:00.000090Z" }, + { { 0, 900000, 0 }, 6, "1970-01-01T00:00:00.000900Z" }, + { { 0, 9000000, 0 }, 3, "1970-01-01T00:00:00.009Z" }, + { { 0, 90000000, 0 }, 3, "1970-01-01T00:00:00.090Z" }, + { { 0, 900000000, 0 }, 3, "1970-01-01T00:00:00.900Z" }, + { { 0, 990000000, 0 }, 3, "1970-01-01T00:00:00.990Z" }, + { { 0, 999000000, 0 }, 3, "1970-01-01T00:00:00.999Z" }, + { { 0, 999900000, 0 }, 6, "1970-01-01T00:00:00.999900Z" }, + { { 0, 999990000, 0 }, 6, "1970-01-01T00:00:00.999990Z" }, + { { 0, 999999000, 0 }, 6, "1970-01-01T00:00:00.999999Z" }, + { { 0, 999999900, 0 }, 9, "1970-01-01T00:00:00.999999900Z" }, + { { 0, 999999990, 0 }, 9, "1970-01-01T00:00:00.999999990Z" }, + { { 0, 999999999, 0 }, 9, "1970-01-01T00:00:00.999999999Z" }, + { { 0, 0, 1439 }, 0, "1970-01-01T23:59:00+23:59" }, + { { 0, 0, 120 }, 0, "1970-01-01T02:00:00+02:00" }, + { { 0, 0, 90 }, 0, "1970-01-01T01:30:00+01:30" }, + { { 0, 0, 60 }, 0, "1970-01-01T01:00:00+01:00" }, + { { 0, 0, 1 }, 0, "1970-01-01T00:01:00+00:01" }, + { { 0, 0, -1 }, 0, "1969-12-31T23:59:00-00:01" }, + { { 0, 0, -60 }, 0, "1969-12-31T23:00:00-01:00" }, + { { 0, 0, -90 }, 0, "1969-12-31T22:30:00-01:30" }, + { { 0, 0, -120 }, 0, "1969-12-31T22:00:00-02:00" }, + { { 0, 0, -1439 }, 0, "1969-12-31T00:01:00-23:59" }, + { { 951782400, 0, 0 }, 0, "2000-02-29T00:00:00Z" }, + { { 1078012800, 0, 0 }, 0, "2004-02-29T00:00:00Z" }, +}; + +int +main() { + int i, ntests; + char buf[40]; + timestamp_t ts; + int n; + + ntests = sizeof(tests) / sizeof(*tests); + for (i = 0; i < ntests; i++) { + const struct test_t t = tests[i]; + + n = (int)timestamp_format(buf, sizeof(buf), &t.ts); + cmp_ok(n, "==", strlen(t.exp), "timestamp_format() (exp: \"%s\")", t.exp); + is(buf, t.exp); + + n = (int)timestamp_format_precision(buf, sizeof(buf), &t.ts, t.precision); + cmp_ok(n, "==", strlen(t.exp), "timestamp_format_precision(%d) (exp: \"%s\")", t.precision, t.exp); + is(buf, t.exp); + } + + { + ts.sec = 0; + ts.nsec = 0; + ts.offset = 0; + n = (int)timestamp_format_precision(buf, sizeof(buf), &ts, 9); + cmp_ok(n, "==", 30); + is(buf, "1970-01-01T00:00:00.000000000Z"); + + n = (int)timestamp_format_precision(buf, sizeof(buf), &ts, 6); + cmp_ok(n, "==", 27); + is(buf, "1970-01-01T00:00:00.000000Z"); + + n = (int)timestamp_format_precision(buf, sizeof(buf), &ts, 2); + cmp_ok(n, "==", 23); + is(buf, "1970-01-01T00:00:00.00Z"); + + n = (int)timestamp_format_precision(buf, sizeof(buf), &ts, 1); + cmp_ok(n, "==", 22); + is(buf, "1970-01-01T00:00:00.0Z"); + } + + { + ts.sec = 0; + ts.offset = 0; + ts.nsec = -1; + ok(!timestamp_format(buf, sizeof(buf), &ts), "nsec out of range"); + ok(!timestamp_format_precision(buf, sizeof(buf), &ts, 0), "nsec out of range"); + ts.nsec = 1000000000; + ok(!timestamp_format(buf, sizeof(buf), &ts), "nsec out of range"); + ok(!timestamp_format_precision(buf, sizeof(buf), &ts, 0), "nsec out of range"); + ts.nsec = 0; + ts.offset = -23 * 60 - 60; + ok(!timestamp_format(buf, sizeof(buf), &ts), "offset out of range"); + ok(!timestamp_format_precision(buf, sizeof(buf), &ts, 0), "offset out of range"); + ts.offset = +23 * 60 + 60; + ok(!timestamp_format(buf, sizeof(buf), &ts), "offset out of range"); + ok(!timestamp_format_precision(buf, sizeof(buf), &ts, 0), "offset out of range"); + ts.offset = 0; + ts.sec = INT64_C(-62135596801); /* 0000-12-31T23:59:59Z */ + ok(!timestamp_format(buf, sizeof(buf), &ts), "sec out of range"); + ok(!timestamp_format_precision(buf, sizeof(buf), &ts, 0), "sec out of range"); + ts.sec = INT64_C(253402387140); /* 10000-01-01T23:59:00Z */ + ok(!timestamp_format(buf, sizeof(buf), &ts), "sec out of range"); + ok(!timestamp_format_precision(buf, sizeof(buf), &ts, 0), "sec out of range"); + ts.sec = 0; + ts.offset = 0; + ts.nsec = 0; + ok(!timestamp_format_precision(buf, sizeof(buf), &ts, -1), "precision out of range"); + ok(!timestamp_format_precision(buf, sizeof(buf), &ts, 10), "precision out of range"); + } + + /* + * 1 2 3 + * 12345678901234567890123456789012345 (+ null-terminator) + * YYYY-MM-DDThh:mm:ssZ + * YYYY-MM-DDThh:mm:ss±hh:mm + * YYYY-MM-DDThh:mm:ss.123Z + * YYYY-MM-DDThh:mm:ss.123±hh:mm + * YYYY-MM-DDThh:mm:ss.123456Z + * YYYY-MM-DDThh:mm:ss.123456±hh:mm + * YYYY-MM-DDThh:mm:ss.123456789Z + * YYYY-MM-DDThh:mm:ss.123456789±hh:mm + */ + + { + ts.sec = 0; + ts.offset = 0; + ts.nsec = 0; + ok( timestamp_format(buf, 21, &ts), "suffcient buffer size"); + ok(!timestamp_format(buf, 20, &ts), "insufficient buffer size"); + ts.offset = 1; + ok( timestamp_format(buf, 26, &ts), "suffcient buffer size"); + ok(!timestamp_format(buf, 25, &ts), "insufficient buffer size"); + } + + done_testing(); +} + diff --git a/deps/c-timestamp/t/parse_malformed.c b/deps/c-timestamp/t/parse_malformed.c new file mode 100644 index 0000000000000000000000000000000000000000..70b751196d92266367b01271cb95da5f9a1daeeb --- /dev/null +++ b/deps/c-timestamp/t/parse_malformed.c @@ -0,0 +1,61 @@ +#include <string.h> +#include "timestamp.h" +#include "tap.h" + +const struct test_t { + const char *str; +} tests[] = { + { "" }, + { "0000-01-01T00:00:00Z" }, /* Year < 0001 */ + { "0001-00-01T00:00:00Z" }, /* Invalid month */ + { "0001-13-01T00:00:00Z" }, /* Invalid month */ + { "0001-01-32T00:00:00Z" }, /* Invalid day */ + { "2013-02-29T00:00:00Z" }, /* Invalid day */ + { "1970-01-01T24:00:00Z" }, /* Invalid hour */ + { "1970-01-01T23:60:00Z" }, /* Invalid minute */ + { "1970-01-01T23:59:61Z" }, /* Invalid second */ + { "1970-01-01T23:59:59+01" }, /* Invalid zone offset */ + { "1970-01-01T23:59:59+01:" }, /* Invalid zone offset */ + { "1970-01-01T23:59:59+01:0" }, /* Invalid zone offset */ + { "1970-01-01T23:59:59+0100" }, /* Invalid zone offset */ + { "1970-01-01T23:59:59+24:00" }, /* Zone hour > 23 */ + { "1970-01-01T23:59:59+01:60" }, /* Zone minute > 59 */ + { "1970-01-01" }, /* Date only */ + { "1970-01-01T23:59:59" }, /* Zone offset is required */ + { "1970-01-01T23:59:59.123" }, /* Zone offset is required */ + { "1970-01-01X23:59:59Z" }, /* Invalid time designator */ + { "1970:01:01T23-59-59Z" }, /* Invalid separators */ + { "1970-01-01T00:00:00.Z" }, /* Fraction must have at-least one digit */ + { "X970-01-01T00:00:00Z" }, /* Non-digit in component */ + { "1X70-01-01T00:00:00Z" }, /* Non-digit in component */ + { "19X0-01-01T00:00:00Z" }, /* Non-digit in component */ + { "197X-01-01T00:00:00Z" }, /* Non-digit in component */ + { "1970-X1-01T00:00:00Z" }, /* Non-digit in component */ + { "1970-0X-01T00:00:00Z" }, /* Non-digit in component */ + { "1970-00-X1T00:00:00Z" }, /* Non-digit in component */ + { "1970-00-0XT00:00:00Z" }, /* Non-digit in component */ + { "1970-01-01T0X:00:00Z" }, /* Non-digit in component */ + { "1970-01-01T00:0X:00Z" }, /* Non-digit in component */ + { "1970-01-01T00:00:0XZ" }, /* Non-digit in component */ + { "1970-01-01T00:00:00.12345X7890Z" }, /* Non-digit in component */ + { "1970-01-01T00:00:00.1234567890Z" }, /* Fraction > 9 digits */ + { "1970-01-01T00:00:00,123456789Z" }, /* Decimal sign must be full stop */ + { "1970-01-01T00:00:00Z " }, /* Trailing space */ +}; + +int +main() { + int i, ntests; + + ntests = sizeof(tests) / sizeof(*tests); + for (i = 0; i < ntests; i++) { + const struct test_t t = tests[i]; + timestamp_t ts; + int ret; + + ret = timestamp_parse(t.str, strlen(t.str), &ts); + cmp_ok(ret, "==", 1, "timestamp_parse(\"%s\")", t.str); + } + done_testing(); +} + diff --git a/deps/c-timestamp/t/parse_wellformed.c b/deps/c-timestamp/t/parse_wellformed.c new file mode 100644 index 0000000000000000000000000000000000000000..4c48db1922f6516d1750ec41efe25807c89d5acc --- /dev/null +++ b/deps/c-timestamp/t/parse_wellformed.c @@ -0,0 +1,101 @@ +#include <string.h> +#include "timestamp.h" +#include "tap.h" + +const struct test_t { + timestamp_t exp; + const char *str; +} tests[] = { + { { INT64_C(-62135596800), 0, 0 }, "0001-01-01T00:00:00Z" }, + { { INT64_C(-62135683140), 0, 1439 }, "0001-01-01T00:00:00+23:59" }, + { { INT64_C(-62135510460), 0, -1439 }, "0001-01-01T00:00:00-23:59" }, + { { INT64_C(253402300799), 0, 0 }, "9999-12-31T23:59:59Z" }, + { { INT64_C(253402214459), 0, 1439 }, "9999-12-31T23:59:59+23:59" }, + { { INT64_C(253402387139), 0, -1439 }, "9999-12-31T23:59:59-23:59" }, + { { 0, 0, 0 }, "1970-01-01T00:00:00Z" }, + { { 1, 0, 0 }, "1970-01-01T00:00:01Z" }, + { { 10, 0, 0 }, "1970-01-01T00:00:10Z" }, + { { 60, 0, 0 }, "1970-01-01T00:01:00Z" }, + { { 600, 0, 0 }, "1970-01-01T00:10:00Z" }, + { { 3600, 0, 0 }, "1970-01-01T01:00:00Z" }, + { { 36000, 0, 0 }, "1970-01-01T10:00:00Z" }, + { { 68169600, 0, 0 }, "1972-02-29T00:00:00Z" }, + { { 0, 123456789, 0 }, "1970-01-01T00:00:00.123456789Z" }, + { { 0, 123456780, 0 }, "1970-01-01T00:00:00.12345678Z" }, + { { 0, 123456700, 0 }, "1970-01-01T00:00:00.1234567Z" }, + { { 0, 123456000, 0 }, "1970-01-01T00:00:00.123456Z" }, + { { 0, 123450000, 0 }, "1970-01-01T00:00:00.12345Z" }, + { { 0, 123400000, 0 }, "1970-01-01T00:00:00.1234Z" }, + { { 0, 123000000, 0 }, "1970-01-01T00:00:00.123Z" }, + { { 0, 120000000, 0 }, "1970-01-01T00:00:00.12Z" }, + { { 0, 100000000, 0 }, "1970-01-01T00:00:00.1Z" }, + { { 0, 10000000, 0 }, "1970-01-01T00:00:00.01Z" }, + { { 0, 1000000, 0 }, "1970-01-01T00:00:00.001Z" }, + { { 0, 100000, 0 }, "1970-01-01T00:00:00.0001Z" }, + { { 0, 10000, 0 }, "1970-01-01T00:00:00.00001Z" }, + { { 0, 1000, 0 }, "1970-01-01T00:00:00.000001Z" }, + { { 0, 100, 0 }, "1970-01-01T00:00:00.0000001Z" }, + { { 0, 10, 0 }, "1970-01-01T00:00:00.00000001Z" }, + { { 0, 1, 0 }, "1970-01-01T00:00:00.000000001Z" }, + { { 0, 9, 0 }, "1970-01-01T00:00:00.000000009Z" }, + { { 0, 90, 0 }, "1970-01-01T00:00:00.00000009Z" }, + { { 0, 900, 0 }, "1970-01-01T00:00:00.0000009Z" }, + { { 0, 9000, 0 }, "1970-01-01T00:00:00.000009Z" }, + { { 0, 90000, 0 }, "1970-01-01T00:00:00.00009Z" }, + { { 0, 900000, 0 }, "1970-01-01T00:00:00.0009Z" }, + { { 0, 9000000, 0 }, "1970-01-01T00:00:00.009Z" }, + { { 0, 90000000, 0 }, "1970-01-01T00:00:00.09Z" }, + { { 0, 900000000, 0 }, "1970-01-01T00:00:00.9Z" }, + { { 0, 990000000, 0 }, "1970-01-01T00:00:00.99Z" }, + { { 0, 999000000, 0 }, "1970-01-01T00:00:00.999Z" }, + { { 0, 999900000, 0 }, "1970-01-01T00:00:00.9999Z" }, + { { 0, 999990000, 0 }, "1970-01-01T00:00:00.99999Z" }, + { { 0, 999999000, 0 }, "1970-01-01T00:00:00.999999Z" }, + { { 0, 999999900, 0 }, "1970-01-01T00:00:00.9999999Z" }, + { { 0, 999999990, 0 }, "1970-01-01T00:00:00.99999999Z" }, + { { 0, 999999999, 0 }, "1970-01-01T00:00:00.999999999Z" }, + { { 0, 0, 0 }, "1970-01-01T00:00:00.0Z" }, + { { 0, 0, 0 }, "1970-01-01T00:00:00.00Z" }, + { { 0, 0, 0 }, "1970-01-01T00:00:00.000Z" }, + { { 0, 0, 0 }, "1970-01-01T00:00:00.0000Z" }, + { { 0, 0, 0 }, "1970-01-01T00:00:00.00000Z" }, + { { 0, 0, 0 }, "1970-01-01T00:00:00.000000Z" }, + { { 0, 0, 0 }, "1970-01-01T00:00:00.0000000Z" }, + { { 0, 0, 0 }, "1970-01-01T00:00:00.00000000Z" }, + { { 0, 0, 0 }, "1970-01-01T00:00:00.000000000Z" }, + { { 0, 0, 1439 }, "1970-01-01T23:59:00+23:59" }, + { { 0, 0, 120 }, "1970-01-01T02:00:00+02:00" }, + { { 0, 0, 90 }, "1970-01-01T01:30:00+01:30" }, + { { 0, 0, 60 }, "1970-01-01T01:00:00+01:00" }, + { { 0, 0, 1 }, "1970-01-01T00:01:00+00:01" }, + { { 0, 0, 0 }, "1970-01-01T00:00:00+00:00" }, + { { 0, 0, -1 }, "1969-12-31T23:59:00-00:01" }, + { { 0, 0, -60 }, "1969-12-31T23:00:00-01:00" }, + { { 0, 0, -90 }, "1969-12-31T22:30:00-01:30" }, + { { 0, 0, -120 }, "1969-12-31T22:00:00-02:00" }, + { { 0, 0, -1439 }, "1969-12-31T00:01:00-23:59" }, + { { 0, 0, 0 }, "1970-01-01T00:00:00z" }, + { { 0, 0, 0 }, "1970-01-01 00:00:00Z" }, + { { 0, 0, 0 }, "1970-01-01t00:00:00Z" }, + { { 0, 0, 0 }, "1970-01-01 00:00:00+00:00" }, +}; + +int +main() { + int i, ntests; + + ntests = sizeof(tests) / sizeof(*tests); + for (i = 0; i < ntests; i++) { + const struct test_t t = tests[i]; + timestamp_t ts; + int ret; + + ret = timestamp_parse(t.str, strlen(t.str), &ts); + cmp_ok(ret, "==", 0, "timestamp_parse(\"%s\")", t.str); + ret = timestamp_compare(&ts, &t.exp); + cmp_ok(ret, "==", 0, "timestamp_compare(\"%s\")", t.str); + cmp_ok(ts.offset, "==", t.exp.offset, "offset (%s)", t.str); + } + done_testing(); +} + diff --git a/deps/c-timestamp/t/tap.c b/deps/c-timestamp/t/tap.c new file mode 100644 index 0000000000000000000000000000000000000000..57b80389fcffdbd5de0a0acda09d23e1d20646a7 --- /dev/null +++ b/deps/c-timestamp/t/tap.c @@ -0,0 +1,325 @@ +/* +libtap - Write tests in C +Copyright (C) 2011 Jake Gelbman <gelbman@gmail.com> +This file is licensed under the GPL v3 +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include "tap.h" + +static int expected_tests = NO_PLAN; +static int failed_tests; +static int current_test; +static char *todo_mesg; + +static char * +vstrdupf (const char *fmt, va_list args) { + char *str; + int size; + va_list args2; + va_copy(args2, args); + if (!fmt) + fmt = ""; + size = vsnprintf(NULL, 0, fmt, args2) + 2; + str = (char *)malloc(size); + vsprintf(str, fmt, args); + va_end(args2); + return str; +} + +void +cplan (int tests, const char *fmt, ...) { + expected_tests = tests; + if (tests == SKIP_ALL) { + char *why; + va_list args; + va_start(args, fmt); + why = vstrdupf(fmt, args); + va_end(args); + printf("1..0 "); + note("SKIP %s\n", why); + exit(0); + } + if (tests != NO_PLAN) { + printf("1..%d\n", tests); + } +} + +int +vok_at_loc (const char *file, int line, int test, const char *fmt, + va_list args) +{ + char *name = vstrdupf(fmt, args); + printf("%sok %d", test ? "" : "not ", ++current_test); + if (*name) + printf(" - %s", name); + if (todo_mesg) { + printf(" # TODO"); + if (*todo_mesg) + printf(" %s", todo_mesg); + } + printf("\n"); + if (!test) { + if (*name) + diag(" Failed%s test '%s'\n at %s line %d.", + todo_mesg ? " (TODO)" : "", name, file, line); + else + diag(" Failed%s test at %s line %d.", + todo_mesg ? " (TODO)" : "", file, line); + if (!todo_mesg) + failed_tests++; + } + free(name); + return test; +} + +int +ok_at_loc (const char *file, int line, int test, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + vok_at_loc(file, line, test, fmt, args); + va_end(args); + return test; +} + +static int +mystrcmp (const char *a, const char *b) { + return a == b ? 0 : !a ? -1 : !b ? 1 : strcmp(a, b); +} + +#define eq(a, b) (!mystrcmp(a, b)) +#define ne(a, b) (mystrcmp(a, b)) + +int +is_at_loc (const char *file, int line, const char *got, const char *expected, + const char *fmt, ...) +{ + int test = eq(got, expected); + va_list args; + va_start(args, fmt); + vok_at_loc(file, line, test, fmt, args); + va_end(args); + if (!test) { + diag(" got: '%s'", got); + diag(" expected: '%s'", expected); + } + return test; +} + +int +isnt_at_loc (const char *file, int line, const char *got, const char *expected, + const char *fmt, ...) +{ + int test = ne(got, expected); + va_list args; + va_start(args, fmt); + vok_at_loc(file, line, test, fmt, args); + va_end(args); + if (!test) { + diag(" got: '%s'", got); + diag(" expected: anything else"); + } + return test; +} + +int +cmp_ok_at_loc (const char *file, int line, int a, const char *op, int b, + const char *fmt, ...) +{ + int test = eq(op, "||") ? a || b + : eq(op, "&&") ? a && b + : eq(op, "|") ? a | b + : eq(op, "^") ? a ^ b + : eq(op, "&") ? a & b + : eq(op, "==") ? a == b + : eq(op, "!=") ? a != b + : eq(op, "<") ? a < b + : eq(op, ">") ? a > b + : eq(op, "<=") ? a <= b + : eq(op, ">=") ? a >= b + : eq(op, "<<") ? a << b + : eq(op, ">>") ? a >> b + : eq(op, "+") ? a + b + : eq(op, "-") ? a - b + : eq(op, "*") ? a * b + : eq(op, "/") ? a / b + : eq(op, "%") ? a % b + : diag("unrecognized operator '%s'", op); + va_list args; + va_start(args, fmt); + vok_at_loc(file, line, test, fmt, args); + va_end(args); + if (!test) { + diag(" %d", a); + diag(" %s", op); + diag(" %d", b); + } + return test; +} + +static void +vdiag_to_fh (FILE *fh, const char *fmt, va_list args) { + char *mesg, *line; + int i; + if (!fmt) + return; + mesg = vstrdupf(fmt, args); + line = mesg; + for (i = 0; *line; i++) { + char c = mesg[i]; + if (!c || c == '\n') { + mesg[i] = '\0'; + fprintf(fh, "# %s\n", line); + if (!c) + break; + mesg[i] = c; + line = mesg + i + 1; + } + } + free(mesg); + return; +} + +int +diag (const char *fmt, ...) { + va_list args; + va_start(args, fmt); + vdiag_to_fh(stderr, fmt, args); + va_end(args); + return 0; +} + +int +note (const char *fmt, ...) { + va_list args; + va_start(args, fmt); + vdiag_to_fh(stdout, fmt, args); + va_end(args); + return 0; +} + +int +exit_status () { + int retval = 0; + if (expected_tests == NO_PLAN) { + printf("1..%d\n", current_test); + } + else if (current_test != expected_tests) { + diag("Looks like you planned %d test%s but ran %d.", + expected_tests, expected_tests > 1 ? "s" : "", current_test); + retval = 255; + } + if (failed_tests) { + diag("Looks like you failed %d test%s of %d run.", + failed_tests, failed_tests > 1 ? "s" : "", current_test); + if (expected_tests == NO_PLAN) + retval = failed_tests; + else + retval = expected_tests - current_test + failed_tests; + } + return retval; +} + +int +bail_out (int ignore, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + printf("Bail out! "); + vprintf(fmt, args); + printf("\n"); + va_end(args); + exit(255); + return 0; +} + +void +skippy (int n, const char *fmt, ...) { + char *why; + va_list args; + va_start(args, fmt); + why = vstrdupf(fmt, args); + va_end(args); + while (n --> 0) { + printf("ok %d ", ++current_test); + note("skip %s\n", why); + } + free(why); +} + +void +ctodo (int ignore, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + todo_mesg = vstrdupf(fmt, args); + va_end(args); +} + +void +cendtodo () { + free(todo_mesg); + todo_mesg = NULL; +} + +#ifndef _WIN32 +#include <sys/mman.h> +#include <sys/param.h> +#include <regex.h> + +#if defined __APPLE__ || defined BSD +#define MAP_ANONYMOUS MAP_ANON +#endif + +/* Create a shared memory int to keep track of whether a piece of code executed +dies. to be used in the dies_ok and lives_ok macros */ +int +tap_test_died (int status) { + static int *test_died = NULL; + int prev; + if (!test_died) { + test_died = (int *)mmap(0, sizeof (int), PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + *test_died = 0; + } + prev = *test_died; + *test_died = status; + return prev; +} + +int +like_at_loc (int for_match, const char *file, int line, const char *got, + const char *expected, const char *fmt, ...) +{ + int test; + regex_t re; + va_list args; + int err = regcomp(&re, expected, REG_EXTENDED); + if (err) { + char errbuf[256]; + regerror(err, &re, errbuf, sizeof errbuf); + fprintf(stderr, "Unable to compile regex '%s': %s at %s line %d\n", + expected, errbuf, file, line); + exit(255); + } + err = regexec(&re, got, 0, NULL, 0); + regfree(&re); + test = for_match ? !err : err; + va_start(args, fmt); + vok_at_loc(file, line, test, fmt, args); + va_end(args); + if (!test) { + if (for_match) { + diag(" '%s'", got); + diag(" doesn't match: '%s'", expected); + } + else { + diag(" '%s'", got); + diag(" matches: '%s'", expected); + } + } + return test; +} +#endif + diff --git a/deps/c-timestamp/t/tap.h b/deps/c-timestamp/t/tap.h new file mode 100644 index 0000000000000000000000000000000000000000..89484f477fd88d4e0b5e63a5d05e7f8369553b0a --- /dev/null +++ b/deps/c-timestamp/t/tap.h @@ -0,0 +1,114 @@ +/* +libtap - Write tests in C +Copyright (C) 2011 Jake Gelbman <gelbman@gmail.com> +This file is licensed under the GPL v3 +*/ + +#ifndef __TAP_H__ +#define __TAP_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef va_copy +#ifdef __va_copy +#define va_copy __va_copy +#else +#define va_copy(d, s) ((d) = (s)) +#endif +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> + +int vok_at_loc (const char *file, int line, int test, const char *fmt, + va_list args); +int ok_at_loc (const char *file, int line, int test, const char *fmt, + ...); +int is_at_loc (const char *file, int line, const char *got, + const char *expected, const char *fmt, ...); +int isnt_at_loc (const char *file, int line, const char *got, + const char *expected, const char *fmt, ...); +int cmp_ok_at_loc (const char *file, int line, int a, const char *op, + int b, const char *fmt, ...); +int bail_out (int ignore, const char *fmt, ...); +void cplan (int tests, const char *fmt, ...); +int diag (const char *fmt, ...); +int note (const char *fmt, ...); +int exit_status (void); +void skippy (int n, const char *fmt, ...); +void ctodo (int ignore, const char *fmt, ...); +void cendtodo (void); + +#define NO_PLAN -1 +#define SKIP_ALL -2 +#define ok(...) ok_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL) +#define is(...) is_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL) +#define isnt(...) isnt_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL) +#define cmp_ok(...) cmp_ok_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL) +#define plan(...) cplan(__VA_ARGS__, NULL) +#define done_testing() return exit_status() +#define BAIL_OUT(...) bail_out(0, "" __VA_ARGS__, NULL) +#define pass(...) ok(1, "" __VA_ARGS__) +#define fail(...) ok(0, "" __VA_ARGS__) + +#define skip(test, ...) do {if (test) {skippy(__VA_ARGS__, NULL); break;} +#define endskip } while (0) + +#define todo(...) ctodo(0, "" __VA_ARGS__, NULL) +#define endtodo cendtodo() + +#define dies_ok(...) dies_ok_common(1, __VA_ARGS__) +#define lives_ok(...) dies_ok_common(0, __VA_ARGS__) + +#ifdef _WIN32 +#define like(...) skippy(1, "like is not implemented on MSWin32") +#define unlike like +#define dies_ok_common(...) \ + skippy(1, "Death detection is not supported on MSWin32") +#else +#define like(...) like_at_loc(1, __FILE__, __LINE__, __VA_ARGS__, NULL) +#define unlike(...) like_at_loc(0, __FILE__, __LINE__, __VA_ARGS__, NULL) +int like_at_loc (int for_match, const char *file, int line, + const char *got, const char *expected, + const char *fmt, ...); +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> +int tap_test_died (int status); +#define dies_ok_common(for_death, code, ...) \ + do { \ + int cpid; \ + int it_died; \ + tap_test_died(1); \ + cpid = fork(); \ + switch (cpid) { \ + case -1: \ + perror("fork error"); \ + exit(1); \ + case 0: \ + close(1); \ + close(2); \ + code \ + tap_test_died(0); \ + exit(0); \ + } \ + if (waitpid(cpid, NULL, 0) < 0) { \ + perror("waitpid error"); \ + exit(1); \ + } \ + it_died = tap_test_died(0); \ + if (!it_died) \ + {code} \ + ok(for_death ? it_died : !it_died, "" __VA_ARGS__); \ + } while (0) +#endif + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/deps/c-timestamp/t/tm.c b/deps/c-timestamp/t/tm.c new file mode 100644 index 0000000000000000000000000000000000000000..89f317ba81f07acca2c6113f99177f68056992c8 --- /dev/null +++ b/deps/c-timestamp/t/tm.c @@ -0,0 +1,63 @@ +#include <string.h> +#include "timestamp.h" +#include "tap.h" + +int +main() { + timestamp_t ts; + struct tm tm; + + { /* 0001-01-01T12:30:45Z */ + ts.sec = INT64_C(-62135551755); + ts.nsec = 0; + ts.offset = 0; + + memset(&tm, 0, sizeof(tm)); + ok(timestamp_to_tm_utc(&ts, &tm) != NULL); + cmp_ok(tm.tm_year, "==", -1899, "tm_year"); + cmp_ok(tm.tm_mon, "==", 0, "tm_mon"); + cmp_ok(tm.tm_mday, "==", 1, "tm_mday"); + cmp_ok(tm.tm_yday, "==", 0, "tm_yday"); + cmp_ok(tm.tm_wday, "==", 1, "tm_wday"); + cmp_ok(tm.tm_hour, "==", 12, "tm_hour"); + cmp_ok(tm.tm_min, "==", 30, "tm_min"); + cmp_ok(tm.tm_sec, "==", 45, "tm_sec"); + } + + { /* 0001-01-01T12:30:45+02:00 */ + ts.sec = INT64_C(-62135558955); + ts.nsec = 0; + ts.offset = 120; + + memset(&tm, 0, sizeof(tm)); + ok(timestamp_to_tm_local(&ts, &tm) != NULL); + cmp_ok(tm.tm_year, "==", -1899, "tm_year"); + cmp_ok(tm.tm_mon, "==", 0, "tm_mon"); + cmp_ok(tm.tm_mday, "==", 1, "tm_mday"); + cmp_ok(tm.tm_yday, "==", 0, "tm_yday"); + cmp_ok(tm.tm_wday, "==", 1, "tm_wday"); + cmp_ok(tm.tm_hour, "==", 12, "tm_hour"); + cmp_ok(tm.tm_min, "==", 30, "tm_min"); + cmp_ok(tm.tm_sec, "==", 45, "tm_sec"); + } + + { /* 1970-12-31T23:59:59Z */ + ts.sec = INT64_C(31535999); + ts.nsec = 0; + ts.offset = 0; + + memset(&tm, 0, sizeof(tm)); + ok(timestamp_to_tm_utc(&ts, &tm) != NULL); + cmp_ok(tm.tm_year, "==", 70, "tm_year"); + cmp_ok(tm.tm_mon, "==", 11, "tm_mon"); + cmp_ok(tm.tm_mday, "==", 31, "tm_mday"); + cmp_ok(tm.tm_yday, "==", 364, "tm_yday"); + cmp_ok(tm.tm_wday, "==", 4, "tm_wday"); + cmp_ok(tm.tm_hour, "==", 23, "tm_hour"); + cmp_ok(tm.tm_min, "==", 59, "tm_min"); + cmp_ok(tm.tm_sec, "==", 59, "tm_sec"); + } + + done_testing(); +} + diff --git a/deps/c-timestamp/t/valid.c b/deps/c-timestamp/t/valid.c new file mode 100644 index 0000000000000000000000000000000000000000..2aaae0f930f59107c400da933077c89a94fb9a63 --- /dev/null +++ b/deps/c-timestamp/t/valid.c @@ -0,0 +1,32 @@ +#include <string.h> +#include "timestamp.h" +#include "tap.h" + +int +main() { + timestamp_t ts; + + ts.sec = 0; + ts.offset = 0; + ts.nsec = -1; + ok(!timestamp_valid(&ts), "nsec out of range"); + + ts.nsec = 1000000000; + ok(!timestamp_valid(&ts), "nsec out of range"); + + ts.nsec = 0; + ts.offset = -23 * 60 - 60; + ok(!timestamp_valid(&ts), "offset out of range"); + + ts.offset = +23 * 60 + 60; + ok(!timestamp_valid(&ts), "offset out of range"); + + ts.offset = 0; + ts.sec = INT64_C(-62135596801); /* 0000-12-31T23:59:59Z */ + ok(!timestamp_valid(&ts), "sec out of range"); + ts.sec = INT64_C(253402387140); /* 10000-01-01T23:59:00Z */ + ok(!timestamp_valid(&ts), "sec out of range"); + + done_testing(); +} + diff --git a/deps/c-timestamp/timestamp.h b/deps/c-timestamp/timestamp.h new file mode 100644 index 0000000000000000000000000000000000000000..b52277a468ca60eaff00eb052aa97495f957a649 --- /dev/null +++ b/deps/c-timestamp/timestamp.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2014 Christian Hansen <chansen@cpan.org> + * <https://github.com/chansen/c-timestamp> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __TIMESTAMP_H__ +#define __TIMESTAMP_H__ +#include <stddef.h> +#include <time.h> +#include <stdint.h> +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int64_t sec; /* Number of seconds since the epoch of 1970-01-01T00:00:00Z */ + int32_t nsec; /* Nanoseconds [0, 999999999] */ + int16_t offset; /* Offset from UTC in minutes [-1439, 1439] */ +} timestamp_t; + +int timestamp_parse (const char *str, size_t len, timestamp_t *tsp); +size_t timestamp_format (char *dst, size_t len, const timestamp_t *tsp); +size_t timestamp_format_precision (char *dst, size_t len, const timestamp_t *tsp, int precision); +int timestamp_compare (const timestamp_t *tsp1, const timestamp_t *tsp2); +bool timestamp_valid (const timestamp_t *tsp); +struct tm * timestamp_to_tm_utc (const timestamp_t *tsp, struct tm *tmp); +struct tm * timestamp_to_tm_local (const timestamp_t *tsp, struct tm *tmp); + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/deps/c-timestamp/timestamp_compare.c b/deps/c-timestamp/timestamp_compare.c new file mode 100644 index 0000000000000000000000000000000000000000..89c8b72251519e6f708aee71dec0b1508201c9bd --- /dev/null +++ b/deps/c-timestamp/timestamp_compare.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014 Christian Hansen <chansen@cpan.org> + * <https://github.com/chansen/c-timestamp> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "timestamp.h" + +int +timestamp_compare(const timestamp_t *t1, const timestamp_t *t2) { + if (t1->sec < t2->sec) + return -1; + if (t1->sec > t2->sec) + return 1; + if (t1->nsec < t2->nsec) + return -1; + if (t1->nsec > t2->nsec) + return 1; + return 0; +} + diff --git a/deps/c-timestamp/timestamp_format.c b/deps/c-timestamp/timestamp_format.c new file mode 100644 index 0000000000000000000000000000000000000000..570663c008536c1c18cad6baf704d40b48500247 --- /dev/null +++ b/deps/c-timestamp/timestamp_format.c @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2014 Christian Hansen <chansen@cpan.org> + * <https://github.com/chansen/c-timestamp> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <stddef.h> +#include "timestamp.h" + +static const uint16_t DayOffset[13] = { + 0, 306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275 +}; + +/* Rata Die algorithm by Peter Baum */ + +static void +rdn_to_ymd(uint32_t rdn, uint16_t *yp, uint16_t *mp, uint16_t *dp) { + uint32_t Z, H, A, B; + uint16_t y, m, d; + + Z = rdn + 306; + H = 100 * Z - 25; + A = H / 3652425; + B = A - (A >> 2); + y = (100 * B + H) / 36525; + d = B + Z - (1461 * y >> 2); + m = (535 * d + 48950) >> 14; + if (m > 12) + y++, m -= 12; + + *yp = y; + *mp = m; + *dp = d - DayOffset[m]; +} + +#define EPOCH INT64_C(62135683200) /* 1970-01-01T00:00:00 */ + +static const uint32_t Pow10[10] = { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 +}; + +static size_t +timestamp_format_internal(char *dst, size_t len, const timestamp_t *tsp, const int precision) { + unsigned char *p; + uint64_t sec; + uint32_t rdn, v; + uint16_t y, m, d; + size_t dlen; + + dlen = sizeof("YYYY-MM-DDThh:mm:ssZ") - 1; + if (tsp->offset) + dlen += 5; /* hh:mm */ + + if (precision) + dlen += 1 + precision; + + if (dlen >= len) + return 0; + + sec = tsp->sec + tsp->offset * 60 + EPOCH; + rdn = sec / 86400; + + rdn_to_ymd(rdn, &y, &m, &d); + + /* + * 1 + * 0123456789012345678 + * YYYY-MM-DDThh:mm:ss + */ + p = (unsigned char *)dst; + v = sec % 86400; + p[18] = '0' + (v % 10); v /= 10; + p[17] = '0' + (v % 6); v /= 6; + p[16] = ':'; + p[15] = '0' + (v % 10); v /= 10; + p[14] = '0' + (v % 6); v /= 6; + p[13] = ':'; + p[12] = '0' + (v % 10); v /= 10; + p[11] = '0' + (v % 10); + p[10] = 'T'; + p[ 9] = '0' + (d % 10); d /= 10; + p[ 8] = '0' + (d % 10); + p[ 7] = '-'; + p[ 6] = '0' + (m % 10); m /= 10; + p[ 5] = '0' + (m % 10); + p[ 4] = '-'; + p[ 3] = '0' + (y % 10); y /= 10; + p[ 2] = '0' + (y % 10); y /= 10; + p[ 1] = '0' + (y % 10); y /= 10; + p[ 0] = '0' + (y % 10); + p += 19; + + if (precision) { + v = tsp->nsec / Pow10[9 - precision]; + switch (precision) { + case 9: p[9] = '0' + (v % 10); v /= 10; + case 8: p[8] = '0' + (v % 10); v /= 10; + case 7: p[7] = '0' + (v % 10); v /= 10; + case 6: p[6] = '0' + (v % 10); v /= 10; + case 5: p[5] = '0' + (v % 10); v /= 10; + case 4: p[4] = '0' + (v % 10); v /= 10; + case 3: p[3] = '0' + (v % 10); v /= 10; + case 2: p[2] = '0' + (v % 10); v /= 10; + case 1: p[1] = '0' + (v % 10); + } + p[0] = '.'; + p += 1 + precision; + } + + if (!tsp->offset) + *p++ = 'Z'; + else { + if (tsp->offset < 0) + p[0] = '-', v = -tsp->offset; + else + p[0] = '+', v = tsp->offset; + + p[5] = '0' + (v % 10); v /= 10; + p[4] = '0' + (v % 6); v /= 6; + p[3] = ':'; + p[2] = '0' + (v % 10); v /= 10; + p[1] = '0' + (v % 10); + p += 6; + } + *p = 0; + return dlen; +} + +/* + * 1 2 3 + * 12345678901234567890123456789012345 (+ null-terminator) + * YYYY-MM-DDThh:mm:ssZ + * YYYY-MM-DDThh:mm:ss±hh:mm + * YYYY-MM-DDThh:mm:ss.123Z + * YYYY-MM-DDThh:mm:ss.123±hh:mm + * YYYY-MM-DDThh:mm:ss.123456Z + * YYYY-MM-DDThh:mm:ss.123456±hh:mm + * YYYY-MM-DDThh:mm:ss.123456789Z + * YYYY-MM-DDThh:mm:ss.123456789±hh:mm + */ + +size_t +timestamp_format(char *dst, size_t len, const timestamp_t *tsp) { + uint32_t f; + int precision; + + if (!timestamp_valid(tsp)) + return 0; + + f = tsp->nsec; + if (!f) + precision = 0; + else { + if ((f % 1000000) == 0) precision = 3; + else if ((f % 1000) == 0) precision = 6; + else precision = 9; + } + return timestamp_format_internal(dst, len, tsp, precision); +} + +size_t +timestamp_format_precision(char *dst, size_t len, const timestamp_t *tsp, int precision) { + if (!timestamp_valid(tsp) || precision < 0 || precision > 9) + return 0; + return timestamp_format_internal(dst, len, tsp, precision); +} + diff --git a/deps/c-timestamp/timestamp_parse.c b/deps/c-timestamp/timestamp_parse.c new file mode 100644 index 0000000000000000000000000000000000000000..37251b384eefaf06901eb1599b8d347b7d4faab1 --- /dev/null +++ b/deps/c-timestamp/timestamp_parse.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2014 Christian Hansen <chansen@cpan.org> + * <https://github.com/chansen/c-timestamp> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <stddef.h> +#include "timestamp.h" + +static int +leap_year(uint16_t y) { + return ((y & 3) == 0 && (y % 100 != 0 || y % 400 == 0)); +} + +static unsigned char +month_days(uint16_t y, uint16_t m) { + static const unsigned char days[2][13] = { + {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} + }; + return days[m == 2 && leap_year(y)][m]; +} + +static int +parse_2d(const unsigned char * const p, size_t i, uint16_t *vp) { + unsigned char d0, d1; + if (((d0 = p[i + 0] - '0') > 9) || + ((d1 = p[i + 1] - '0') > 9)) + return 1; + *vp = d0 * 10 + d1; + return 0; +} + +static int +parse_4d(const unsigned char * const p, size_t i, uint16_t *vp) { + unsigned char d0, d1, d2, d3; + if (((d0 = p[i + 0] - '0') > 9) || + ((d1 = p[i + 1] - '0') > 9) || + ((d2 = p[i + 2] - '0') > 9) || + ((d3 = p[i + 3] - '0') > 9)) + return 1; + *vp = d0 * 1000 + d1 * 100 + d2 * 10 + d3; + return 0; +} + +static const uint32_t Pow10[10] = { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 +}; + +static const uint16_t DayOffset[13] = { + 0, 306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275 +}; + +int +timestamp_parse(const char *str, size_t len, timestamp_t *tsp) { + const unsigned char *cur, *end; + unsigned char ch; + uint16_t year, month, day, hour, min, sec; + uint32_t rdn, sod, nsec; + int16_t offset; + + /* + * 1 + * 01234567890123456789 + * 2013-12-31T23:59:59Z + */ + cur = (const unsigned char *)str; + if (len < 20 || + cur[4] != '-' || cur[7] != '-' || + cur[13] != ':' || cur[16] != ':') + return 1; + + ch = cur[10]; + if (!(ch == 'T' || ch == ' ' || ch == 't')) + return 1; + + if (parse_4d(cur, 0, &year) || year < 1 || + parse_2d(cur, 5, &month) || month < 1 || month > 12 || + parse_2d(cur, 8, &day) || day < 1 || day > 31 || + parse_2d(cur, 11, &hour) || hour > 23 || + parse_2d(cur, 14, &min) || min > 59 || + parse_2d(cur, 17, &sec) || sec > 59) + return 1; + + if (day > 28 && day > month_days(year, month)) + return 1; + + if (month < 3) + year--; + + rdn = (1461 * year)/4 - year/100 + year/400 + DayOffset[month] + day - 306; + sod = hour * 3600 + min * 60 + sec; + end = cur + len; + cur = cur + 19; + offset = nsec = 0; + + ch = *cur++; + if (ch == '.') { + const unsigned char *start; + size_t ndigits; + + start = cur; + for (; cur < end; cur++) { + const unsigned char digit = *cur - '0'; + if (digit > 9) + break; + nsec = nsec * 10 + digit; + } + + ndigits = cur - start; + if (ndigits < 1 || ndigits > 9) + return 1; + + nsec *= Pow10[9 - ndigits]; + + if (cur == end) + return 1; + + ch = *cur++; + } + + if (!(ch == 'Z' || ch == 'z')) { + /* + * 01234 + * ±00:00 + */ + if (cur + 5 < end || !(ch == '+' || ch == '-') || cur[2] != ':') + return 1; + + if (parse_2d(cur, 0, &hour) || hour > 23 || + parse_2d(cur, 3, &min) || min > 59) + return 1; + + offset = hour * 60 + min; + if (ch == '-') + offset *= -1; + + cur += 5; + } + + if (cur != end) + return 1; + + tsp->sec = ((int64_t)rdn - 719163) * 86400 + sod - offset * 60; + tsp->nsec = nsec; + tsp->offset = offset; + return 0; +} + diff --git a/deps/c-timestamp/timestamp_tm.c b/deps/c-timestamp/timestamp_tm.c new file mode 100644 index 0000000000000000000000000000000000000000..3046b730ba49134875611821877b0442bdb2ff42 --- /dev/null +++ b/deps/c-timestamp/timestamp_tm.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2014 Christian Hansen <chansen@cpan.org> + * <https://github.com/chansen/c-timestamp> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <stddef.h> +#include <time.h> +#include "timestamp.h" + +static const uint16_t DayOffset[13] = { + 0, 306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275 +}; + +/* Rata Die algorithm by Peter Baum */ + +static void +rdn_to_struct_tm(uint32_t rdn, struct tm *tmp) { + uint32_t Z, H, A, B; + uint16_t C, y, m, d; + + Z = rdn + 306; + H = 100 * Z - 25; + A = H / 3652425; + B = A - (A >> 2); + y = (100 * B + H) / 36525; + C = B + Z - (1461 * y >> 2); + m = (535 * C + 48950) >> 14; + if (m > 12) + d = C - 306, y++, m -= 12; + else + d = C + 59 + ((y & 3) == 0 && (y % 100 != 0 || y % 400 == 0)); + + tmp->tm_mday = C - DayOffset[m]; /* Day of month [1,31] */ + tmp->tm_mon = m - 1; /* Month of year [0,11] */ + tmp->tm_year = y - 1900; /* Years since 1900 */ + tmp->tm_wday = rdn % 7; /* Day of week [0,6] (Sunday =0) */ + tmp->tm_yday = d - 1; /* Day of year [0,365] */ +} + +#define RDN_OFFSET INT64_C(62135683200) /* 1970-01-01T00:00:00 */ + +static struct tm * +timestamp_to_tm(const timestamp_t *tsp, struct tm *tmp, const bool local) { + uint64_t sec; + uint32_t rdn, sod; + + if (!timestamp_valid(tsp)) + return NULL; + + sec = tsp->sec + RDN_OFFSET; + if (local) + sec += tsp->offset * 60; + rdn = sec / 86400; + sod = sec % 86400; + + rdn_to_struct_tm(rdn, tmp); + tmp->tm_sec = sod % 60; sod /= 60; + tmp->tm_min = sod % 60; sod /= 60; + tmp->tm_hour = sod; + return tmp; +} + +struct tm * +timestamp_to_tm_local(const timestamp_t *tsp, struct tm *tmp) { + return timestamp_to_tm(tsp, tmp, true); +} + +struct tm * +timestamp_to_tm_utc(const timestamp_t *tsp, struct tm *tmp) { + return timestamp_to_tm(tsp, tmp, false); +} + diff --git a/deps/c-timestamp/timestamp_valid.c b/deps/c-timestamp/timestamp_valid.c new file mode 100644 index 0000000000000000000000000000000000000000..44c2648c9f2ce742abbbb37f791be8d9a616d3c6 --- /dev/null +++ b/deps/c-timestamp/timestamp_valid.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014 Christian Hansen <chansen@cpan.org> + * <https://github.com/chansen/c-timestamp> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "timestamp.h" + +#define MIN_SEC INT64_C(-62135596800) /* 0001-01-01T00:00:00 */ +#define MAX_SEC INT64_C(253402300799) /* 9999-12-31T23:59:59 */ + +bool +timestamp_valid(const timestamp_t *tsp) { + const int64_t sec = tsp->sec + tsp->offset * 60; + if (sec < MIN_SEC || sec > MAX_SEC || + tsp->nsec < 0 || tsp->nsec > 999999999 || + tsp->offset < -1439 || tsp->offset > 1439) + return false; + return true; +} +