40

How to format struct timespec to string? This structure is returned e.g. by clock_gettime() on Linux gcc:

struct timespec {
    time_t   tv_sec;        /* seconds */
    long     tv_nsec;       /* nanoseconds */
};
caf
  • 233,326
  • 40
  • 323
  • 462
Cartesius00
  • 23,584
  • 43
  • 124
  • 195
  • 4
    It depends what you want the string to look like. Also, don't use both the `c++` and `c` tags. Tag with the language you are using. – David Schwartz Nov 29 '11 at 00:25
  • 1
    Which are you using, c++ or c? Furthermore, what do you mean format? You'll need to specify the outputformat you want (seconds since epoch is trivial, others might be harder, depending on how exactly it should look like). – Grizzly Nov 29 '11 at 00:28

5 Answers5

53

One way to format it is:

printf("%lld.%.9ld", (long long)ts.tv_sec, ts.tv_nsec);
caf
  • 233,326
  • 40
  • 323
  • 462
  • This works but I'm curious to know why %.9ld was chosen and not something more straightforward like %09ld? – Zectbumo Feb 15 '19 at 10:54
  • 1
    @Zectbumo: It's just a matter of whether you think of it as printing a decimal integer with a minimum number of digits, or left-padding the converted integer with zero characters to a minimum field width. It's the same number of characters and the result is the same either way. – caf Feb 15 '19 at 13:06
  • 1
    While this technically answers the OP's question (as no format was specified), most programmers will want something more readable than a literal printout of the number of seconds and nanoseconds. For instance, if the timespec value represents a calendar date and time then a timestamp format would be preferable, whereas for elapsed time it would be most useful to break it down into something like hours, minutes, seconds, and nanoseconds (for example). A complete answer would address such uses. – Adrian Lopez Aug 14 '20 at 01:52
  • This is definitely the best answer for my use case. – bruceg Jun 20 '23 at 17:10
25

I wanted to ask the same question. Here is my current solution to obtain a string like this: 2013-02-07 09:24:40.749355372 I am not sure if there is a more straight forward solution than this, but at least the string format is freely configurable with this approach.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define NANO 1000000000L

// buf needs to store 30 characters
int timespec2str(char *buf, uint len, struct timespec *ts) {
    int ret;
    struct tm t;

    tzset();
    if (localtime_r(&(ts->tv_sec), &t) == NULL)
        return 1;

    ret = strftime(buf, len, "%F %T", &t);
    if (ret == 0)
        return 2;
    len -= ret - 1;

    ret = snprintf(&buf[strlen(buf)], len, ".%09ld", ts->tv_nsec);
    if (ret >= len)
        return 3;

    return 0;
}

int main(int argc, char **argv) {
    clockid_t clk_id = CLOCK_REALTIME;
    const uint TIME_FMT = strlen("2012-12-31 12:59:59.123456789") + 1;
    char timestr[TIME_FMT];

    struct timespec ts, res;
    clock_getres(clk_id, &res);
    clock_gettime(clk_id, &ts);

    if (timespec2str(timestr, sizeof(timestr), &ts) != 0) {
        printf("timespec2str failed!\n");
        return EXIT_FAILURE;
    } else {
        unsigned long resol = res.tv_sec * NANO + res.tv_nsec;
        printf("CLOCK_REALTIME: res=%ld ns, time=%s\n", resol, timestr);
        return EXIT_SUCCESS;
    }
}

output:

gcc mwe.c -lrt 
$ ./a.out 
CLOCK_REALTIME: res=1 ns, time=2013-02-07 13:41:17.994326501
Thomas Tempelmann
  • 11,045
  • 8
  • 74
  • 149
stefanct
  • 2,503
  • 1
  • 28
  • 32
  • Does it have to be `len -= ret - 1;`? Why is `len -= ret;` not good? – russoue Nov 12 '14 at 18:45
  • Instead of strlen you could use sizeof. The same stupid, but compile-time-resolved. – Ethouris Jan 21 '15 at 16:34
  • The comparison between ret >= len is signed vs. unsigned. Minor change. – bjackfly Jan 22 '15 at 14:48
  • @Ethouris any decent compiler will resolve `strlen` for string literals at compile time. But the `strlen` in the `snprintf` paramter could be replaced indeed. – stefanct Jan 24 '15 at 21:51
  • 1
    @russoue i can't remember to be honest, but i guess because i calculate without the trailing `\0` that is included in the returned value of `strftime`. – stefanct Jan 24 '15 at 21:54
  • 1
    sizeof(str)-1 will give you the same result - although you are adding 1 there, so sizeof will replace the complete expression. And my comment was rather sarcastic :P – Ethouris Jan 26 '15 at 08:17
6

The following will return an ISO8601 and RFC3339-compliant UTC timestamp, including nanoseconds.

It uses strftime(), which works with struct timespec just as well as with struct timeval because all it cares about is the number of seconds, which both provide. Nanoseconds are then appended (careful to pad with zeros!) as well as the UTC suffix 'Z'.

Example output: 2021-01-19T04:50:01.435561072Z

#include <stdio.h>
#include <time.h>
#include <sys/time.h>

int utc_system_timestamp(char[]);

int main(void) {
    char buf[31];
    utc_system_timestamp(buf);
    printf("%s\n", buf);
}

// Allocate exactly 31 bytes for buf
int utc_system_timestamp(char buf[]) {
    const int bufsize = 31;
    const int tmpsize = 21;
    struct timespec now;
    struct tm tm;
    int retval = clock_gettime(CLOCK_REALTIME, &now);
    gmtime_r(&now.tv_sec, &tm);
    strftime(buf, tmpsize, "%Y-%m-%dT%H:%M:%S.", &tm);
    sprintf(buf + tmpsize -1, "%09luZ", now.tv_nsec);
    return retval;
}

GCC command line example (note the -lrt):

gcc foo.c -o foo -lrt
Charles Burns
  • 10,310
  • 7
  • 64
  • 81
  • 1
    `sprintf(buf, "%s%09luZ", buf, now.tv_nsec);` is undefined behavior, the destination buffer may not overlap with any of the other arguments. `sprintf(buf+strlen(buf), "%09luZ", now.tv_nsec);` is the way to go. – mch May 27 '21 at 14:54
  • @mch: Indeed, this violated C99 §7.19.6.5 which states, `If copying takes place between objects that overlap, the behavior is undefined.` Fixed. Thank you for the collaboration. – Charles Burns May 28 '21 at 23:01
1

You can pass the tv_sec parameter to some of the formatting function. Have a look at gmtime, localtime(). Then look at snprintf.

Adrian Cornish
  • 23,227
  • 13
  • 61
  • 77
-11

You could use a std::stringstream. You can stream anything into it:

std::stringstream stream;
stream << 5.7;
stream << foo.bar;

std::string s = stream.str();

That should be a quite general approach. (Works only for C++, but you asked the question for this language too.)

Sh4pe
  • 1,800
  • 1
  • 14
  • 30
  • 12
    -1: No, you can't just "stream anything into it"; the appropriate `operator<<` overload must exist. [And, for `timespec`, it does not](http://coliru.stacked-crooked.com/a/844cc3f81eff9cb5). – Lightness Races in Orbit Mar 21 '14 at 11:16