libhmd-c

C human-readable date string library
git clone https://git.sinitax.com/sinitax/libhmd-c
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

commit df9afb60e36e888f2850730a2b98a60a725c49e2
parent 257d645c5589ce4804a4a6dad36bc9dd4b508458
Author: Louis Burda <quent.burda@gmail.com>
Date:   Mon, 26 Jun 2023 18:52:30 +0200

Add date span / delta

Diffstat:
Minclude/hmd.h | 13+++++--------
Mlibhmd.api | 4++++
Mlibhmd.lds | 4++++
Msrc/hmd.c | 200+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
4 files changed, 178 insertions(+), 43 deletions(-)

diff --git a/include/hmd.h b/include/hmd.h @@ -71,19 +71,16 @@ struct hmd_tod { uint16_t spn; }; -struct hmd_ival { - uint16_t year; - uint16_t day; - uint8_t hour; - uint8_t min; - uint8_t sec; -}; - enum hmd_err hmd_spn_parse(struct hmd_spn *spn, const char *arg); +enum hmd_err hmd_spn_ival_parse(struct hmd_spn *spn, const char *arg); + enum hmd_err hmd_spn_calc(struct hmd_spn *spn, uint64_t start, uint64_t end); +uint64_t hmd_spn_secs(struct hmd_spn *spn); char *hmd_spn_str(struct hmd_spn *spn, const struct allocator *allocator, int *rc); +char *hmd_spn_ival_str(struct hmd_spn *spn, + const struct allocator *allocator, int *rc); enum hmd_err hmd_date_parse_daydate_reltime_weekday( struct hmd_date *date, char *arg); diff --git a/libhmd.api b/libhmd.api @@ -1,7 +1,11 @@ hmd_spn_parse +hmd_spn_ival_parse hmd_spn_calc +hmd_spn_secs + hmd_spn_str +hmd_spn_ival_str hmd_date_parse_daydate_reltime_weekday hmd_date_parse_daydate_reltime_coarse diff --git a/libhmd.lds b/libhmd.lds @@ -1,8 +1,12 @@ LIBHMD_0.1.1 { hmd_spn_parse; + hmd_spn_ival_parse; hmd_spn_calc; + hmd_spn_secs; + hmd_spn_str; + hmd_spn_ival_str; hmd_date_parse_daydate_reltime_weekday; hmd_date_parse_daydate_reltime_coarse; diff --git a/src/hmd.c b/src/hmd.c @@ -144,6 +144,61 @@ time2tm(struct tm *tm, uint64_t time) return HMD_OK; } +static char * +allocdup(const struct allocator *allocator, int *rc, const char *str) +{ + char *dup; + + dup = allocator->alloc(allocator, strlen(str) + 1, rc); + if (!dup) return NULL; + + strcpy(dup, str); + + return dup; +} + +static char * +concat(char *buf, char *c, size_t *cap, const struct allocator *allocator, + int *rc, const char *fmt, ...) +{ + va_list ap, cpy; + size_t left, off; + ssize_t n; + + if (!c) return NULL; + + va_copy(cpy, ap); + + off = (size_t) (c - buf); + left = *cap - off; + va_start(ap, fmt); + n = vsnprintf(c, left, fmt, ap); + va_end(ap); + + if (n >= left) { + if (off + (size_t) n + 1 > *cap * 2) + *cap = off + (size_t) n + 1; + else + *cap *= 2; + buf = allocator->realloc(allocator, buf, *cap, rc); + if (!buf && rc) *rc = -*rc; + if (!buf) return NULL; + + c = buf + off; + left = *cap - off; + va_start(cpy, fmt); + n = vsnprintf(c, left, fmt, cpy); + va_end(cpy); + } + + if (n < 0 || n > left) { + if (rc) *rc = HMD_ERR_INT; + return NULL; + } + + return c + n; +} + enum hmd_err hmd_spn_parse(struct hmd_spn *spn, const char *arg) { @@ -184,8 +239,28 @@ hmd_spn_parse(struct hmd_spn *spn, const char *arg) } enum hmd_err +hmd_spn_ival_parse(struct hmd_spn *spn, const char *arg) +{ + memset(spn, 0, sizeof(struct hmd_spn)); + if (!strcmp(arg, "yearly")) { + spn->years = 1; + } else if (!strcmp(arg, "weekly")) { + spn->weeks = 1; + } else if (!strcmp(arg, "daily")) { + spn->days = 1; + } else if (!strcmp(arg, "hourly")) { + spn->hours = 1; + } else { + return hmd_spn_parse(spn, arg); + } + + return HMD_OK; +} + +enum hmd_err hmd_spn_calc(struct hmd_spn *spn, uint64_t start, uint64_t end) { + int years, days, hours, mins, secs; struct tm tm_start, tm_end; enum hmd_err err; @@ -197,38 +272,103 @@ hmd_spn_calc(struct hmd_spn *spn, uint64_t start, uint64_t end) err = time2tm(&tm_end, end); if (err) return err; - spn->years = (uint32_t) (tm_end.tm_year - tm_start.tm_year); - spn->days = (uint32_t) (tm_end.tm_yday - tm_start.tm_yday); - spn->weeks = spn->days / 7; - spn->days = spn->days % 7; - spn->hours = (uint32_t) (tm_end.tm_hour - tm_start.tm_hour); - spn->mins = (uint32_t) (tm_end.tm_min - tm_start.tm_min); - spn->secs = (uint32_t) (tm_end.tm_sec - tm_start.tm_sec); + years = tm_end.tm_year - tm_start.tm_year; + days = tm_end.tm_yday - tm_start.tm_yday; + + years = days = hours = mins = secs = 0; + secs += tm_end.tm_sec - tm_start.tm_sec; + if (secs < 0) { + mins -= 1; + secs += 60; + } + + mins += tm_end.tm_min - tm_start.tm_min; + if (mins < 0) { + hours -= 1; + mins += 60; + } + + hours += tm_end.tm_hour - tm_start.tm_hour; + if (hours < 0) { + days -= 1; + hours += 24; + } + + days += tm_end.tm_yday - tm_start.tm_yday; + if (days < 0) { + years -= 1; + days += 365; + } + + if (years < 0) return HMD_ERR_INT; + + spn->years = (uint32_t) years; + spn->days = (uint32_t) days % 7; + spn->weeks = (uint32_t) days / 7; + spn->hours = (uint32_t) hours; + spn->mins = (uint32_t) mins; + spn->secs = (uint32_t) secs; return HMD_OK; } +uint64_t +hmd_spn_secs(struct hmd_spn *spn) +{ + return spn->years * 365 * 24 * 3600 + + (spn->weeks * 7 + spn->days) * 24 * 3600 + + spn->hours * 3600 + spn->mins * 60 + spn->secs; +} + char * hmd_spn_str(struct hmd_spn *spn, const struct allocator *allocator, int *rc) { char *str, *c; + size_t cap; - c = str = allocator->alloc(allocator, 32 /* >= 5 * 6 + 1 */, rc); + cap = 1; + c = str = allocator->alloc(allocator, cap, rc); if (!str && rc) *rc = -*rc; if (!str) return NULL; - if (spn->years) c += snprintf(c, 6, "%.4uy", spn->years); - if (spn->weeks) c += snprintf(c, 6, "%.4uw", spn->weeks); - if (spn->days) c += snprintf(c, 6, "%.4ud", spn->days); - if (spn->hours) c += snprintf(c, 6, "%.4uh", spn->hours); - if (spn->mins) c += snprintf(c, 6, "%.4um", spn->mins); - if (spn->secs) c += snprintf(c, 6, "%.4us", spn->secs); + if (spn->years) c = concat(str, c, &cap, allocator, rc, "%uy", spn->years); + if (spn->weeks) c = concat(str, c, &cap, allocator, rc, "%uw", spn->weeks); + if (spn->days) c = concat(str, c, &cap, allocator, rc, "%ud", spn->days); + if (spn->hours) c = concat(str, c, &cap, allocator, rc, "%uh", spn->hours); + if (spn->mins) c = concat(str, c, &cap, allocator, rc, "%um", spn->mins); + if (spn->secs) c = concat(str, c, &cap, allocator, rc, "%us", spn->secs); + if (!c) { + allocator->free(allocator, str); + return NULL; + } + *c = '\0'; return str; } +char * +hmd_spn_ival_str(struct hmd_spn *spn, + const struct allocator *allocator, int *rc) +{ + if (!spn->secs && !spn->mins && spn->hours != 0 + && !spn->days && !spn->weeks && !spn->years) { + return allocdup(allocator, rc, "hourly"); + } else if (!spn->secs && !spn->mins && !spn->hours + && spn->days != 0 && !spn->weeks && !spn->years) { + return allocdup(allocator, rc, "daily"); + } else if (!spn->secs && !spn->mins && !spn->hours + && !spn->days && spn->weeks != 0 && !spn->years) { + return allocdup(allocator, rc, "weekly"); + } else if (!spn->secs && !spn->mins && !spn->hours + && !spn->days && !spn->weeks && spn->years != 0) { + return allocdup(allocator, rc, "yearly"); + } else { + return hmd_spn_str(spn, allocator, rc); + } +} + enum hmd_err hmd_date_parse_daydate_reltime_weekday(struct hmd_date *date, char *arg) { @@ -491,16 +631,12 @@ hmd_date_str_reltime_coarse(struct hmd_date *date, now = time(NULL); err = time2tm(&nowtm, (uint64_t) now); - if (err) { - *rc = (int) err; - return NULL; - } + if (err && rc) *rc = (int) err; + if (err) return NULL; err = time2tm(&datetm, date->ts); - if (err) { - *rc = (int) err; - return NULL; - } + if (err && rc) *rc = (int) err; + if (err) return NULL; if (DAYS(&datetm) == DAYS(&nowtm) - 1) { datestr = strfmt(allocator, rc, "yesterday"); @@ -518,7 +654,7 @@ hmd_date_str_reltime_coarse(struct hmd_date *date, datestr = strfmt(allocator, rc, "next %s", hmd_wdnames[datetm.tm_wday]); } else { - *rc = HMD_ERR_FMT; + if (rc) *rc = HMD_ERR_FMT; return NULL; } @@ -535,10 +671,8 @@ hmd_date_str_iso8601(struct hmd_date *date, char *str; err = time2tm(&tm, date->ts); - if (err) { - *rc = (int) err; - return NULL; - } + if (err && rc) *rc = (int) err; + if (err) return NULL; str = allocator->alloc(allocator, 20, rc); if (!str && rc) *rc = -*rc; @@ -573,16 +707,12 @@ hmd_date_str(struct hmd_date *date, now = time(NULL); err = time2tm(&nowtm, (uint64_t) now); - if (err) { - *rc = (int) err; - return NULL; - } + if (err && rc) *rc = (int) err; + if (err) return NULL; err = time2tm(&datetm, date->ts); - if (err) { - *rc = (int) err; - return NULL; - } + if (err && rc) *rc = (int) err; + if (err) return NULL; if (DAYS(&nowtm) == DAYS(&datetm)) return hmd_date_str_reltime_exact(date, allocator, rc);