ITS Time API Developer Guide
V2X messages need time, but not "PC time" and not plain UTC. They need ETSI ITS time: a TAI-based time scale starting at 2004-01-01 00:00:00 UTC and used in ETSI ITS-G5 / C-ITS messages (see ETSI TS 102 894-2).
On the cube:evk, ITS time is derived from GNSS → chrony → system clock → cube:time. Applications read it through either the C API or the C++ API.
This chapter covers:
- What ITS time is and why we use it
- How to check if the cube is synchronized
- How to read the current ITS timestamp (C and C++)
- How to get the current TAI–UTC offset
1. Background: ITS time on cube:evk
As described in the Time Synchronization chapter, the device runs chrony and locks the system clock to the GNSS PPS. This provides:
- A stable TAI time base
- A known TAI–UTC offset (leap seconds)
- A correct ITS time (TAI seconds since 2004-01-01)
The CLI tool:
cube-timectl showshows the same information that is available from code:
- Current ITS timestamp
- Whether the system time is synchronized
- Current TAI–UTC offset
- Leap second status
The library cube:time is the programmatic way to access this.
2. C API (time.h)
For C or simple C++ code, use the two helper functions from time.h:
/** * \brief get current ITS timestamp in milliseconds * \return milliseconds since ITS epoch start (-1 if not synchronized) */int64_t cube_time_now_ms();
/** * \brief get current ITS timestamp in microseconds * \return microseconds since ITS epoch start (-1 if not synchronized) */int64_t cube_time_now_us();Example
#include <cube/time.h>#include <stdio.h>
int main(void){ int64_t its_ms = cube_time_now_ms(); if (its_ms < 0) { printf("ITS time not synchronized yet\n"); return 1; }
printf("ITS time: %lld ms since 2004-01-01T00:00:00Z\n", (long long)its_ms); return 0;}Key points:
- Return value
< 0: not synchronized (GNSS / chrony not locked yet) - Value is since ITS epoch (2004-01-01), not the UNIX epoch (1970-01-01)
- Suitable for filling ITS timestamps in messages from pure C
3. C++ API (clock.hpp)
For C++ code, a clock type is provided that behaves like a standard C++ clock:
struct cube::time::EtsiItsClock { static time_point now(); static std::optional<time_point> maybe_now(); static bool is_synchronized(); static duration utc_offset() noexcept; static time_point from_system_clock(std::chrono::system_clock::time_point);};It provides:
now(): ITS time (assumes synchronized)maybe_now(): ITS time only if synchronizedis_synchronized(): boolean checkutc_offset(): current offset between ITS and UTCfrom_system_clock(...): convert asystem_clock::time_pointto ITS
Example: check and print ITS time
#include <cube/time/clock.hpp>#include <chrono>#include <iostream>
int main(){ using namespace std::chrono;
if (!cube::time::EtsiItsClock::is_synchronized()) { std::cout << "ITS clock is NOT synchronized yet\n"; return 0; }
auto now_its = cube::time::EtsiItsClock::now(); auto since_epoch = duration_cast<milliseconds>(now_its.time_since_epoch()); std::cout << "ITS time: " << since_epoch.count() << " ms since 2004-01-01T00:00:00Z (TAI)\n";
return 0;}Example: safe access (no crash when unsynced)
if (auto maybe_its = cube::time::EtsiItsClock::maybe_now()) { using std::chrono::duration_cast; using std::chrono::seconds; auto its_s = duration_cast<seconds>(maybe_its->time_since_epoch()).count(); std::cout << "ITS time: " << its_s << " s\n";} else { std::cout << "ITS time not available (not synchronized)\n";}4. Getting the TAI–UTC Offset
Some V2X stacks (such as cube-tiny-stack) need the current TAI–UTC offset (leap seconds) to format or validate timestamps.
A C++ helper is provided:
#include <cube/time/tai.hpp>#include <chrono>#include <iostream>
int main(){ auto offset = cube::time::tai_utc_offset(); std::cout << "Current TAI-UTC offset: " << offset.count() << " seconds\n";}This should match what you see in:
cube-timectl showand what chrony currently believes.
5. Converting from system_clock
If you already have a timestamp in std::chrono::system_clock (for example, a log entry or sensor timestamp), you can convert it to ITS directly:
auto sys_now = std::chrono::system_clock::now();auto its_now = cube::time::EtsiItsClock::from_system_clock(sys_now);This applies the current ITS–UTC offset and returns an ITS time point.
6. Notes
- The ITS epoch is 2004-01-01, not 1970-01-01
- ITS uses TAI seconds; UTC has leap seconds, TAI does not
- This is why TAI–UTC is tracked explicitly
- If PPS/GNSS disappears, the clock may temporarily stay synchronized. Verify with
cube-timectl show.
In Summary
- Use the C API if you need "ITS now" as an integer.
- Use the C++ API with chrono for safe optional timestamps.
- Both APIs expose the same information as
cube-timectl show. - This is the same time base the V2X stack uses, so the application and the radio stay aligned.

