diff --git a/src/libiso8601/.types.h.swp b/src/libiso8601/.types.h.swp deleted file mode 100644 index 800743c..0000000 Binary files a/src/libiso8601/.types.h.swp and /dev/null differ diff --git a/src/libiso8601/TopSource.c b/src/libiso8601/TopSource.c index f890996..2045dbf 100644 --- a/src/libiso8601/TopSource.c +++ b/src/libiso8601/TopSource.c @@ -10,6 +10,10 @@ // Below are all the includes used throughout the library. #include #include +#include + + +#include /* options for text editors kate: replace-trailing-space-save true; space-indent true; tab-width 4; diff --git a/src/libiso8601/build.monolithic b/src/libiso8601/build.monolithic index 4fe847c..9245773 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}.c)" + MONOLITHIC_SOURCE="$(echo src/libiso8601/{TopSource,c_library,calc,parser,print,manip}.c)" make_monolithic ${SRC} C || return 1 libiso8601_MONOLITHIC=1 diff --git a/src/libiso8601/c_library.c b/src/libiso8601/c_library.c index 8d2f734..0ec4e75 100644 --- a/src/libiso8601/c_library.c +++ b/src/libiso8601/c_library.c @@ -5,24 +5,32 @@ * http://www.gnu.org/copyleft/gpl.html for details. */ +// days between 0000-001 and 1970-001 (the unix epoch) +#define EPOCH_GDAY (719528) + void iso8601_now(struct iso8601_date* date, struct iso8601_details* details) { struct timespec ts; + struct tm tm; + + clock_gettime(CLOCK_REALTIME, &ts); // populate the `struct iso8601_date' if it was passed if(date) { - clock_gettime(CLOCK_REALTIME, &ts); iso8601_from_ts(date, &ts); } // populate the `struct iso8601_details' if it was passed if(details) { - tzset(); + memset(details, 0, sizeof(struct iso8601_details)); details->date_prec = iso8601_prec_day; details->time_prec = iso8601_prec_secfrac; - details->tz_sec = timezone; + + // this is silly, but it's the only way to recover the timezone + localtime_r(&ts.tv_sec, &tm); + details->tz_sec = tm.tm_gmtoff; } } @@ -33,7 +41,7 @@ void iso8601_from_ts(struct iso8601_date* date, const struct timespec* ts) ldiv_t qr; qr = ldiv(ts->tv_sec, 86400); - date->day = /* XXX days between 0000-001 and 1970-001 */ + qr.quot; + date->day = EPOCH_GDAY + qr.quot; date->sec = qr.rem; date->nsec = ts->tv_nsec; } @@ -42,7 +50,7 @@ void iso8601_from_ts(struct iso8601_date* date, const struct timespec* ts) void iso8601_to_ts(struct timespec* ts, const struct iso8601_date* date) { - ts->tv_sec = 86400L * date->day + date->sec; + ts->tv_sec = 86400L * (date->day - EPOCH_GDAY) + date->sec; ts->tv_nsec = date->nsec; } @@ -53,7 +61,7 @@ void iso8601_from_time_t(struct iso8601_date* date, const time_t* t) ldiv_t qr; qr = ldiv(*t, 86400); - date->day = /* XXX days between 0000-001 and 1970-001 */ + qr.quot; + date->day = EPOCH_GDAY + qr.quot; date->sec = qr.rem; date->nsec = 0; } @@ -62,8 +70,7 @@ void iso8601_from_time_t(struct iso8601_date* date, const time_t* t) void iso8601_to_time_t(time_t* t, const struct iso8601_date* date) { - *t = 86400L * date->day + date->sec; - if(date->nsec >= 500000000) ++*t; + *t = 86400L * (date->day - EPOCH_GDAY) + date->sec; } diff --git a/src/libiso8601/functions.h b/src/libiso8601/functions.h index 0b96c34..d03d219 100644 --- a/src/libiso8601/functions.h +++ b/src/libiso8601/functions.h @@ -21,6 +21,13 @@ int iso8601_isleap(int year); void iso8601_to_cal(int* year, int* month, int* day, const struct iso8601_date* date); int iso8601_from_cal(struct iso8601_date* date, int year, int month, int day); +/* print.c */ +void iso8601_print(char* str, int amt, const struct iso8601_date* date, + const struct iso8601_details* details); + +/* manip.c */ +void iso8601_add_seconds(struct iso8601_date* date, long seconds); + /* 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 new file mode 100644 index 0000000..0f162b2 --- /dev/null +++ b/src/libiso8601/manip.c @@ -0,0 +1,31 @@ +/* libiso8601/src/libiso8601/manip.c + * + * (c)2006, Laurence Withers, . + * Released under the GNU GPLv2. See file COPYING or + * http://www.gnu.org/copyleft/gpl.html for details. +*/ + + + +void iso8601_add_seconds(struct iso8601_date* date, long seconds) +{ + ldiv_t qr; + + switch(date->sec) { + case 86400: + case 86401: + --date->sec; + break; + } + + qr = ldiv(seconds + date->sec, 86400); + date->day += qr.quot; + date->sec = qr.rem; +} + + + +/* 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/print.c b/src/libiso8601/print.c new file mode 100644 index 0000000..d306906 --- /dev/null +++ b/src/libiso8601/print.c @@ -0,0 +1,154 @@ +/* libiso8601/src/libiso8601/print.c + * + * (c)2006, Laurence Withers, . + * Released under the GNU GPLv2. See file COPYING or + * http://www.gnu.org/copyleft/gpl.html for details. +*/ + + + +static const struct iso8601_details _default_details = { + iso8601_prec_day, + iso8601_prec_sec, + 1, + 0 +}; + + + +void iso8601_print(char* str, int amt, const struct iso8601_date* date, + const struct iso8601_details* details) +{ + int y, m, d, ret, extended; + struct iso8601_date dttz; + double frac; + + // use default details if none provided + if(!details) details = &_default_details; + + // adjust output for timezone + dttz = *date; + iso8601_add_seconds(&dttz, details->tz_sec); + + // determine whether or not to force extended output + iso8601_to_cal(&y, &m, &d, &dttz); + extended = details->extended || y < 0 || y > 9999; + + switch(details->date_prec) { + case iso8601_prec_year: + if(y < 0) snprintf(str, amt, "%05d", y); + else snprintf(str, amt, "%04d", y); + return; + + case iso8601_prec_month: + iso8601_to_cal(&y, &m, &d, &dttz); + if(y < 0) snprintf(str, amt, "%05d-%02d", y, m); + else snprintf(str, amt, extended ? "%04d-%02d" : "%04d%02d", y, m); + return; + + case iso8601_prec_day: + iso8601_to_cal(&y, &m, &d, &dttz); + if(y < 0) ret = snprintf(str, amt, "%05d-%02d-%02d", y, m, d); + ret = snprintf(str, amt, extended ? "%04d-%02d-%02d" : "%04d%02d%02d", y, m, d); + break; + + default: + fprintf(stderr, "<<< NOT IMPLEMENTED >>>\n"); + abort(); + } + + if(ret < 1 || ret >= amt) return; + str += ret; + amt -= ret; + + switch(dttz.sec) { + case 86400: + y = 23; + m = 59; + d = 60; + break; + + case 86401: + y = 24; + m = 0; + d = 0; + break; + + default: + y = dttz.sec / 3600; + dttz.sec -= y * 3600; + m = dttz.sec / 60; + dttz.sec -= m * 60; + d = dttz.sec; + } + + switch(details->time_prec) { + case iso8601_prec_none: + return; + + case iso8601_prec_hour: + ret = snprintf(str, amt, "T%02d", y); + break; + + case iso8601_prec_min: + ret = snprintf(str, amt, extended ? "T%02d:%02d" : "T%02d%02d", y, m); + break; + + case iso8601_prec_sec: + ret = snprintf(str, amt, extended ? "T%02d:%02d:%02d" : "T%02d%02d%02d", y, m, d); + break; + + case iso8601_prec_hourfrac: + frac = y + m / 60.0 + (d + date->nsec / 1e9) / 3600.0; + ret = snprintf(str, amt, "T%#09.6f", frac); + break; + + case iso8601_prec_minfrac: + frac = m + (d + date->nsec / 1e9) / 60.0; + ret = snprintf(str, amt, extended ? "T%02d:%#09.6f" : "T%02d%#09.6f", y, frac); + break; + + case iso8601_prec_secfrac: + frac = d + date->nsec / 1e9; + ret = snprintf(str, amt, extended ? "T%02d:%02d:%#09.6f" : "T%02d%02d%#09.6f", y, m, frac); + break; + } + + if(ret < 1 || ret >= amt) return; + str += ret; + amt -= ret; + + if(details->tz_sec) { + if(!--amt) { + *str = 0; + return; + } + if(details->tz_sec < 0) { + *str++ = '-'; + ret = -details->tz_sec; + } else { + *str++ = '+'; + ret = details->tz_sec; + } + + y = ret / 3600; + ret -= y * 3600; + m = ret / 60; + ret -= m * 60; + d = ret; + + if(d) snprintf(str, amt, extended ? "%02d:%02d:%02d" : "%02d%02d%02d", y, m, d); + else if(m) snprintf(str, amt, extended ? "%02d:%02d" : "%02d%02d", y, m); + else snprintf(str, amt, "%02d", y); + } else { + *str++ = 'Z'; + if(amt > 1) *str = 0; + } +} + + + +/* 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/types.h b/src/libiso8601/types.h index 84caf21..3b80717 100644 --- a/src/libiso8601/types.h +++ b/src/libiso8601/types.h @@ -8,9 +8,9 @@ struct iso8601_date { - uint32_t nsec; + int32_t nsec; int32_t day; - uint32_t sec; + int32_t sec; // special values: 86400 == leap second 23:59:60, 86401 == midnight 24:00:00 }; enum iso8601_date_prec { @@ -33,7 +33,7 @@ enum iso8601_time_prec { }time_prec; struct iso8601_details { - uint8_t date_prec, time_prec; + uint8_t date_prec, time_prec, extended; int32_t tz_sec; }; diff --git a/src/tests/printer.c b/src/tests/printer.c new file mode 100644 index 0000000..f629274 --- /dev/null +++ b/src/tests/printer.c @@ -0,0 +1,189 @@ +/* libiso8601/src/tests/printer.c + * + * (c)2006, 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 + + + +void do_details_local(struct iso8601_details* dt_out, const struct iso8601_details* dt_in) +{ + memcpy(dt_out, dt_in, sizeof(struct iso8601_details)); +} + +void do_details_local_nofrac(struct iso8601_details* dt_out, const struct iso8601_details* dt_in) +{ + memcpy(dt_out, dt_in, sizeof(struct iso8601_details)); + dt_out->time_prec = iso8601_prec_sec; +} + +void do_details_local_min(struct iso8601_details* dt_out, const struct iso8601_details* dt_in) +{ + memcpy(dt_out, dt_in, sizeof(struct iso8601_details)); + dt_out->time_prec = iso8601_prec_min; +} + +void do_details_local_minfrac(struct iso8601_details* dt_out, const struct iso8601_details* dt_in) +{ + memcpy(dt_out, dt_in, sizeof(struct iso8601_details)); + dt_out->time_prec = iso8601_prec_minfrac; +} + +void do_details_local_hour(struct iso8601_details* dt_out, const struct iso8601_details* dt_in) +{ + memcpy(dt_out, dt_in, sizeof(struct iso8601_details)); + dt_out->time_prec = iso8601_prec_hour; +} + +void do_details_local_hourfrac(struct iso8601_details* dt_out, const struct iso8601_details* dt_in) +{ + memcpy(dt_out, dt_in, sizeof(struct iso8601_details)); + dt_out->time_prec = iso8601_prec_hourfrac; +} + +void do_details_local_day(struct iso8601_details* dt_out, const struct iso8601_details* dt_in) +{ + memcpy(dt_out, dt_in, sizeof(struct iso8601_details)); + dt_out->time_prec = iso8601_prec_none; +} + +void do_details_local_month(struct iso8601_details* dt_out, const struct iso8601_details* dt_in) +{ + memcpy(dt_out, dt_in, sizeof(struct iso8601_details)); + dt_out->date_prec = iso8601_prec_month; +} + +void do_details_local_year(struct iso8601_details* dt_out, const struct iso8601_details* dt_in) +{ + memcpy(dt_out, dt_in, sizeof(struct iso8601_details)); + dt_out->date_prec = iso8601_prec_year; +} + +void do_details_utc(struct iso8601_details* dt_out, const struct iso8601_details* dt_in) +{ + memcpy(dt_out, dt_in, sizeof(struct iso8601_details)); + dt_out->tz_sec = 0; +} + +void do_details_utc_nofrac(struct iso8601_details* dt_out, const struct iso8601_details* dt_in) +{ + memcpy(dt_out, dt_in, sizeof(struct iso8601_details)); + dt_out->tz_sec = 0; + dt_out->time_prec = iso8601_prec_sec; +} + +void do_details_utc_min(struct iso8601_details* dt_out, const struct iso8601_details* dt_in) +{ + memcpy(dt_out, dt_in, sizeof(struct iso8601_details)); + dt_out->tz_sec = 0; + dt_out->time_prec = iso8601_prec_min; +} + +void do_details_utc_minfrac(struct iso8601_details* dt_out, const struct iso8601_details* dt_in) +{ + memcpy(dt_out, dt_in, sizeof(struct iso8601_details)); + dt_out->tz_sec = 0; + dt_out->time_prec = iso8601_prec_minfrac; +} + +void do_details_utc_hour(struct iso8601_details* dt_out, const struct iso8601_details* dt_in) +{ + memcpy(dt_out, dt_in, sizeof(struct iso8601_details)); + dt_out->tz_sec = 0; + dt_out->time_prec = iso8601_prec_hour; +} + +void do_details_utc_hourfrac(struct iso8601_details* dt_out, const struct iso8601_details* dt_in) +{ + memcpy(dt_out, dt_in, sizeof(struct iso8601_details)); + dt_out->tz_sec = 0; + dt_out->time_prec = iso8601_prec_hourfrac; +} + +void do_details_utc_day(struct iso8601_details* dt_out, const struct iso8601_details* dt_in) +{ + memcpy(dt_out, dt_in, sizeof(struct iso8601_details)); + dt_out->tz_sec = 0; + dt_out->time_prec = iso8601_prec_none; +} + +void do_details_utc_month(struct iso8601_details* dt_out, const struct iso8601_details* dt_in) +{ + memcpy(dt_out, dt_in, sizeof(struct iso8601_details)); + dt_out->tz_sec = 0; + dt_out->date_prec = iso8601_prec_month; +} + +void do_details_utc_year(struct iso8601_details* dt_out, const struct iso8601_details* dt_in) +{ + memcpy(dt_out, dt_in, sizeof(struct iso8601_details)); + dt_out->tz_sec = 0; + dt_out->date_prec = iso8601_prec_year; +} + + + +struct test { + const char* desc; + void (*do_details)(struct iso8601_details* dt_out, const struct iso8601_details* dt_in); +}; +const struct test tests[] = { + { "UTC with fraction", do_details_utc }, + { "UTC without fraction", do_details_utc_nofrac }, + { "UTC minute", do_details_utc_min }, + { "UTC minute with fraction", do_details_utc_minfrac }, + { "UTC hour", do_details_utc_hour }, + { "UTC hour with fraction", do_details_utc_hourfrac }, + { "UTC day", do_details_utc_day }, + { "UTC month", do_details_utc_month }, + { "UTC year", do_details_utc_year }, + { "Local with fraction", do_details_local }, + { "Local without fraction", do_details_local_nofrac }, + { "Local minute", do_details_local_min }, + { "Local minute with fraction", do_details_local_minfrac }, + { "Local hour", do_details_local_hour }, + { "Local hour with fraction", do_details_local_hourfrac }, + { "Local day", do_details_local_day }, + { "Local month", do_details_local_month }, + { "Local year", do_details_local_year }, + { 0, 0 } +}; + + + +int main(int argc, char* argv[]) +{ + int ret = 0; + struct iso8601_date dt; + struct iso8601_details details, details2; + char buf[100], buf2[100]; + const struct test* test; + + if(argc == 2 && !strcmp(argv[1], "--print-summary")) { + printf("Prints the current time in various formats.\n"); + return 0; + } + + iso8601_now(&dt, &details); + printf("%-30s %-30s %s\n", "Name", "Basic format", "Extended format"); + for(test = tests; test->desc; ++test) { + test->do_details(&details2, &details); + iso8601_print(buf, sizeof(buf), &dt, &details2); + ++details2.extended; + iso8601_print(buf2, sizeof(buf2), &dt, &details2); + printf("%-30s %-30s %s\n", test->desc, buf, buf2); + } + + return ret; +} + +/* options for text editors +kate: replace-trailing-space-save true; space-indent true; tab-width 4; +vim: expandtab:ts=4:sw=4 +*/