summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/jau/fraction_type.hpp50
-rw-r--r--src/basic_types.cpp21
-rw-r--r--test/test_fractions_01.cpp50
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;