Add some more leap second support

This commit is contained in:
Laurence Withers 2007-05-22 11:40:40 +00:00
parent 4bd50f62e2
commit 7aace3d8e3
5 changed files with 333 additions and 3 deletions

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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;
}
}
}

267
src/tests/leap.c Normal file
View File

@ -0,0 +1,267 @@
/* libiso8601/src/tests/leap.c
*
* (c)2007, Laurence Withers, <l@lwithers.me.uk>.
* Released under the GNU GPLv2. See file COPYING or
* http://www.gnu.org/copyleft/gpl.html for details.
*/
#include "iso8601.h"
#include <stdio.h>
#include <string.h>
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
*/