diff options
-rw-r--r-- | include/jau/fraction_type.hpp | 50 | ||||
-rw-r--r-- | src/basic_types.cpp | 21 | ||||
-rw-r--r-- | test/test_fractions_01.cpp | 50 |
3 files changed, 112 insertions, 9 deletions
diff --git a/include/jau/fraction_type.hpp b/include/jau/fraction_type.hpp index f261edd..c479efc 100644 --- a/include/jau/fraction_type.hpp +++ b/include/jau/fraction_type.hpp @@ -96,7 +96,7 @@ namespace jau { * and fraction_timespec for almost infinite range of time-points or durations beyond 292 years. * * Constants are provided in namespace jau::fractions_i64, - * from fractions_i64::pico to fractions_i64::tera, including fractions_i64::seconds to fractions_i64::days, etc. + * from fractions_i64::pico to fractions_i64::tera, including fractions_i64::seconds to fractions_i64::years, etc. * * Literal operators are provided in namespace jau::fractions_i64_literals, * e.g. for `3_s`, `100_ns` ... literals. @@ -741,6 +741,8 @@ namespace jau { inline constexpr const jau::fraction_i64 giga ( 1'000'000'000l, 1lu ); /** mega is 10^6 */ inline constexpr const jau::fraction_i64 mega ( 1'000'000l, 1lu ); + /** years is 31'557'600/1 using 365.2425 days per year */ + inline constexpr const jau::fraction_i64 years ( 31'557'600l, 1lu ); /** days is 86400/1 */ inline constexpr const jau::fraction_i64 days ( 86'400l, 1lu ); /** hours is 3660/1 */ @@ -785,6 +787,8 @@ namespace jau { /** Literal for fractions_i64::pico */ constexpr fraction_i64 operator ""_p(unsigned long long int __p) { return (int64_t)__p * fractions_i64::pico; } + /** Literal for fractions_i64::years */ + constexpr fraction_i64 operator ""_y(unsigned long long int __y) { return (int64_t)__y * fractions_i64::years; } /** Literal for fractions_i64::days */ constexpr fraction_i64 operator ""_d(unsigned long long int __d) { return (int64_t)__d * fractions_i64::days; } /** Literal for fractions_i64::hours */ @@ -803,16 +807,23 @@ namespace jau { /** * Timespec structure using int64_t for its components - * in analogy to `struct timespec_t` on 64bit platforms. + * in analogy to `struct timespec_t` on 64-bit platforms. * * fraction_timespec covers an almost infinite range of time - * while maintaining high precision like `struct timespec_t`. + * while maintaining high precision like `struct timespec_t` on 64-bit platforms. * - * Note: Counting nanoseconds in int64_t only lasts until `2262-04-12`, + * If used as an absolute time-point, zero is time since Unix Epoch `00:00:00 UTC on 1970-01-01`. + * + * Note-1: Counting nanoseconds in int64_t only lasts until `2262-04-12`, * since INT64_MAX is 9'223'372'036'854'775'807 for 9'223'372'036 seconds or 292 years. * - * If used as time-point, zero is time since Unix Epoch `00:00:00 UTC on 1970-01-01`. + * Note-2: Limitations of `struct timespec` on 32-bit platforms + * - to_timespec() conversion to `struct timespec` + * - 32-bit signed integer only last for 68 years or until year 2038, starting from 1970 Unix Epoch + * - [System call conversion for year 2038](https://lwn.net/Articles/643234/) + * - test/test_fractions_01.cpp `struct timespec type validation Test 04.00` * + * @see to_timespec() * @see to_fraction_i64() * @see sleep_until() * @see sleep_for() @@ -954,9 +965,34 @@ namespace jau { return normalize(); } - std::string to_string() const noexcept { - return std::to_string(tv_sec) + "s + " + std::to_string(tv_nsec) + "ns"; + /** + * Return conversion to POSIX `struct timespec`, + * potentially narrowing the components if underlying system is not using 64-bit signed integer. + * + * Note-2: Limitations of `struct timespec` on 32-bit platforms + * - 32-bit signed integer only last for 68 years or until year 2038, starting from 1970 Unix Epoch + * - [System call conversion for year 2038](https://lwn.net/Articles/643234/) + * - test/test_fractions_01.cpp `struct timespec type validation Test 04.00` + */ + constexpr struct timespec to_timespec() const noexcept { + using ns_type = decltype(timespec::tv_nsec); + return { static_cast<std::time_t>( tv_sec ), static_cast<ns_type>( tv_nsec ) }; } + + /** + * Return simple string representation in seconds and nanoseconds. + */ + std::string to_string() const noexcept; + + /** + * Convenience string conversion of `tv_sec` component interpreted as seconds since Unix Epoch in UTC + * to ISO 8601 `YYYY-mm-ddTHH:MM:SSZ`, e.g. '2020-05-18T02:26:21Z`. + * + * Implementation uses `strftime()` with format `%Y-%m-%dT%H:%M:%SZ`. + * + * @param use_space if true, using space instead for 'T' separator and drop trailing UTC `Z` for readability, otherwise be compliant with ISO 8601 (default) + */ + std::string to_iso8601_string(const bool use_space=false) const noexcept; }; inline std::string to_string(const fraction_timespec& v) noexcept { return v.to_string(); } diff --git a/src/basic_types.cpp b/src/basic_types.cpp index eea78c8..651973f 100644 --- a/src/basic_types.cpp +++ b/src/basic_types.cpp @@ -69,12 +69,28 @@ uint64_t jau::getWallClockSeconds() noexcept { return static_cast<uint64_t>( t.tv_sec ); } +std::string fraction_timespec::to_string() const noexcept { + return std::to_string(tv_sec) + "s + " + std::to_string(tv_nsec) + "ns"; +} + +std::string fraction_timespec::to_iso8601_string(const bool use_space) const noexcept { + std::time_t t0 = static_cast<std::time_t>(tv_sec); + struct std::tm tm_0; + if( nullptr == ::gmtime_r( &t0, &tm_0 ) ) { + return use_space ? "1970-01-01 00:00:00" : "1970-01-01T00:00:00Z"; // 20 + 1 + } else { + char b[20+1]; + strftime(b, sizeof(b), use_space ? "%Y-%m-%d %H:%M:%S" : "%Y-%m-%dT%H:%M:%SZ", &tm_0); + return std::string(b); + } +} + void jau::sleep_until(const fraction_timespec& absolute_time, const bool monotonic) noexcept { if( absolute_time <= fraction_tv::zero ) { return; } // typedef struct timespec __gthread_time_t; - __gthread_time_t ts = { static_cast<std::time_t>( absolute_time.tv_sec ), static_cast<long>( absolute_time.tv_nsec ) }; + __gthread_time_t ts = absolute_time.to_timespec(); while ( -1 == ::clock_nanosleep(monotonic ? CLOCK_MONOTONIC : CLOCK_REALTIME, TIMER_ABSTIME, @@ -129,7 +145,7 @@ std::cv_status jau::wait_until(std::condition_variable& cv, std::unique_lock<std return std::cv_status::no_timeout; } // typedef struct timespec __gthread_time_t; - __gthread_time_t ts = { static_cast<std::time_t>( absolute_time.tv_sec ), static_cast<long>( absolute_time.tv_nsec ) }; + __gthread_time_t ts = absolute_time.to_timespec(); if( jau_has_pthread_cond_clockwait() ) { pthread_cond_clockwait(cv.native_handle(), lock.mutex()->native_handle(), @@ -163,6 +179,7 @@ std::cv_status jau::wait_for(std::condition_variable& cv, std::unique_lock<std:: } } + jau::ExceptionBase::ExceptionBase(std::string const type, std::string const m, const char* file, int line) noexcept : msg_(std::string(type).append(" @ ").append(file).append(":").append(std::to_string(line)).append(": ").append(m)), backtrace_( jau::get_backtrace(true /* skip_anon_frames */) ) diff --git a/test/test_fractions_01.cpp b/test/test_fractions_01.cpp index d8e5014..4f768b8 100644 --- a/test/test_fractions_01.cpp +++ b/test/test_fractions_01.cpp @@ -732,6 +732,56 @@ TEST_CASE( "Fraction Time Arithmetic Sub Test 03.2", "[fraction][fraction_timesp } } +/** + * Resembling the GNU/Linux bits/types.h, + * documenting whether time_t is 32-bit (arm-32) or 64-bit (arm-64, x86_64, ..). + */ +static int sizeof_time_t() { +/* X32 kernel interface is 64-bit. */ +#if defined __x86_64__ && defined __ILP32__ + // 64 bit size + #if __WORDSIZE == 32 + return sizeof( __int64_t ); + #else + return sizeof( long int ); + #endif +#else + // 32 bit or 64 bit + return sizeof( long int ); +#endif +} + +/** + * Resembling the GNU/Linux bits/types.h, + * documenting whether tv_nsec of struct timespec is 32-bit (arm-32) or 64-bit (arm-64, x86_64, ..). + */ +static int sizeof_tv_nsec() { +#if __WORDSIZE == 64 \ + || (defined __SYSCALL_WORDSIZE && __SYSCALL_WORDSIZE == 64) \ + || __TIMESIZE == 32 + // 32 bit or 64 bit: __syscall_slong_t + return sizeof( int64_t ); +#else + // 32 bit or 64 bit + return sizeof( long int ); +#endif +} + +TEST_CASE( "struct timespec type validation Test 04.00", "[fraction][struct_timespec][time]" ) { + // testing fraction_timespec::to_timespec() + { + using time_t_type = decltype(timespec::tv_sec); + INFO_STR(" tv_sec: sizeof=" + std::to_string( sizeof( time_t_type ) ) + ", signed " + std::to_string( std::is_signed_v<time_t_type>) ); + CHECK( sizeof_time_t() == sizeof( time_t_type ) ); + CHECK( true == std::is_signed_v<time_t_type> ); + + using ns_type = decltype(timespec::tv_nsec); + INFO_STR(" tv_nsec: sizeof=" + std::to_string( sizeof( ns_type ) ) + ", signed " + std::to_string( std::is_signed_v<ns_type>) ); + CHECK( sizeof_tv_nsec() == sizeof( ns_type ) ); + CHECK( true == std::is_signed_v<ns_type> ); + } +} + TEST_CASE( "Fraction Time Measurement Test 04.01", "[fraction][fraction_timespec][time]" ) { // We assume accuracy of at least 1/2 millisecond, hence the difference shall not be greater const fraction_i64 accuracy = fractions_i64::milli*2_i64/3_i64; |