On 2024-02-08 09:00, Bruno Haible wrote:
char *str_from_time (char *resultbuf, size_t *lengthp,
bool i18n, const char *format,
time_t t, bool local_time, int ns);
char *str_from_tm (char *resultbuf, size_t *lengthp,
bool i18n, const char *format,
struct tm const *tp, bool local_time, int ns);
I suggest some simplifications and some complications:
1. There's no need for the from_tm flavor. That just complicates things,
because callers use localtime or localtime_r or gmtime or gmtime_r or
whatever, and deal with those other functions failing. Many programs
don't do this correctly, so let's have str_from_done do it and be done
with it.
2. Instead of i18n being a boolean, why not make it a locale_t? That's
more general.
3. Likewise for localtime; why not make it a timezone_t? Sure it's
overkill, but having a boolean at all is overkill in some sense.
4. We can also establish simpler APIs for common cases, which call the
more-general API.
Migration from ctime:
s = ctime (tp);
=>
char buf[64];
size_t buf_size = sizeof (buf);
s = str_from_time (buf, &buf_size, false, "%a %b %e %H:%M:%S %Y\n", *tp,
true, 0);
if (s == NULL) /* handle error case */;
The migration is more complicated than that, since one must have 'if (s
!= buf) free (s);' afterwards.
Better to have struct timezone than separate time_t and ns args.
In practice I don't think these buffers will overflow, as time strings
are short. So there's little need for malloc.
So how about the following API instead?
size_t str_from_time (char *restrict s, idx_t maxsize,
char const *format, struct timespec t,
locale_t locale, timezone_t tz);
If localtime_rz fails, this substitutes the decimal representation of T.
If the string doesn't fit this returns 0 and (if MAXSIZE is nonzero)
stores some truncation of the string into S.
We also arrange for identifiers C_locale and local_tz to stand for
the C locale and the local timezone, and CTIME_BUFSIZE and ctime_fmt to
be appropriate buffer sizes and formats for ctime-equivalent calls.
Migration from ctime:
c = ctime (tp);
=>
char c[CTIME_BUFSIZE];
str_from_time (c, sizeof c, ctime_fmt,
(struct timespec) { .tv_sec = *tp },
C_locale, local_tz);
and we can package this up into a function like this:
char c[CTIME_BUFSIZE];
safer_ctime (c, *tp);
if people prefer simplicity.