Add iso8601_add_multiple()

Add a function to efficiently add (or subtract) a multiple of a period
to a timestamp.
This commit is contained in:
Laurence Withers 2008-12-11 15:37:12 +00:00
parent d431f68054
commit cad2283f6a
4 changed files with 105 additions and 16 deletions

View File

@ -13,6 +13,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <inttypes.h>
#include <sys/time.h> #include <sys/time.h>
/* Useful define to alleviate typos */ /* Useful define to alleviate typos */

View File

@ -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, void iso8601_difference(const struct iso8601_date* d1, const struct iso8601_date* d2,
struct iso8601_elapsed* per, int* sign) struct iso8601_elapsed* per, int* sign)
{ {

View File

@ -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. /*! \brief Find difference between dates.
\param d1 First date. \param d1 First date.
@ -93,5 +107,5 @@ void iso8601_difference(const struct iso8601_date* d1, const struct iso8601_date
/*!@}*/ /*!@}*/
/* options for text editors /* options for text editors
kate: replace-trailing-space-save true; space-indent true; tab-width 4; 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
*/ */

View File

@ -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() /* date_stack_pop_date()
* Pops a date from the stack, returning non-0 on error (underflow or type mismatch). * 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) int date_stack_pop_date(struct iso8601_date* d, struct iso8601_details* det)
{ {
struct date_stack* next;
if(!date_stack_top) { if(!date_stack_top) {
fputs("Stack underflow.\n", stderr); fputs("Stack underflow.\n", stderr);
return -1; 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; *d = date_stack_top->u.date.d;
*det = date_stack_top->u.date.det; *det = date_stack_top->u.date.det;
date_stack_pop();
--date_stack_size;
next = date_stack_top->next;
free(date_stack_top);
date_stack_top = next;
return 0; 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) int date_stack_pop_elapsed(struct iso8601_elapsed* per)
{ {
struct date_stack* next;
if(!date_stack_top) { if(!date_stack_top) {
fputs("Stack underflow.\n", stderr); fputs("Stack underflow.\n", stderr);
return -1; return -1;
@ -138,17 +145,40 @@ int date_stack_pop_elapsed(struct iso8601_elapsed* per)
} }
*per = date_stack_top->u.per; *per = date_stack_top->u.per;
date_stack_pop();
--date_stack_size;
next = date_stack_top->next;
free(date_stack_top);
date_stack_top = next;
return 0; 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() /* usage()
* Displays help. * Displays help.
*/ */
@ -159,11 +189,14 @@ void usage(void)
"Operators:\n" "Operators:\n"
" + (date, period) Advance date by elapsed time, push date.\n" " + (date, period) Advance date by elapsed time, push date.\n"
" - (date, period) Regress 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" " 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 second.\n"
" <= (date, date) Prints 1 if first date less than or equal to second.\n" " <= (date, date) Prints 1 if first date less than or equal to second.\n"
" == (date, date) Prints 1 if dates equal.\n" " == (date, date) Prints 1 if dates equal.\n"
" p (date) Prints a date.\n" " p (date) Prints a date.\n"
" ds Dump stack.\n"
"\n" "\n"
"Type help for this screen at any point.\n" "Type help for this screen at any point.\n"
"\n", stdout); "\n", stdout);
@ -175,7 +208,7 @@ int parse_command(char* cmd)
{ {
struct iso8601_date date, date2; struct iso8601_date date, date2;
struct iso8601_details details, details2; struct iso8601_details details, details2;
struct iso8601_elapsed period; struct iso8601_elapsed period, num;
double period_d; double period_d;
char date_str[40]; char date_str[40];
int ret = 0, sign; 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")) { } else if(!strcmp(cmd, "dp")) {
if(date_stack_pop_date(&date, &details) || date_stack_pop_date(&date2, &details2)) { if(date_stack_pop_date(&date, &details) || date_stack_pop_date(&date2, &details2)) {
ret = 1; ret = 1;
@ -259,6 +313,9 @@ int parse_command(char* cmd)
putc('\n', stdout); putc('\n', stdout);
} }
} else if(!strcmp(cmd, "ds")) {
date_stack_dump();
} else { } else {
fputs("Unrecognised command or argument.\n", stderr); fputs("Unrecognised command or argument.\n", stderr);
ret = 1; ret = 1;