Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions cpp_utils/include/cpp_utils/macros/macros.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,5 +87,12 @@ namespace utils {
#define _EPROSIMA_WINDOWS_PLATFORM 1
#endif // if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(_MSC_VER)

#if defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(_WIN64) || defined(__x86_64__) || \
defined(__ppc64__) || defined(__aarch64__)
#define _PLATFORM_64BIT 1
#else
#define _PLATFORM_32BIT 1
#endif // if defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(_WIN64) || defined(__x86_64__) || defined(__ppc64__) || defined(__aarch64__)

} /* namespace utils */
} /* namespace eprosima */
19 changes: 18 additions & 1 deletion cpp_utils/include/cpp_utils/time/time_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,15 @@ namespace utils {
//! Type of Duration in milliseconds
using Duration_ms = uint32_t;

/**
* Type used to fix the clock to the system clock
*/
using Timeclock = std::chrono::system_clock;

/**
* Type used to represent time points
*/
using Timestamp = std::chrono::time_point<std::chrono::system_clock>;
using Timestamp = std::chrono::time_point<Timeclock>;
Comment thread
EugenioCollado marked this conversation as resolved.

/**
* @brief Now time
Expand Down Expand Up @@ -98,5 +103,17 @@ CPP_UTILS_DllAPI std::chrono::milliseconds duration_to_ms(
CPP_UTILS_DllAPI void sleep_for(
const Duration_ms& sleep_time) noexcept;

/**
* @brief Makes sure that the time is clamped into a valid range.
* This is useful for keeping time values within the ranges
* depending on the platform.
*
* @param time time to normalize.
*
* @return normalized time value.
*/
CPP_UTILS_DllAPI time_t normalize(
const time_t& time) noexcept;

} /* namespace utils */
} /* namespace eprosima */
51 changes: 40 additions & 11 deletions cpp_utils/src/cpp/time/time_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <cpp_utils/macros/macros.hpp>
#include <cpp_utils/time/time_utils.hpp>
#include <cpp_utils/exception/PreconditionNotMet.hpp>
#include <cpp_utils/exception/ValueNotAllowedException.hpp>
#include <cpp_utils/Log.hpp>

// These functions has different names in windows
Expand All @@ -36,17 +37,17 @@ namespace utils {

Timestamp now() noexcept
{
return std::chrono::system_clock::now();
return Timeclock::now();
}

Timestamp the_end_of_time() noexcept
{
return std::chrono::time_point<std::chrono::system_clock>::max();
return Timestamp::max();
}

Timestamp the_beginning_of_time() noexcept
{
return std::chrono::time_point<std::chrono::system_clock>::min();
return Timestamp::min();
}

Timestamp date_to_timestamp(
Expand All @@ -66,7 +67,7 @@ Timestamp date_to_timestamp(
tm.tm_mon = static_cast<int>(month) - 1;
tm.tm_year = static_cast<int>(year) - 1900;

return std::chrono::system_clock::from_time_t(timegm(&tm));
return Timeclock::from_time_t(normalize(timegm(&tm)));
}

Timestamp time_to_timestamp(
Expand All @@ -77,16 +78,14 @@ Timestamp time_to_timestamp(
std::tm tm;

// Initialise with current timestamp to set date
auto current_ts = now();
std::chrono::high_resolution_clock::time_point::duration duration = current_ts.time_since_epoch();
time_t duration_seconds = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
time_t duration_seconds = normalize(Timeclock::to_time_t(now()));
tm = *std::gmtime(&duration_seconds);

tm.tm_sec = static_cast<int>(second);
tm.tm_min = static_cast<int>(minute);
tm.tm_hour = static_cast<int>(hour);

return std::chrono::system_clock::from_time_t(timegm(&tm));
return Timeclock::from_time_t(timegm(&tm));
}

std::string timestamp_to_string(
Expand All @@ -95,8 +94,7 @@ std::string timestamp_to_string(
bool local_time /* = false */)
{
std::ostringstream ss;
const std::chrono::high_resolution_clock::time_point::duration duration = timestamp.time_since_epoch();
const time_t duration_seconds = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
time_t duration_seconds = normalize(Timeclock::to_time_t(timestamp));

std::tm* tm = nullptr;
if (local_time)
Expand Down Expand Up @@ -153,7 +151,14 @@ Timestamp string_to_timestamp(
{
utc_time = timegm(&tm);
}
return std::chrono::system_clock::from_time_t(utc_time);

if ((time_t)-1 == utc_time)
{
throw ValueNotAllowedException(
STR_ENTRY << "Failed to convert string to timestamp");
}

return Timeclock::from_time_t(utc_time);
}

std::chrono::milliseconds duration_to_ms(
Expand All @@ -168,5 +173,29 @@ void sleep_for(
std::this_thread::sleep_for(duration_to_ms(sleep_time));
}

time_t normalize(
const time_t& time) noexcept
{
time_t normalized_time = time;
#if _EPROSIMA_WINDOWS_PLATFORM // In Windows std::gmtime does not support negative values
time_t max_value;

#if _PLATFORM_64BIT
max_value = 32535215999; // In WIN64, max value is 3000-12-31_23-59-59
#else
max_value = std::numeric_limits<int32_t>::max(); // In WIN32, values greater than 2^32 are not supported
#endif // if PLATFORM_64BIT

if (0 > time || time > max_value)
{
EPROSIMA_LOG_WARNING(TIME_UTILS,
"Timestamp value: " << time << " is out of range for Windows, clamping to 0 and " <<
max_value);
normalized_time = std::max((time_t) 0, std::min(max_value, time));
}
#endif // if _EPROSIMA_WINDOWS_PLATFORM
return normalized_time;
}

} /* namespace utils */
} /* namespace eprosima */
150 changes: 148 additions & 2 deletions cpp_utils/test/unittest/time/time_utils_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ using namespace eprosima::utils;
* - now
* - now with alternative format
* - old time
* - date before 1970
* - the beginning of time
* - future time
* - date after 2038
* - the end of time
* - some time today
*/
TEST(time_utils_test, timestamp_to_string_to_timestamp)
Expand Down Expand Up @@ -112,14 +116,81 @@ TEST(time_utils_test, timestamp_to_string_to_timestamp)
ASSERT_EQ(timestamp_to_string(old_time_from_str), expected_string_os.str());
}

// date before 1970
{
Timestamp old_time = date_to_timestamp(1959u, 7u, 20u, 6u, 39u, 42u);
std::string old_time_str = timestamp_to_string(old_time);

std::ostringstream expected_string_os;
#if _EPROSIMA_WINDOWS_PLATFORM
expected_string_os
<< 1970
<< "-" << "01"
<< "-" << "01"
<< "_" << "00"
<< "-" << "00"
<< "-" << "00";
#else
expected_string_os
<< 1959
<< "-" << "07"
<< "-" << 20
<< "_" << "06"
<< "-" << 39
<< "-" << 42;
#endif // _EPROSIMA_WINDOWS_PLATFORM

// Test timestamp_to_string
ASSERT_EQ(old_time_str, expected_string_os.str());

// Test string_to_timestamp
Timestamp old_time_from_str = string_to_timestamp(old_time_str);
// NOTE: cannot directly compare timestamps because some precision is lost during ts->str conversion
ASSERT_EQ(timestamp_to_string(old_time_from_str), expected_string_os.str());
}

// the begininng of time
{
Timestamp beginning_time = the_beginning_of_time();
std::string beginning_time_str = timestamp_to_string(beginning_time);

std::ostringstream expected_string_os;
#if _EPROSIMA_WINDOWS_PLATFORM
expected_string_os
<< 1970
<< "-" << "01"
<< "-" << "01"
<< "_" << "00"
<< "-" << "00"
<< "-" << "00";
#else //1677-09-21_00-12-44
expected_string_os
<< 1677
<< "-" << "09"
<< "-" << "21"
<< "_" << "00"
<< "-" << "12"
<< "-" << "44";
#endif // _EPROSIMA_WINDOWS_PLATFORM


// Test timestamp_to_string
ASSERT_EQ(beginning_time_str, expected_string_os.str());

// Test string_to_timestamp
Timestamp beginning_time_from_str = string_to_timestamp(beginning_time_str);
// NOTE: cannot directly compare timestamps because some precision is lost during ts->str conversion
ASSERT_EQ(timestamp_to_string(beginning_time_from_str), expected_string_os.str());
}

// future time
{
Timestamp future_time = date_to_timestamp(2233u, 5u, 22u);
Timestamp future_time = date_to_timestamp(2033u, 5u, 22u);
Comment thread
EugenioCollado marked this conversation as resolved.
std::string future_time_str = timestamp_to_string(future_time);

std::ostringstream expected_string_os;
expected_string_os
<< 2233
<< 2033
<< "-" << "05"
<< "-" << 22
<< "_" << "00"
Expand All @@ -135,6 +206,81 @@ TEST(time_utils_test, timestamp_to_string_to_timestamp)
ASSERT_EQ(timestamp_to_string(future_time_from_str), expected_string_os.str());
}

// date after 2038
{
Timestamp future_time = date_to_timestamp(2049u, 5u, 22u);
std::string future_time_str = timestamp_to_string(future_time);

std::ostringstream expected_string_os;
#if _EPROSIMA_WINDOWS_PLATFORM && PLATFORM_32BIT
expected_string_os
<< 2038
<< "-" << "01"
<< "-" << 19
<< "_" << "03"
<< "-" << "14"
<< "-" << "07";
#else
expected_string_os
<< 2049
<< "-" << "05"
<< "-" << 22
<< "_" << "00"
<< "-" << "00"
<< "-" << "00";
#endif // _EPROSIMA_WINDOWS_PLATFORM

// Test timestamp_to_string
ASSERT_EQ(future_time_str, expected_string_os.str());

// Test string_to_timestamp
Timestamp future_time_from_str = string_to_timestamp(future_time_str);
// NOTE: cannot directly compare timestamps because some precision is lost during ts->str conversion
ASSERT_EQ(timestamp_to_string(future_time_from_str), expected_string_os.str());
}

// the end of time
{
Timestamp end_time = the_end_of_time();
std::string end_time_str = timestamp_to_string(end_time);

std::ostringstream expected_string_os;
#if _EPROSIMA_WINDOWS_PLATFORM && _PLATFORM_32BIT
expected_string_os
<< 2038
<< "-" << "01"
<< "-" << 19
<< "_" << "03"
<< "-" << "14"
<< "-" << "07";
#elif _EPROSIMA_WINDOWS_PLATFORM && _PLATFORM_64BIT
expected_string_os
<< 3000
<< "-" << "12"
<< "-" << 31
<< "_" << "23"
<< "-" << "59"
<< "-" << "59";
#else // 2262-04-11 23:47:16
expected_string_os
<< 2262
<< "-" << "04"
<< "-" << 11
<< "_" << "23"
<< "-" << "47"
<< "-" << "16";
#endif // _EPROSIMA_WINDOWS_PLATFORM


// Test timestamp_to_string
ASSERT_EQ(end_time_str, expected_string_os.str());

// Test string_to_timestamp
Timestamp end_time_from_str = string_to_timestamp(end_time_str);
// NOTE: cannot directly compare timestamps because some precision is lost during ts->str conversion
ASSERT_EQ(timestamp_to_string(end_time_from_str), expected_string_os.str());
}

// some time today
{
Timestamp some_time_today = time_to_timestamp(13u, 13u, 13u);
Expand Down