Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reasonable snprintf-like alternatives to strftime?

Are there any standard (C, C++, POSIX, Linux...) alternatives to strftime which can be used to

  1. calculate the required size of the string buffer for a given format and time,
  2. truncate the output (instead of leaving the array contents undefined like strftime does) if buffer size is less than required for full output.

For example, snprintf-like semantics for date/time formatting which accept strftime format strings would do perfectly.

Functions like std::put_time in C++11 and later are not an option, because these may attempt to allocate extra memory dynamically and may throw exceptions.

like image 309
jotik Avatar asked Jul 06 '17 10:07

jotik


1 Answers

Could keep trying larger buffers until code succeeds (or decides this is too much). The below uses a VLA (not C++), to sneakily avoid "attempt to allocate extra memory dynamically" - wink wink.

Simply allocating a large buffer, say char buf[J_STRFTIME_MAX], should be sufficient for practical coding. @Michaël Roy and avoid the iterative approach.

#include <stdio.h>
#include <time.h>
#define J_STRFTIME_MAX 100

size_t j_strftime(char * s, size_t maxsize, const char * fmt, const struct tm * t) {
  size_t sz = strftime(s, maxsize, fmt, t);
  if (sz) {
    return sz;
  }
  size_t new_size = maxsize ? maxsize : 1;
  do {
    new_size *= 2;
    char new_s[new_size];
    sz = strftime(new_s, sizeof new_s, fmt, t);
    if (sz) {
      s[0] = 0;
      // strncat(s, new_s, maxsize);
      strncat(s, new_s, maxsize - 1);
      return strlen(new_s);
    }
  } while (sz < J_STRFTIME_MAX/2);
  return 0;
}

int main() {
  time_t now;
  time(&now);
  struct tm tm = *gmtime(&now);
  for (size_t i = 1; i < 30; i += 3) {
    char s[i];
    size_t sz = j_strftime(s, sizeof s, "%c", &tm);
    printf("%2zu %2zu <%s>\n", i, sz, s);
  }
}

Output

 1 24 <T>
 4 24 <Thu >
 7 24 <Thu Jul>
10 24 <Thu Jul  6>
13 24 <Thu Jul  6 14>
16 24 <Thu Jul  6 14:45>
19 24 <Thu Jul  6 14:45:00>
22 24 <Thu Jul  6 14:45:00 20>
25 24 <Thu Jul  6 14:45:00 2017>
28 24 <Thu Jul  6 14:45:00 2017>

Non-iterative

size_t j_strftime2(char * s, size_t maxsize, const char * fmt, const struct tm * t) {
  size_t sz = strftime(s, maxsize, fmt, t);
  if (sz == 0) {
    char new_s[J_STRFTIME_MAX];
    sz = strftime(new_s, sizeof new_s, fmt, t);
    if (sz == 0) {
      return 0;  // Too too big
    }
    s[0] = 0;
    // strncat(s, new_s, maxsize);
    strncat(s, new_s, maxsize - 1);
  }
  return sz;
}

[Edit] Code corrected.

like image 52
chux - Reinstate Monica Avatar answered Nov 16 '22 15:11

chux - Reinstate Monica