diff --git a/src/libiso8601/build.monolithic b/src/libiso8601/build.monolithic index b3451a4..99c8327 100644 --- a/src/libiso8601/build.monolithic +++ b/src/libiso8601/build.monolithic @@ -11,7 +11,7 @@ then MONOLITHIC_SOURCE="$(echo src/libiso8601/{TopHeader,types,functions,BottomHeader}.h)" make_monolithic ${HDR} Ch || return 1 - MONOLITHIC_SOURCE="$(echo src/libiso8601/{TopSource,c_library,calc,parser,print,manip,leap}.c)" + MONOLITHIC_SOURCE="$(echo src/libiso8601/{TopSource,leap,c_library,calc,parser,print,manip}.c)" make_monolithic ${SRC} C || return 1 libiso8601_MONOLITHIC=1 diff --git a/src/libiso8601/functions.h b/src/libiso8601/functions.h index a40d262..240ff3f 100644 --- a/src/libiso8601/functions.h +++ b/src/libiso8601/functions.h @@ -67,11 +67,14 @@ void iso8601_print(char* str, int amt, const struct iso8601_date* date, const struct iso8601_details* details); /* manip.c */ +int iso8601_lt(const struct iso8601_date* d1, const struct iso8601_date* d2); +int iso8601_lte(const struct iso8601_date* d1, const struct iso8601_date* d2); void iso8601_add_seconds(struct iso8601_date* date, long seconds); /* leap.c */ int iso8601_seconds_leap(const struct iso8601_date* date); int iso8601_valid_leap(const struct iso8601_date* date); +int iso8601_leap_elapsed(const struct iso8601_date* start, const struct iso8601_date* end); /* options for text editors kate: replace-trailing-space-save true; space-indent true; tab-width 4; diff --git a/src/libiso8601/leap.c b/src/libiso8601/leap.c index e0004db..4ea0bcf 100644 --- a/src/libiso8601/leap.c +++ b/src/libiso8601/leap.c @@ -67,6 +67,29 @@ int iso8601_valid_leap(const struct iso8601_date* date) +static int _leap_elapsed_day(int sday, int eday) +{ + int spos, epos; + + for(spos = 0; (unsigned)spos < sizeof(leap_second_days) / sizeof(int); ++spos) { + if(sday <= leap_second_days[spos]) break; + } + for(epos = 0; (unsigned)epos < sizeof(leap_second_days) / sizeof(int); ++epos) { + if(eday <= leap_second_days[epos]) break; + } + + return spos - epos; +} + + + +int iso8601_leap_elapsed(const struct iso8601_date* start, const struct iso8601_date* end) +{ + return _leap_elapsed_day(start->day, end->day); +} + + + /* options for text editors kate: replace-trailing-space-save true; space-indent true; tab-width 4; vim: expandtab:ts=4:sw=4 diff --git a/src/libiso8601/manip.c b/src/libiso8601/manip.c index cfcb524..c489f60 100644 --- a/src/libiso8601/manip.c +++ b/src/libiso8601/manip.c @@ -7,19 +7,56 @@ +int iso8601_lt(const struct iso8601_date* d1, const struct iso8601_date* d2) +{ + if(d1->day < d2->day) return 1; + if(d1->day > d2->day) return 0; + if(d1->sec < d2->sec) return 1; + if(d1->sec > d2->sec) return 0; + if(d1->nsec < d2->nsec) return 0; + return 1; +} + + + +int iso8601_lte(const struct iso8601_date* d1, const struct iso8601_date* d2) +{ + if(d1->day < d2->day) return 1; + if(d1->day > d2->day) return 0; + if(d1->sec < d2->sec) return 1; + if(d1->sec > d2->sec) return 0; + if(d1->nsec < d2->nsec) return 0; + return d1->nsec == d2->nsec; +} + + + void iso8601_add_seconds(struct iso8601_date* date, long seconds) { + int sday; ldiv_t qr; - // TODO: leap second support - + sday = date->day; qr = ldiv(seconds + date->sec, 86400); date->day += qr.quot; + date->sec = qr.rem; if(date->sec < 0) { --date->day; date->sec += 86400; } + + /* leap second correction */ + if(sday != date->day) { + date->sec += _leap_elapsed_day(sday, date->day); + if(date->sec < 0) { + --date->day; + date->sec += iso8601_seconds_leap(date); + } else if(date->sec >= iso8601_seconds_leap(date)) { + date->sec -= iso8601_seconds_leap(date); + ++date->day; + } + } } diff --git a/src/tests/leap.c b/src/tests/leap.c new file mode 100644 index 0000000..0f36def --- /dev/null +++ b/src/tests/leap.c @@ -0,0 +1,267 @@ +/* libiso8601/src/tests/leap.c + * + * (c)2007, Laurence Withers, . + * Released under the GNU GPLv2. See file COPYING or + * http://www.gnu.org/copyleft/gpl.html for details. +*/ + +#include "iso8601.h" + +#include +#include + + + +int leap_diff(const char* s1, const char* s2, int* diff) +{ + struct iso8601_date d1, d2; + + if(iso8601_parse(s1, &d1, 0, 0) || iso8601_parse(s2, &d2, 0, 0)) { + fprintf(stderr, "Unable to parse ``%s'' or ``%s'' (%m).", s1, s2); + return -1; + } + + *diff = iso8601_leap_elapsed(&d1, &d2); + return 0; +} + + + +int regression1(void) +{ + struct iso8601_date d; + char s1[30], s2[30], s3[30]; + int ret = 0, oday; + + iso8601_from_cal(&d, 2005, 12, 31); + oday = d.day; + d.sec = 0; + d.nsec = 0; + iso8601_print(s1, sizeof(s1), &d, 0); + + iso8601_add_seconds(&d, 86400); + iso8601_print(s2, sizeof(s2), &d, 0); + ret |= (d.day != oday) | (d.sec != 86400); + + iso8601_add_seconds(&d, 86400); + iso8601_print(s3, sizeof(s3), &d, 0); + ret |= (d.day != (oday + 1)) | (d.sec != 86399); + + printf("%s -> %s -> %s\n", s1, s2, s3); + + return ret; +} + + + +int regression2(void) +{ + struct iso8601_date d; + char s1[30], s2[30], s3[30]; + int ret = 0, oday; + + iso8601_from_cal(&d, 2005, 12, 31); + oday = d.day; + d.sec = 86399; + d.nsec = 0; + iso8601_print(s1, sizeof(s1), &d, 0); + + iso8601_add_seconds(&d, 86400); + iso8601_print(s2, sizeof(s2), &d, 0); + ret |= (d.day != (oday + 1)) | (d.sec != 86398); + + iso8601_add_seconds(&d, 86400); + iso8601_print(s3, sizeof(s3), &d, 0); + ret |= (d.day != (oday + 2)) | (d.sec != 86398); + + printf("%s -> %s -> %s\n", s1, s2, s3); + + return ret; +} + + + +int regression3(void) +{ + struct iso8601_date d; + char s1[30], s2[30], s3[30]; + int ret = 0, oday; + + iso8601_from_cal(&d, 2005, 12, 31); + oday = d.day; + d.sec = 86400; + d.nsec = 0; + iso8601_print(s1, sizeof(s1), &d, 0); + + iso8601_add_seconds(&d, 86400); + iso8601_print(s2, sizeof(s2), &d, 0); + ret |= (d.day != (oday + 1)) | (d.sec != 86399); + + iso8601_add_seconds(&d, 86400); + iso8601_print(s3, sizeof(s3), &d, 0); + ret |= (d.day != (oday + 2)) | (d.sec != 86399); + + printf("%s -> %s -> %s\n", s1, s2, s3); + + return ret; +} + + + +int regression4(void) +{ + struct iso8601_date d; + char s1[30], s2[30], s3[30]; + int ret = 0, oday; + + iso8601_from_cal(&d, 2006, 1, 1); + oday = d.day; + d.sec = 86399; + d.nsec = 0; + iso8601_print(s1, sizeof(s1), &d, 0); + + iso8601_add_seconds(&d, -86400); + iso8601_print(s2, sizeof(s2), &d, 0); + ret |= (d.day != (oday - 1)) | (d.sec != 86400); + + iso8601_add_seconds(&d, -86400); + iso8601_print(s3, sizeof(s3), &d, 0); + ret |= (d.day != (oday - 1)) | (d.sec != 0); + + printf("%s -> %s -> %s\n", s1, s2, s3); + + return ret; +} + + + +int regression5(void) +{ + struct iso8601_date d; + char s1[30], s2[30], s3[30]; + int ret = 0, oday; + + iso8601_from_cal(&d, 2006, 1, 2); + oday = d.day; + d.sec = 86398; + d.nsec = 0; + iso8601_print(s1, sizeof(s1), &d, 0); + + iso8601_add_seconds(&d, -86400); + iso8601_print(s2, sizeof(s2), &d, 0); + ret |= (d.day != (oday - 1)) | (d.sec != 86398); + + iso8601_add_seconds(&d, -86400); + iso8601_print(s3, sizeof(s3), &d, 0); + ret |= (d.day != (oday - 2)) | (d.sec != 86399); + + printf("%s -> %s -> %s\n", s1, s2, s3); + + return ret; +} + + + +int regression6(void) +{ + struct iso8601_date d; + char s1[30], s2[30], s3[30]; + int ret = 0, oday; + + iso8601_from_cal(&d, 2006, 1, 2); + oday = d.day; + d.sec = 86399; + d.nsec = 0; + iso8601_print(s1, sizeof(s1), &d, 0); + + iso8601_add_seconds(&d, -86400); + iso8601_print(s2, sizeof(s2), &d, 0); + ret |= (d.day != (oday - 1)) | (d.sec != 86399); + + iso8601_add_seconds(&d, -86400); + iso8601_print(s3, sizeof(s3), &d, 0); + ret |= (d.day != (oday - 2)) | (d.sec != 86400); + + printf("%s -> %s -> %s\n", s1, s2, s3); + + return ret; +} + + + +struct regression_test { + const char* name; + int (*func)(void); +}; + +struct regression_test regression_tests[] = { + { "Start of leap day, +86400s", regression1 }, + { "Near end of leap day, +86400s", regression2 }, + { "End of leap day, +86400s", regression3 }, + { "Start of post-leap day, -86400s", regression4 }, + { "Near end of post-leap day, -86400s", regression5 }, + { "End of post-leap day, -86400s", regression6 }, + { 0, 0 } +}; + + + +int regression(void) +{ + int ret = 0; + struct regression_test* test; + + for(test = regression_tests; test->func; ++test) { + if(test->func()) { + fprintf(stderr, "%s failed.\n", test->name); + ++ret; + } + } + + return ret; +} + + + +int main(int argc, char* argv[]) +{ + int ret = 0, diff; + + if(argc == 2 && !strcmp(argv[1], "--print-summary")) { + fputs("Leap second regression test.\n", stdout); + return 0; + } + + switch(argc) { + case 1: + ret = regression(); + fputs("Alternative use: pass two dates on commandline to see elapsed leap seconds.\n", + stdout); + break; + + case 3: + ret = leap_diff(argv[1], argv[2], &diff); + if(!ret) printf("Leap second correction: %d\n", diff); + break; + + case 2: + if(!strcmp(argv[1], "--print-summary")) { + printf("Leap second regression test."); + return 0; + } + /* fall through */ + default: + ret = 1; + fputs("Either zero or two arguments expected.\n", stderr); + break; + } + + /* TODO */ + + return ret; +} + +/* options for text editors +kate: replace-trailing-space-save true; space-indent true; tab-width 4; +vim: expandtab:ts=4:sw=4 +*/