3

Can someone please suggest how can I retrieve linux time using

struct timespec ts

type? It just gives me time since Epoch. Can I get the actual Linux time using this datatype?

Brief Background: I am writing a logger utility on embedded device with timestamp resolution in milli/micro seconds. The source adds timestamp which is consumed by destination component.

struct stLogItem logitem; //stLogItem has a member, struct timespec ts    
clock_gettime(clk_id, &logitem.ts);

The destination component is printing this log timestamp on file/console. But the date which is printed out is time since Epoch, and not the actual Linux date.

The printed data is: 1970-01-01 23:30:07.586864475

Whereas, the Linux date is different as shown below:

root@imh:# date

Tue Nov 14 11:34:12 UTC 2017

Its not a format issue. It is about getting the current Linux time (in nano seconds).

Irfan
  • 2,713
  • 3
  • 29
  • 43
  • 6
    What is "actual Linux time"? Time since the first announcement by Linus? :) – unwind Nov 28 '17 at 12:58
  • You might like to have a look at `localtime*()`. – alk Nov 28 '17 at 12:59
  • Tue Nov 14 10:06:34 UTC 2017 (after typing "date" on shell) – Irfan Nov 28 '17 at 12:59
  • Google how to get date and time in linux c. time(), localtime() – Ronny Brendel Nov 28 '17 at 13:00
  • I am using struct timespec for best resolution. – Irfan Nov 28 '17 at 13:01
  • does it suggest that I can not retrieve Linux time through "struct timespec"? – Irfan Nov 28 '17 at 13:03
  • Returning time as a simple scalar is a feature, not a bug. You have to convert it to a human-readable format separately if that's what you want. – tripleee Nov 28 '17 at 13:06
  • Possible duplicate of [How do I make a human-readable string out of a struct tm?](https://stackoverflow.com/questions/3937301/how-do-i-make-a-human-readable-string-out-of-a-struct-tm) – tripleee Nov 28 '17 at 13:07
  • Do you want to get the elapsed real time using the `clock_gettime`call? – danglingpointer Nov 28 '17 at 13:08
  • Its not a conversion problem. Its about getting current time. I am already printing human readable time on console. The output shows it: " 1970-01-01 22:17:01.776871940" – Irfan Nov 28 '17 at 13:08
  • using timespec, I want to retrieve the same date and time as returned by localtime(...) function. – Irfan Nov 28 '17 at 13:10
  • What is `clk_id` and how is it set? – alk Nov 28 '17 at 14:31
  • 1
    This update isn't enough. You need to show the actual code that produces the invalid output. – dbush Nov 28 '17 at 14:33
  • @dbush, directly after retrieving the ts value by line, "clock_gettime(clk_id, &logitem.ts);", I have used the solution which you have provided and the solution alk has provided. Both of these solutions produce the output I mentioned. – Irfan Nov 28 '17 at 14:38
  • Please refer to my first comment on alk's post. Which shows output using his solution. – Irfan Nov 28 '17 at 14:40
  • When I run his code I get "Date: Tue Nov 28 09:41:03 2017 378233094ns" as output. If running "date" from the shell gives you the proper time, you're doing sometime different. **Show your code**. – dbush Nov 28 '17 at 14:43
  • @alk, the clk_id is CLOCK_REALTIME. Infact, I pass it as it is. Means clk_id was just placeholder for actual value. – Irfan Nov 28 '17 at 14:46
  • Did you take my code as is and compiled it as is, no additions, no removals (despite the `...`) to a single main? – alk Nov 28 '17 at 14:48

3 Answers3

7

After calling clock_gettime, ts.tv_sec, which has type time_t, is populated with the timestamp in seconds since the epoch. You can pass that directly to localtime:

struct timespec ts;    
clock_gettime(clk_id, &ts);
struct tm *my_tm = localtime(&ts.tv_sec);

Now my_tm points to a struct tm which has the time broken down into year / month / day / hour / minute / second, and ts.tv_nsec has the nanosecond portion.

alk
  • 69,737
  • 10
  • 105
  • 255
dbush
  • 205,898
  • 23
  • 218
  • 273
  • Note that the `tm_year` and `tm_mon` fields of the broken down time have oddball encodings, and `strftime()` can be used to format time strings (but nanoseconds have to be handled separately and carefully). – Jonathan Leffler Nov 28 '17 at 14:49
  • Here is the documentation on `localtime()` and `localtime_r()`: https://man7.org/linux/man-pages/man3/localtime.3p.html. Note that `localtime()` is _not_ thread-safe. If you need a threadsafe version, use the `localtime_r()` "re-entrant" (thread-safe) version. – Gabriel Staples Apr 15 '22 at 20:24
  • Thank you! I used your example to write my own. I needed the help to see how to use `localtime()`. I've expanded your answer a lot and added a bunch more output, such as formatting the output as `Fri Apr 15 14:05:12 2022 -0700`, [in my own answer here.](https://stackoverflow.com/a/71889097/4561887) – Gabriel Staples Apr 15 '22 at 21:30
3

What keeps you from doing:

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

int main(void)
{
  struct timespec ts;    
  clock_gettime(CLOCK_REALTIME, &ts);

  char * p = ctime(&ts.tv_sec); /* Note that ctime() isn't thread-safe. */
  p[strcspn(p, "\r\n")] = 0;

  printf("Date: %s %ldns\n", p, ts.tv_nsec);

  ...
}

From the relevant documentation:

All implementations support the system-wide real-time clock, which is identified by CLOCK_REALTIME. Its time represents seconds and nanoseconds since the Epoch.

(POSIX documentation is here.)

alk
  • 69,737
  • 10
  • 105
  • 255
  • this just prints the output "Thu Jan 1 23:28:20 1970 690758173ns " where as the result of "date" command gives output "Tue Nov 14 10:59:57 UTC 2017". You can see two dates have different time values (just forget about format) – Irfan Nov 28 '17 at 13:54
  • And this is due to the fact that ts variable has that time. ctime just returns the input time as string – Irfan Nov 28 '17 at 13:59
  • @Irfan: If the above code really gives you "*Thu Jan 1 23:28:20 1970 ...*" it seems your system clock is messed up, running on the wrong time. – alk Nov 28 '17 at 14:02
  • 2
    @Irfan You should update your question with the actual code that produces that output. It's likely you're doing something else wrong. – dbush Nov 28 '17 at 14:09
1

Seeing @dbush's answer is exactly what I needed to see to learn how to convert Linux timestamps to usable local times!

I took it further though and printed the timestamp output in more ways, including in human-readable strings with day, month, year, etc., like this: Fri Apr 15 14:05:12 2022 -0700.

Here is the full source code, from my eRCaGuy_hello_world repo:

timing_clock_gettime_basic.c

// This define is required to bring in some of the extra POSIX features defined
// in `<time.h>`. Depending on your compiler settings, it may be required to
// get access to `clock_gettime()`. Using `-std=gnu17`, however, brings it in
// automatically since the compiler then uses the "gnu c" language instead of
// the standard "c" language.
//
// #define _POSIX_C_SOURCE 200112L

// Linux includes
// NA

// C includes
#include <errno.h>   // `errno`
#include <inttypes.h> // `PRIu64`
#include <stdbool.h> // For `true` (`1`) and `false` (`0`) macros in C
#include <stdint.h>  // For `uint8_t`, `int8_t`, etc.
#include <stdio.h>   // For `printf()`
#include <string.h>  // `strerror(errno)`
#include <time.h>    // Includes `clock_gettime()` on Linux


#define NS_PER_SEC (1000000000L)
/// Convert seconds to nanoseconds
#define SEC_TO_NS(sec) ((sec)*NS_PER_SEC)


// int main(int argc, char *argv[])  // alternative prototype
int main()
{
    printf("Obtain an NTP-adjusted Real-time Clock timestamp on Linux.\n\n");

    // Obtain a timestamp
    struct timespec ts;
    int retcode = clock_gettime(CLOCK_REALTIME, &ts);
    if (retcode == -1)
    {
        printf("Failed to get a timestamp. errno = %i: %s\n",
            errno, strerror(errno));
    }

    // Print seconds.nanoseconds
    printf("timestamp = %li.%09li sec.\n\n", ts.tv_sec, ts.tv_nsec);

    // Convert it to just `uint64_t` nanoseconds
    // See: eRCaGuy_hello_world/c/timinglib.c
    uint64_t ns = SEC_TO_NS((uint64_t)ts.tv_sec) + (uint64_t)ts.tv_nsec;
    printf("timestamp = %" PRIu64 " nanoseconds.\n\n", ns);

    // Convert it to a local time stored in `struct tm`. Use the re-entrant
    // (thread-safe) version of the function, called `localtime_r()`. See:
    // 1. https://man7.org/linux/man-pages/man3/localtime.3p.html
    // 1. https://stackoverflow.com/a/47532938/4561887
    // 1. `struct tm`: https://man7.org/linux/man-pages/man3/ctime.3.html
    struct tm localtime_struct;
    // Note: retptr means "return pointer"
    struct tm * retptr = localtime_r(&ts.tv_sec, &localtime_struct);
    if (retptr == NULL)
    {
        printf("Failed to convert to localtime. errno = %i: %s\n",
            errno, strerror(errno));
    }
    printf("localtime_struct contains:\n"
           "  ns    = %li\n" // Nanoseconds (0-999999999); NOT FROM THIS STRUCT
           "  sec   = %i\n"  // Seconds (0-60)
           "  min   = %i\n"  // Minutes (0-59)
           "  hour  = %i\n"  // Hours (0-23)
           "  mday  = %i\n"  // Day of the month (1-31)
           "  mon   = %i\n"  // Month (0-11)
           "  year  = %i\n"  // Year - 1900
           "  wday  = %i\n"  // Day of the week (0-6, Sunday = 0)
           "  yday  = %i\n"  // Day in the year (0-365, 1 Jan = 0)
           "  isdst = %i\n"  // Daylight saving time
           "\n",
           ts.tv_nsec,
           localtime_struct.tm_sec,
           localtime_struct.tm_min,
           localtime_struct.tm_hour,
           localtime_struct.tm_mday,
           localtime_struct.tm_mon,
           localtime_struct.tm_year,
           localtime_struct.tm_wday,
           localtime_struct.tm_yday,
           localtime_struct.tm_isdst);

    // Convert the `struct tm` localtime struct to a human-readable string in
    // normal human time units of Day, Month, Year, etc.
    // - This is the format string required to output timestamps in the exact
    //   same format as `git` uses when you `git commit`.
    const char * time_format_str = "%a %b %-d %H:%M:%S %Y %z";
    char time_str[100];
    size_t bytes_written = strftime(time_str, sizeof(time_str),
        time_format_str, &localtime_struct);
    if (bytes_written == 0)
    {
        printf("Failed to convert `struct tm` to a human-readable "
               "time string.\n");
    }
    printf("Formatted local time string = %s\n\n", time_str);


    return 0;
}

Sample build and run command, and output:

eRCaGuy_hello_world/c$ gcc -Wall -Wextra -Werror -O3 -std=gnu17 timing_clock_gettime_full_demo.c -o bin/a && bin/a
Obtain an NTP-adjusted Real-time Clock timestamp on Linux.

timestamp = 1650056712.080211270 sec.

timestamp = 1650056712080211270 nanoseconds.

localtime_struct contains:
  ns    = 80211270
  sec   = 12
  min   = 5
  hour  = 14
  mday  = 15
  mon   = 3
  year  = 122
  wday  = 5
  yday  = 104
  isdst = 0

Formatted local time string = Fri Apr 15 14:05:12 2022 -0700

The above code is part of my more-thorough answer here: How to read an NTP-adjusted RTC (Real-time clock) timestamp on Linux

Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265