From cad2283f6ab3224a2dedacb4ed45f3ea6ea95edc Mon Sep 17 00:00:00 2001 From: Laurence Withers Date: Thu, 11 Dec 2008 15:37:12 +0000 Subject: [PATCH] Add iso8601_add_multiple() Add a function to efficiently add (or subtract) a multiple of a period to a timestamp. --- src/libiso8601/000_TopSource.c | 1 + src/libiso8601/400_manip.c | 17 +++++++ src/libiso8601/400_manip.h | 16 ++++++- src/tests/manip.c | 87 ++++++++++++++++++++++++++++------ 4 files changed, 105 insertions(+), 16 deletions(-) diff --git a/src/libiso8601/000_TopSource.c b/src/libiso8601/000_TopSource.c index 087d455..4e20f64 100644 --- a/src/libiso8601/000_TopSource.c +++ b/src/libiso8601/000_TopSource.c @@ -13,6 +13,7 @@ #include #include #include +#include #include /* Useful define to alleviate typos */ diff --git a/src/libiso8601/400_manip.c b/src/libiso8601/400_manip.c index 964cf0e..e61f110 100644 --- a/src/libiso8601/400_manip.c +++ b/src/libiso8601/400_manip.c @@ -98,6 +98,23 @@ void iso8601_subtract_elapsed(struct iso8601_date* date, const struct iso8601_el +void iso8601_add_multiple(struct iso8601_date* date, const struct iso8601_elapsed* per, int n) +{ + intmax_t nsec; + imaxdiv_t qr; + struct iso8601_elapsed mult; + + nsec = per->nsec * imaxabs(n); + qr = imaxdiv(nsec, BILLION); + mult.sec = qr.quot + per->sec * imaxabs(n); + mult.nsec = qr.rem; + + if(n < 0) iso8601_subtract_elapsed(date, &mult); + else iso8601_add_elapsed(date, &mult); +} + + + void iso8601_difference(const struct iso8601_date* d1, const struct iso8601_date* d2, struct iso8601_elapsed* per, int* sign) { diff --git a/src/libiso8601/400_manip.h b/src/libiso8601/400_manip.h index eac24da..c8d69dc 100644 --- a/src/libiso8601/400_manip.h +++ b/src/libiso8601/400_manip.h @@ -73,6 +73,20 @@ void iso8601_subtract_elapsed(struct iso8601_date* date, const struct iso8601_el +/*! \brief Add a multiple of a period to a date. + +\param[in,out] date Date to modify. +\param per Period to advance date/time by. +\param n Multiple of \a per. + +Adds \a n multiples of \a per to \a date. \a n may be 0 or negative. The result is stored in +\a date. This is an efficient implementation which avoids loops, but it does use 64-bit arithmetic. + +*/ +void iso8601_add_multiple(struct iso8601_date* date, const struct iso8601_elapsed* per, int n); + + + /*! \brief Find difference between dates. \param d1 First date. @@ -93,5 +107,5 @@ void iso8601_difference(const struct iso8601_date* d1, const struct iso8601_date /*!@}*/ /* options for text editors kate: replace-trailing-space-save true; space-indent true; tab-width 4; -vim: expandtab:ts=4:sw=4 +vim: expandtab:ts=4:sw=4:syntax=ch.doxygen */ diff --git a/src/tests/manip.c b/src/tests/manip.c index 32b3127..793b2ec 100644 --- a/src/tests/manip.c +++ b/src/tests/manip.c @@ -92,13 +92,26 @@ void date_stack_push_elapsed(const struct iso8601_elapsed* per) +/* date_stack_pop() + * Pops an element from the date stack. + */ +void date_stack_pop(void) +{ + struct date_stack* next; + + --date_stack_size; + next = date_stack_top->next; + free(date_stack_top); + date_stack_top = next; +} + + + /* date_stack_pop_date() * Pops a date from the stack, returning non-0 on error (underflow or type mismatch). */ int date_stack_pop_date(struct iso8601_date* d, struct iso8601_details* det) { - struct date_stack* next; - if(!date_stack_top) { fputs("Stack underflow.\n", stderr); return -1; @@ -110,11 +123,7 @@ int date_stack_pop_date(struct iso8601_date* d, struct iso8601_details* det) *d = date_stack_top->u.date.d; *det = date_stack_top->u.date.det; - - --date_stack_size; - next = date_stack_top->next; - free(date_stack_top); - date_stack_top = next; + date_stack_pop(); return 0; } @@ -126,8 +135,6 @@ int date_stack_pop_date(struct iso8601_date* d, struct iso8601_details* det) */ int date_stack_pop_elapsed(struct iso8601_elapsed* per) { - struct date_stack* next; - if(!date_stack_top) { fputs("Stack underflow.\n", stderr); return -1; @@ -138,17 +145,40 @@ int date_stack_pop_elapsed(struct iso8601_elapsed* per) } *per = date_stack_top->u.per; - - --date_stack_size; - next = date_stack_top->next; - free(date_stack_top); - date_stack_top = next; + date_stack_pop(); return 0; } +/* date_stack_dump() + * Dumps and clears the contents of the date stack. + */ +void date_stack_dump(void) +{ + while(date_stack_top) { + switch(date_stack_top->type) { + case date_stack_type_date: + fprintf(stderr, "Date (day=%d, sec=%d, nsec=%d)\n", + date_stack_top->u.date.d.day, date_stack_top->u.date.d.sec, date_stack_top->u.date.d.nsec); + break; + + case date_stack_type_per: + fprintf(stderr, "Period (sec=%d, nsec=%d)\n", + date_stack_top->u.per.sec, date_stack_top->u.per.nsec); + break; + + default: + fprintf(stderr, "Unknown type %d.\n", date_stack_top->type); + break; + } + date_stack_pop(); + } +} + + + /* usage() * Displays help. */ @@ -159,11 +189,14 @@ void usage(void) "Operators:\n" " + (date, period) Advance date by elapsed time, push date.\n" " - (date, period) Regress date by elapsed time, push date.\n" + " +* (date, period, num) Advance date by elapsed time multiplied by num, push date.\n" + " -* (date, period, num) Regress date by elapsed time multiplied by num, push date.\n" " dp (date, date) Compute difference, print elapsed time.\n" " < (date, date) Prints 1 if first date less than second.\n" " <= (date, date) Prints 1 if first date less than or equal to second.\n" " == (date, date) Prints 1 if dates equal.\n" " p (date) Prints a date.\n" + " ds Dump stack.\n" "\n" "Type help for this screen at any point.\n" "\n", stdout); @@ -175,7 +208,7 @@ int parse_command(char* cmd) { struct iso8601_date date, date2; struct iso8601_details details, details2; - struct iso8601_elapsed period; + struct iso8601_elapsed period, num; double period_d; char date_str[40]; int ret = 0, sign; @@ -209,6 +242,27 @@ int parse_command(char* cmd) } + } else if(!strcmp(cmd, "+*") || !strcmp(cmd, "-*")) { + if(date_stack_pop_elapsed(&num)) { + ret = 1; + + } else if(num.nsec) { + ret = 1; + fputs("Number cannot be fractional.\n", stderr); + + } else { + + if(date_stack_pop_elapsed(&period) || date_stack_pop_date(&date, &details)) { + ret = 1; + + } else { + iso8601_add_multiple(&date, &period, ((cmd[0] == '-') ? -1 : 1) * num.sec); + date_stack_push_date(&date, &details); + + } + + } + } else if(!strcmp(cmd, "dp")) { if(date_stack_pop_date(&date, &details) || date_stack_pop_date(&date2, &details2)) { ret = 1; @@ -259,6 +313,9 @@ int parse_command(char* cmd) putc('\n', stdout); } + } else if(!strcmp(cmd, "ds")) { + date_stack_dump(); + } else { fputs("Unrecognised command or argument.\n", stderr); ret = 1;