'Query RTC and NTP time in Linux?
The way the Linux kernel handle time is somewhat complicated as it uses different source of time, and regularly resynchronize everything. There are command-line tools (like hwclock
) to query the RTC time explicitely. However I would like to do it programmatically, inside a C or C++ program. If Linux provides command-line tools, I assume that there is a way to do this programmatically, but so far I haven't been able to find the right function, and how to use it.
So my questions are:
Solution 1:[1]
Use time(time_t*) or clock_gettime(clockid_t,struct timespec *) if you wish for more precision, from the time.h header for the RTC time.
there is an article describing how to get query NTP time ( which was a google search away ).
describing the structure of an NTP packet
typedef struct
{
uint8_t li_vn_mode; // Eight bits. li, vn, and mode.
// li. Two bits. Leap indicator.
// vn. Three bits. Version number of the protocol.
// mode. Three bits. Client will pick mode 3 for client.
uint8_t stratum; // Eight bits. Stratum level of the local clock.
uint8_t poll; // Eight bits. Maximum interval between successive messages.
uint8_t precision; // Eight bits. Precision of the local clock.
uint32_t rootDelay; // 32 bits. Total round trip delay time.
uint32_t rootDispersion; // 32 bits. Max error aloud from primary clock source.
uint32_t refId; // 32 bits. Reference clock identifier.
uint32_t refTm_s; // 32 bits. Reference time-stamp seconds.
uint32_t refTm_f; // 32 bits. Reference time-stamp fraction of a second.
uint32_t origTm_s; // 32 bits. Originate time-stamp seconds.
uint32_t origTm_f; // 32 bits. Originate time-stamp fraction of a second.
uint32_t rxTm_s; // 32 bits. Received time-stamp seconds.
uint32_t rxTm_f; // 32 bits. Received time-stamp fraction of a second.
uint32_t txTm_s; // 32 bits and the most important field the client cares about. Transmit time-stamp seconds.
uint32_t txTm_f; // 32 bits. Transmit time-stamp fraction of a second.
} ntp_packet; // Total: 384 bits or 48 bytes.
sending a query
// Call up the server using its IP address and port number.
if ( connect( sockfd, ( struct sockaddr * ) &serv_addr, sizeof( serv_addr) ) < 0 )
error( "ERROR connecting" );
// Send it the NTP packet it wants. If n == -1, it failed.
n = write( sockfd, ( char* ) &packet, sizeof( ntp_packet ) );
if ( n < 0 )
error( "ERROR writing to socket" );
reading the response
// Wait and receive the packet back from the server. If n == -1, it failed.
n = read( sockfd, ( char* ) &packet, sizeof( ntp_packet ) );
if ( n < 0 )
error( "ERROR reading from socket" );
and parsing it
// These two fields contain the time-stamp seconds as the packet left the NTP server.
// The number of seconds correspond to the seconds passed since 1900.
// ntohl() converts the bit/byte order from the network's to host's "endianness".
packet.txTm_s = ntohl( packet.txTm_s ); // Time-stamp seconds.
packet.txTm_f = ntohl( packet.txTm_f ); // Time-stamp fraction of a second.
// Extract the 32 bits that represent the time-stamp seconds (since NTP epoch) from when the packet left the server.
// Subtract 70 years worth of seconds from the seconds since 1900.
// This leaves the seconds since the UNIX epoch of 1970.
// (1900)------------------(1970)**************************************(Time Packet Left the Server)
time_t txTm = ( time_t ) ( packet.txTm_s - NTP_TIMESTAMP_DELTA );
Solution 2:[2]
A real-time clock (RTC) is a clock which keeps time relative to some absolute time epoch, such as counting the number of seconds since January 1, 1970 at midnight UTC/GMT (sometimes written as 1970-01-01 UTC
). This is opposed to a regular clock which might count seconds since boot-up, for instance. Note that a real-time clock has nothing to do with a real-time operating system, which is a completely different concept.
In order to continue to keep time while a computer or system is unplugged, a real-time clock must have a backup battery, such as a tiny coin cell battery, to allow it to keep ticking while the system is without power.
Since real-time clocks keep time from a known epoch, you can use them to obtain the year, month, day, hour, minute, second, etc.
"NTP" time simply means: "Network Time Protocol (NTP)" time. This is a synchronization technique to periodically adjust each computer's local RTC so that they all read the same absolute time, at the same time. It takes real-time clocks to the next level of synchronization, since they can otherwise drift over time. NTP-adjusted RTCs can be within a few milliseconds of each other when on the same network (see: https://en.wikipedia.org/wiki/Network_Time_Protocol).
How to read an NTP-adjusted RTC (Real-time clock) timestamp on Linux
So, to read the real-time clock on Linux you do the same thing you'd do to read the NTP time on Linux, since they are one and the same: the RTC is periodically adjusted according to the NTP synchronization is all.
Obtain such a timestamp with the POSIX/Linux function clock_gettime()
, with the CLOCK_REALTIME
clock chosen. See: https://man7.org/linux/man-pages/man3/clock_gettime.3.html. It states (emphasis added):
All implementations support the system-wide real-time clock, which is identified by
CLOCK_REALTIME
. Its time represents seconds and nanoseconds since the Epoch. When its time is changed, timers for a relative interval are unaffected, but timers for an absolute point in time are affected.
and:
CLOCK_REALTIME
A settable system-wide clock that measures real (i.e., wall-clock) time. Setting this clock requires appropriate privileges. This clock is affected by discontinuous jumps in the system time (e.g., if the system administrator manually changes the clock), and by the incremental adjustments performed by
adjtime(3)
and NTP.
Here is an example of getting an NTP-adjusted RTC timestamp in C or C++ on a POSIX or Linux system, with full error checking.
From my code timing_clock_gettime_full_demo.c:
// 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));
}
Now, you have whole seconds stored in ts.tv_sec
and whole nanoseconds from 0-999999999 (< 1 sec) stored in tv.tv_nsec
.
You can print this in all sorts of ways. Here is the full, runnable code with a bunch of advanced printing demos. See the link just above for the full code online.
// 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
Use a system call or pipe
Keep in mind that you can also call any system call in C or C++ too, if you want. Meaning: any command-line call you can call by hand yourself in the terminal, you can call in C or C++ too.
So, if there is some system call that you can't find a C or C++ API to, just call it as a command-line system call.
The command is system()
. I have an example of that here: C equivalent of Python's exec() function.
If you need to read the output from the system call, you'll need to use a pipe, however. Here are the steps:
How to read from a pipe to read output from a system call in C:
- Run a cmd and open a pipe with
popen()
. - Read the command response from the pipe with
fgets()
, using the file ptr to the pipe obtained frompopen()
. - Close the pipe file with
pclose()
.
For a demo of that, study my answer here: Pipe demo: how to read keyboard presses from a system call pipe in C.
References:
- My code above, in my eRCaGuy_hello_world repo: timing_clock_gettime_full_demo.c
- Retrieve Linux Time using struct timespec
- *****https://man7.org/linux/man-pages/man3/clock_gettime.3.html
- RTC: https://en.wikipedia.org/wiki/Real-time_clock
- NTP: https://en.wikipedia.org/wiki/Network_Time_Protocol
- https://man7.org/linux/man-pages/man3/localtime.3p.html - contains a great
example of
strftime()
at the bottom too! struct tm
: https://man7.org/linux/man-pages/man3/ctime.3.html- https://man7.org/linux/man-pages/man3/strftime.3p.html
- My timing library
See also
- [my answer] Retrieve Linux Time using struct timespec
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | dvhh |
Solution 2 |