Util.h#

Fully qualified name: carb/time/Util.h

File members: carb/time/Util.h

// SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: LicenseRef-NvidiaProprietary
//
// NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
// property and proprietary rights in and to this material, related
// documentation and any modifications thereto. Any use, reproduction,
// disclosure or distribution of this material and related documentation
// without an express license agreement from NVIDIA CORPORATION or
// its affiliates is strictly prohibited.
#pragma once

#include "../Defines.h"

#include <carb/CarbWindows.h>

#include <time.h>
#include <chrono>

namespace carb
{
namespace time
{

template <size_t N>
inline char* asctime_r(const struct tm* tm, char (&buf)[N]) noexcept
{
    // Buffer requirements as specified:
    // https://linux.die.net/man/3/gmtime_r
    // https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/asctime-s-wasctime-s?view=msvc-170
    static_assert(N >= 26, "Insufficient buffer size");
#if CARB_PLATFORM_WINDOWS
    return asctime_s(buf, N, tm) == 0 ? buf : nullptr;
#elif CARB_POSIX
    return ::asctime_r(tm, buf);
#else
    CARB_UNSUPPORTED_PLATFORM();
#endif
}

template <size_t N>
inline char* ctime_r(const time_t* timep, char (&buf)[N]) noexcept
{
    // Buffer requirements as specified:
    // https://linux.die.net/man/3/gmtime_r
    // https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/ctime-s-ctime32-s-ctime64-s-wctime-s-wctime32-s-wctime64-s?view=msvc-170
    static_assert(N >= 26, "Insufficient buffer size");
#if CARB_PLATFORM_WINDOWS
    auto err = ctime_s(buf, N, timep);
    return err == 0 ? buf : nullptr;
#elif CARB_POSIX
    return ::ctime_r(timep, buf);
#else
    CARB_UNSUPPORTED_PLATFORM();
#endif
}

inline struct tm* gmtime_r(const time_t* timep, struct tm* result) noexcept
{
#if CARB_PLATFORM_WINDOWS
    auto err = ::gmtime_s(result, timep);
    return err == 0 ? result : nullptr;
#elif CARB_POSIX
    return ::gmtime_r(timep, result);
#else
    CARB_UNSUPPORTED_PLATFORM();
#endif
}

inline struct tm* localtime_r(const time_t* timep, struct tm* result) noexcept
{
#if CARB_PLATFORM_WINDOWS
    auto err = ::localtime_s(result, timep);
    return err == 0 ? result : nullptr;
#elif CARB_POSIX
    return ::localtime_r(timep, result);
#else
    CARB_UNSUPPORTED_PLATFORM();
#endif
}

inline std::chrono::nanoseconds getSteadyTimestampNs()
{
    return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch());
}

inline std::chrono::nanoseconds getNanosecondsSinceUnixEpoch()
{
#if CARB_PLATFORM_WINDOWS
    CARBWIN_FILETIME ft;

    GetSystemTimeAsFileTime(reinterpret_cast<FILETIME*>(&ft));

    // Convert to 100ns units (ie: 'NT ticks').
    uint64_t windowsTime = (((uint64_t)ft.dwHighDateTime << 32) | ft.dwLowDateTime);

    // Convert Windows epoch (1601-01-01) to Unix epoch (1970-01-01).
    // Windows epoch is 116444736000000000 100ns units before Unix epoch.
    constexpr uint64_t kWindowsToUnixEpoch = 116444736000000000ULL;

    // Convert to Unix epoch and then to nanoseconds.
    return std::chrono::nanoseconds((windowsTime - kWindowsToUnixEpoch) * 100);
#elif CARB_POSIX
    struct timespec ts;

    clock_gettime(CLOCK_REALTIME, &ts);
    return std::chrono::nanoseconds(((int64_t)ts.tv_sec * 1'000'000'000ll) + ts.tv_nsec);
#else
    CARB_UNSUPPORTED_PLATFORM();
#endif
}

inline std::chrono::nanoseconds getMonotonicNanosecondsSinceUnixEpoch(
    std::chrono::nanoseconds absTimestampNs, std::chrono::nanoseconds steadyTimestampNs = std::chrono::nanoseconds(0))
{
    std::chrono::nanoseconds now = steadyTimestampNs.count() == 0 ? getSteadyTimestampNs() : steadyTimestampNs;
    static std::chrono::nanoseconds nsFromUnixEpochToBoot = absTimestampNs - now;

    return now + nsFromUnixEpochToBoot;
}

inline std::chrono::nanoseconds getMonotonicNanosecondsSinceUnixEpoch()
{
    static std::chrono::nanoseconds nsSinceEpoch = getNanosecondsSinceUnixEpoch();

    return getMonotonicNanosecondsSinceUnixEpoch(nsSinceEpoch);
}

} // namespace time
} // namespace carb