Another attempt below, with known open issues:

1) it seems something has to be said about tz variables, either the
function "always sets" them, "never sets" them, or (in new text below)
"may set" them depending on what other functions are called.  Not
optimal, but better than not documenting it.  We can inspect the
implementation later and change this too.

2) can we implement this in a way that it never fails?  I still allow
return==NULL to indicate errors below, until we can confirm that it is
possible to implement this in a way that cannot fail.  Returning "magic"
values like "1970-01-01" seems worse than NULL to me, since then callers
will need to do string comparisons to catch error situations.

3) below it says that nothing can be assumed about thread safety (beyond
that it depend on an environment variable), which seems a bit
sub-optimal, but let's see how this ends up being implemented and if we
can say something better.  Saying that nothing can be assumed about
thread safety is better than not saying anything, IMO.

4) Bruno suggested not documenting anything about week/month names, what
calendar is used, the year < 1 handling or expected output string
lengths -- I tend to disagree: at least my goal is for this function to
be a drop-in well-defined superset for ctime.  For ctime those
properties are either specified and documented (and then we want to
document that this function is compatible) or left
undefined/undocumented (and then we want to provide well-defined
portable documented semantics).  The later problem with ctime seems to
be the reason we need to introduce a safer variant in the first place.

5) Naming.  I'm okay with 'safer_ctime' but still think it is ugly.
Bruno's suggestion of 'c_strnl_from_time' sounds better to me, even
though it is a mouthful.  How about 'strtime.h' and 'strctime'?  If
there is a need to offer a drop-in for asctime, then 'strasctime' is
relevant.  This seems more in line with existing C stdlib functions.

/Simon

/* strtime.h -- safe versions of time-related string functions.
   Copyright (C) 2024 FSF
   Authors: Paul Eggert, Bruno Haible, Simon Josefsson
   License: LGPL-2+
 */

#include <stdint.h>
#include <time.h>

/* This evaluates to 35 on typical machines today, and will grow
   automatically if time_t gets wider - it could even exceed 70 if
   needed.  7 = floor(log10(60*60*24*365)). */
#define STRCTIME_BUFSIZE \
    (sizeof "Wed Jun 30 21:49:08 \n" \
     + INT_STRLEN_BOUND (time_t) - 7)

/* Convert WHEN representing the number of seconds related to epoch,
   1970-01-01 00:00:00 +0000 (UTC), to a fixed locale-independent
   NUL-terminated string such as "Wed Jun 30 21:49:08 1993\n\0",
   relative to the user's specified timezone (TZ environment variable),
   using abbreviations for the days of the week as "Sun", "Mon", "Tue",
   "Wed", "Thu", "Fri", and "Sat" and abbreviations for the months as
   "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
   "Nov", and "Dec".  The function may set the external variables
   tzname, timezone, and daylight (see tzset(3)) with information about
   the current timezone.  The output is copied into STR which should
   have room for at least STRCTIME_BUFSIZE bytes.  If STR is NULL, a
   pointer to a global statically pre-allocated buffer of size
   STRCTIME_BUFSIZE is used instead.  For years 1000 to 9999 inclusive
   the output string length is 26 characters including the final NUL
   byte.  The string length may be shorter for some years before 1000,
   and larger for years after 9999 or before -999.  The years are not
   padded with whitespace or zeros, so valid outputs include strings
   "Wed Jun 30 21:49:08 623\n" and "Wed Jun 30 21:49:08 11147\n", and
   for negative years strings such as "Wed Jun 30 21:49:08 -42\n".  The
   preloptic Gregorian calendar is used for all years, to cover years
   before the Gregorian calendar was adopted; and for years before 1 the
   ISO 8601 approach to have years 2, 1, 0, -1, and so on is used
   instead of having 2 BC, 1 BC, AD 1, AD 2.  On systems with a 64-bit
   time_t type, the year value may be large as in strings looking like
   "Sun Sep 16 01:03:52 -292471206706\n\0", and future systems with
   larger time_t types may lead to even longer strings.  If WHEN cannot
   be converted into a string, NULL is returned and errno is set to an
   error, otherwise on success STR (or a pointer to the global buffer)
   is returned.  The result depends on the environment variable TZ; no
   further Thread safety attributes can be reliably assumed about this
   function. */

char *strctime (time_t when, char *str);

Attachment: signature.asc
Description: PGP signature

Reply via email to