Skip to content

Commit 757a4cd

Browse files
Fix time utils time_t 64 bits in windows (#134)
* fix time utils overflow in windows Signed-off-by: Eugenio Collado <eugeniocollado@eprosima.com> * Fix tests for ubuntu and changed to _EPROSIMA_WINDOWS_PLATFORM macro Signed-off-by: Eugenio Collado <eugeniocollado@eprosima.com> * Uncrustify Signed-off-by: Eugenio Collado <eugeniocollado@eprosima.com> * Applied suggestions Signed-off-by: Eugenio Collado <eugeniocollado@eprosima.com> * Refs #23440: Add new _PLAFORM_32/64 BITS Signed-off-by: Mario Dominguez <mariodominguez@eprosima.com> * Refs #23440: normalize(time_t) method Signed-off-by: Mario Dominguez <mariodominguez@eprosima.com> * Refs #23440: update tests Signed-off-by: Mario Dominguez <mariodominguez@eprosima.com> * Refs #23440: Linter Signed-off-by: Mario Dominguez <mariodominguez@eprosima.com> --------- Signed-off-by: Eugenio Collado <eugeniocollado@eprosima.com> Signed-off-by: Mario Dominguez <mariodominguez@eprosima.com> Co-authored-by: Mario Dominguez <mariodominguez@eprosima.com>
1 parent 3bfcc19 commit 757a4cd

4 files changed

Lines changed: 213 additions & 14 deletions

File tree

cpp_utils/include/cpp_utils/macros/macros.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,5 +87,12 @@ namespace utils {
8787
#define _EPROSIMA_WINDOWS_PLATFORM 1
8888
#endif // if defined(WIN32) || defined(_WIN32) || defined(__WIN32) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(_MSC_VER)
8989

90+
#if defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(_WIN64) || defined(__x86_64__) || \
91+
defined(__ppc64__) || defined(__aarch64__)
92+
#define _PLATFORM_64BIT 1
93+
#else
94+
#define _PLATFORM_32BIT 1
95+
#endif // if defined(WIN64) || defined(_WIN64) || defined(__WIN64) || defined(_WIN64) || defined(__x86_64__) || defined(__ppc64__) || defined(__aarch64__)
96+
9097
} /* namespace utils */
9198
} /* namespace eprosima */

cpp_utils/include/cpp_utils/time/time_utils.hpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,15 @@ namespace utils {
2929
//! Type of Duration in milliseconds
3030
using Duration_ms = uint32_t;
3131

32+
/**
33+
* Type used to fix the clock to the system clock
34+
*/
35+
using Timeclock = std::chrono::system_clock;
36+
3237
/**
3338
* Type used to represent time points
3439
*/
35-
using Timestamp = std::chrono::time_point<std::chrono::system_clock>;
40+
using Timestamp = std::chrono::time_point<Timeclock>;
3641

3742
/**
3843
* @brief Now time
@@ -98,5 +103,17 @@ CPP_UTILS_DllAPI std::chrono::milliseconds duration_to_ms(
98103
CPP_UTILS_DllAPI void sleep_for(
99104
const Duration_ms& sleep_time) noexcept;
100105

106+
/**
107+
* @brief Makes sure that the time is clamped into a valid range.
108+
* This is useful for keeping time values within the ranges
109+
* depending on the platform.
110+
*
111+
* @param time time to normalize.
112+
*
113+
* @return normalized time value.
114+
*/
115+
CPP_UTILS_DllAPI time_t normalize(
116+
const time_t& time) noexcept;
117+
101118
} /* namespace utils */
102119
} /* namespace eprosima */

cpp_utils/src/cpp/time/time_utils.cpp

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <cpp_utils/macros/macros.hpp>
2525
#include <cpp_utils/time/time_utils.hpp>
2626
#include <cpp_utils/exception/PreconditionNotMet.hpp>
27+
#include <cpp_utils/exception/ValueNotAllowedException.hpp>
2728
#include <cpp_utils/Log.hpp>
2829

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

3738
Timestamp now() noexcept
3839
{
39-
return std::chrono::system_clock::now();
40+
return Timeclock::now();
4041
}
4142

4243
Timestamp the_end_of_time() noexcept
4344
{
44-
return std::chrono::time_point<std::chrono::system_clock>::max();
45+
return Timestamp::max();
4546
}
4647

4748
Timestamp the_beginning_of_time() noexcept
4849
{
49-
return std::chrono::time_point<std::chrono::system_clock>::min();
50+
return Timestamp::min();
5051
}
5152

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

69-
return std::chrono::system_clock::from_time_t(timegm(&tm));
70+
return Timeclock::from_time_t(normalize(timegm(&tm)));
7071
}
7172

7273
Timestamp time_to_timestamp(
@@ -77,16 +78,14 @@ Timestamp time_to_timestamp(
7778
std::tm tm;
7879

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

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

89-
return std::chrono::system_clock::from_time_t(timegm(&tm));
88+
return Timeclock::from_time_t(timegm(&tm));
9089
}
9190

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

10199
std::tm* tm = nullptr;
102100
if (local_time)
@@ -153,7 +151,14 @@ Timestamp string_to_timestamp(
153151
{
154152
utc_time = timegm(&tm);
155153
}
156-
return std::chrono::system_clock::from_time_t(utc_time);
154+
155+
if ((time_t)-1 == utc_time)
156+
{
157+
throw ValueNotAllowedException(
158+
STR_ENTRY << "Failed to convert string to timestamp");
159+
}
160+
161+
return Timeclock::from_time_t(utc_time);
157162
}
158163

159164
std::chrono::milliseconds duration_to_ms(
@@ -168,5 +173,29 @@ void sleep_for(
168173
std::this_thread::sleep_for(duration_to_ms(sleep_time));
169174
}
170175

176+
time_t normalize(
177+
const time_t& time) noexcept
178+
{
179+
time_t normalized_time = time;
180+
#if _EPROSIMA_WINDOWS_PLATFORM // In Windows std::gmtime does not support negative values
181+
time_t max_value;
182+
183+
#if _PLATFORM_64BIT
184+
max_value = 32535215999; // In WIN64, max value is 3000-12-31_23-59-59
185+
#else
186+
max_value = std::numeric_limits<int32_t>::max(); // In WIN32, values greater than 2^32 are not supported
187+
#endif // if PLATFORM_64BIT
188+
189+
if (0 > time || time > max_value)
190+
{
191+
EPROSIMA_LOG_WARNING(TIME_UTILS,
192+
"Timestamp value: " << time << " is out of range for Windows, clamping to 0 and " <<
193+
max_value);
194+
normalized_time = std::max((time_t) 0, std::min(max_value, time));
195+
}
196+
#endif // if _EPROSIMA_WINDOWS_PLATFORM
197+
return normalized_time;
198+
}
199+
171200
} /* namespace utils */
172201
} /* namespace eprosima */

cpp_utils/test/unittest/time/time_utils_test.cpp

Lines changed: 148 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@ using namespace eprosima::utils;
3131
* - now
3232
* - now with alternative format
3333
* - old time
34+
* - date before 1970
35+
* - the beginning of time
3436
* - future time
37+
* - date after 2038
38+
* - the end of time
3539
* - some time today
3640
*/
3741
TEST(time_utils_test, timestamp_to_string_to_timestamp)
@@ -112,14 +116,81 @@ TEST(time_utils_test, timestamp_to_string_to_timestamp)
112116
ASSERT_EQ(timestamp_to_string(old_time_from_str), expected_string_os.str());
113117
}
114118

119+
// date before 1970
120+
{
121+
Timestamp old_time = date_to_timestamp(1959u, 7u, 20u, 6u, 39u, 42u);
122+
std::string old_time_str = timestamp_to_string(old_time);
123+
124+
std::ostringstream expected_string_os;
125+
#if _EPROSIMA_WINDOWS_PLATFORM
126+
expected_string_os
127+
<< 1970
128+
<< "-" << "01"
129+
<< "-" << "01"
130+
<< "_" << "00"
131+
<< "-" << "00"
132+
<< "-" << "00";
133+
#else
134+
expected_string_os
135+
<< 1959
136+
<< "-" << "07"
137+
<< "-" << 20
138+
<< "_" << "06"
139+
<< "-" << 39
140+
<< "-" << 42;
141+
#endif // _EPROSIMA_WINDOWS_PLATFORM
142+
143+
// Test timestamp_to_string
144+
ASSERT_EQ(old_time_str, expected_string_os.str());
145+
146+
// Test string_to_timestamp
147+
Timestamp old_time_from_str = string_to_timestamp(old_time_str);
148+
// NOTE: cannot directly compare timestamps because some precision is lost during ts->str conversion
149+
ASSERT_EQ(timestamp_to_string(old_time_from_str), expected_string_os.str());
150+
}
151+
152+
// the begininng of time
153+
{
154+
Timestamp beginning_time = the_beginning_of_time();
155+
std::string beginning_time_str = timestamp_to_string(beginning_time);
156+
157+
std::ostringstream expected_string_os;
158+
#if _EPROSIMA_WINDOWS_PLATFORM
159+
expected_string_os
160+
<< 1970
161+
<< "-" << "01"
162+
<< "-" << "01"
163+
<< "_" << "00"
164+
<< "-" << "00"
165+
<< "-" << "00";
166+
#else //1677-09-21_00-12-44
167+
expected_string_os
168+
<< 1677
169+
<< "-" << "09"
170+
<< "-" << "21"
171+
<< "_" << "00"
172+
<< "-" << "12"
173+
<< "-" << "44";
174+
#endif // _EPROSIMA_WINDOWS_PLATFORM
175+
176+
177+
// Test timestamp_to_string
178+
ASSERT_EQ(beginning_time_str, expected_string_os.str());
179+
180+
// Test string_to_timestamp
181+
Timestamp beginning_time_from_str = string_to_timestamp(beginning_time_str);
182+
// NOTE: cannot directly compare timestamps because some precision is lost during ts->str conversion
183+
ASSERT_EQ(timestamp_to_string(beginning_time_from_str), expected_string_os.str());
184+
}
185+
115186
// future time
116187
{
117-
Timestamp future_time = date_to_timestamp(2233u, 5u, 22u);
188+
Timestamp future_time = date_to_timestamp(2033u, 5u, 22u);
118189
std::string future_time_str = timestamp_to_string(future_time);
119190

120191
std::ostringstream expected_string_os;
121192
expected_string_os
122-
<< 2233
193+
<< 2033
123194
<< "-" << "05"
124195
<< "-" << 22
125196
<< "_" << "00"
@@ -135,6 +206,81 @@ TEST(time_utils_test, timestamp_to_string_to_timestamp)
135206
ASSERT_EQ(timestamp_to_string(future_time_from_str), expected_string_os.str());
136207
}
137208

209+
// date after 2038
210+
{
211+
Timestamp future_time = date_to_timestamp(2049u, 5u, 22u);
212+
std::string future_time_str = timestamp_to_string(future_time);
213+
214+
std::ostringstream expected_string_os;
215+
#if _EPROSIMA_WINDOWS_PLATFORM && PLATFORM_32BIT
216+
expected_string_os
217+
<< 2038
218+
<< "-" << "01"
219+
<< "-" << 19
220+
<< "_" << "03"
221+
<< "-" << "14"
222+
<< "-" << "07";
223+
#else
224+
expected_string_os
225+
<< 2049
226+
<< "-" << "05"
227+
<< "-" << 22
228+
<< "_" << "00"
229+
<< "-" << "00"
230+
<< "-" << "00";
231+
#endif // _EPROSIMA_WINDOWS_PLATFORM
232+
233+
// Test timestamp_to_string
234+
ASSERT_EQ(future_time_str, expected_string_os.str());
235+
236+
// Test string_to_timestamp
237+
Timestamp future_time_from_str = string_to_timestamp(future_time_str);
238+
// NOTE: cannot directly compare timestamps because some precision is lost during ts->str conversion
239+
ASSERT_EQ(timestamp_to_string(future_time_from_str), expected_string_os.str());
240+
}
241+
242+
// the end of time
243+
{
244+
Timestamp end_time = the_end_of_time();
245+
std::string end_time_str = timestamp_to_string(end_time);
246+
247+
std::ostringstream expected_string_os;
248+
#if _EPROSIMA_WINDOWS_PLATFORM && _PLATFORM_32BIT
249+
expected_string_os
250+
<< 2038
251+
<< "-" << "01"
252+
<< "-" << 19
253+
<< "_" << "03"
254+
<< "-" << "14"
255+
<< "-" << "07";
256+
#elif _EPROSIMA_WINDOWS_PLATFORM && _PLATFORM_64BIT
257+
expected_string_os
258+
<< 3000
259+
<< "-" << "12"
260+
<< "-" << 31
261+
<< "_" << "23"
262+
<< "-" << "59"
263+
<< "-" << "59";
264+
#else // 2262-04-11 23:47:16
265+
expected_string_os
266+
<< 2262
267+
<< "-" << "04"
268+
<< "-" << 11
269+
<< "_" << "23"
270+
<< "-" << "47"
271+
<< "-" << "16";
272+
#endif // _EPROSIMA_WINDOWS_PLATFORM
273+
274+
275+
// Test timestamp_to_string
276+
ASSERT_EQ(end_time_str, expected_string_os.str());
277+
278+
// Test string_to_timestamp
279+
Timestamp end_time_from_str = string_to_timestamp(end_time_str);
280+
// NOTE: cannot directly compare timestamps because some precision is lost during ts->str conversion
281+
ASSERT_EQ(timestamp_to_string(end_time_from_str), expected_string_os.str());
282+
}
283+
138284
// some time today
139285
{
140286
Timestamp some_time_today = time_to_timestamp(13u, 13u, 13u);

0 commit comments

Comments
 (0)