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 <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <sys/time.h>
/* 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,
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.
\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
*/

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()
* 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;