ITS Time API Developer Guide
V2X messages need time — but not “PC time” and not even plain UTC. They need ETSI ITS time, which is 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 this ITS time is derived from GNSS → chrony → system clock → cube:time, and you can read it from your own applications using either the C API or the C++ API.
This chapter explains:
- 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 gives us:
- a stable TAI time base
- a known TAI–UTC offset (leap seconds)
- and therefore a correct ITS time (TAI seconds since 2004-01-01)
The CLI tool:
cube-timectl showshows exactly the same information that you can get from code:
- current ITS timestamp
- whether the system time is synchronized
- current TAI–UTC offset
- leap second status
The library cube:time is just the programmatic way to get this.
2. C API (time.h)
If you’re writing 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 UNIX epoch (1970-01-01)
- perfect for filling ITS timestamps in messages in pure C
3. C++ API (clock.hpp)
For modern C++ code we provide a proper C++ clock 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);};This gives you:
now()— get ITS time (assumes we are synchronized)maybe_now()— get ITS time only if synchronizedis_synchronized()— quick boolean checkutc_offset()— how far ITS is currently offset from 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 (like cube-tiny-stack) need to know the current TAI–UTC offset (leap seconds) to format or validate timestamps.
For that there is a tiny C++ helper:
#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
Sometimes you already have a timestamp in std::chrono::system_clock (e.g. log entry, sensor timestamp) and you just want to convert it to ITS.
That’s what this is for:
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 a proper ITS time point.
6. Notes
- ITS epoch is 2004-01-01, not 1970-01-01
- ITS uses TAI seconds; UTC has leap seconds, TAI does not
- That’s why we must track TAI–UTC
- If PPS/GNSS disappears, the clock may temporarily stay synchronized — you can check this via
cube-timectl show
In Summary
- Use C API if you just need “ITS now” as an integer.
- Use C++ API if you work with chrono and want safe optional timestamps.
- Both APIs expose the same information as you see in
cube-timectl show. - This is the same time base the V2X stack uses — so your app and the radio stay in the same time universe.

