74.1 Time
Persistent time type
Version 2 represents an absolute UTC time as a signed 64-bit count of 100-nanosecond units since 00:00:00 on 1 January 1970 UTC:
using LssTime = int64;
constexpr int64 LSS_TIME_UNITS_PER_SECOND = 10'000'000;
The persistent representation is therefore exactly eight octets. The 100-nanosecond unit converts exactly to and from Windows file time. On Linux it retains more precision than commit timestamps normally require while providing a much greater range than signed 64-bit nanoseconds. Negative values represent times before the Unix epoch.
An LssTime is serialized as a little-endian signed 64-bit integer.
It records UTC and never contains a local time-zone or daylight-saving offset.
Windows
Windows FILETIME is an unsigned 64-bit count of 100-nanosecond
units since 1 January 1601 UTC. Conversion therefore requires only addition or subtraction of the
constant difference between the Windows and Unix epochs.
constexpr uint64 FILETIME_UNIX_EPOCH_OFFSET =
116'444'736'000'000'000ULL;
LssTime FromFileTime(FILETIME fileTime)
{
uint64 windowsTime =
(uint64(fileTime.dwHighDateTime) << 32) |
uint64(fileTime.dwLowDateTime);
if (windowsTime >= FILETIME_UNIX_EPOCH_OFFSET)
{
uint64 unixTime = windowsTime - FILETIME_UNIX_EPOCH_OFFSET;
cxAssert(unixTime <= uint64(std::numeric_limits<int64>::max()));
return static_cast<LssTime>(unixTime);
}
uint64 magnitude = FILETIME_UNIX_EPOCH_OFFSET - windowsTime;
cxAssert(magnitude <= uint64(std::numeric_limits<int64>::max()));
return -static_cast<LssTime>(magnitude);
}
FILETIME ToFileTime(LssTime time)
{
cxAssert(time >= -static_cast<int64>(FILETIME_UNIX_EPOCH_OFFSET));
uint64 windowsTime;
if (time >= 0)
{
cxAssert(uint64(time) <=
std::numeric_limits<uint64>::max() - FILETIME_UNIX_EPOCH_OFFSET);
windowsTime = uint64(time) + FILETIME_UNIX_EPOCH_OFFSET;
}
else
{
uint64 magnitude = uint64(-(time + 1)) + 1;
windowsTime = FILETIME_UNIX_EPOCH_OFFSET - magnitude;
}
FILETIME result;
result.dwLowDateTime = uint32(windowsTime);
result.dwHighDateTime = uint32(windowsTime >> 32);
return result;
}
LssTime GetCurrentLssTime()
{
FILETIME fileTime;
GetSystemTimePreciseAsFileTime(&fileTime);
return FromFileTime(fileTime);
}
Production conversion functions must report an out-of-range value rather than relying only on the
illustrative assertions above. Combining the two 32-bit fields arithmetically avoids casting an
unaligned FILETIME* to a 64-bit pointer.
Linux
Linux clock_gettime(CLOCK_REALTIME) returns seconds and
nanoseconds since the Unix epoch in a timespec. Sub-100-nanosecond
precision is truncated when converting to LssTime.
LssTime FromTimespec(const timespec& time)
{
cxAssert(0 <= time.tv_nsec && time.tv_nsec < 1'000'000'000);
return CheckedAdd(
CheckedMultiply(int64(time.tv_sec), LSS_TIME_UNITS_PER_SECOND),
int64(time.tv_nsec) / 100);
}
timespec ToTimespec(LssTime time)
{
int64 seconds = time / LSS_TIME_UNITS_PER_SECOND;
int64 remainder = time % LSS_TIME_UNITS_PER_SECOND;
// C++ remainder has the sign of the dividend. Normalize it so tv_nsec
// is in the interval [0, 1'000'000'000).
if (remainder < 0)
{
--seconds;
remainder += LSS_TIME_UNITS_PER_SECOND;
}
timespec result;
result.tv_sec = static_cast<time_t>(seconds);
result.tv_nsec = static_cast<long>(remainder * 100);
return result;
}
LssTime GetCurrentLssTime()
{
timespec time;
int result = clock_gettime(CLOCK_REALTIME, &time);
cxAssert(result == 0);
return FromTimespec(time);
}
Production code must propagate a failure from
clock_gettime() and verify that conversion to the platform's
time_t is in range.
Ordering semantics
LssTime is wall-clock metadata. The system clock can repeat or
move backwards when corrected, so timestamps do not define transaction order, uniqueness or elapsed
duration. Snapshot records use their partition-local transaction sequence numbers for commit order;
their LssTime values record the associated civil time.