Skip to content

Commit bfb55e7

Browse files
Add string to Timestamp converter (#61)
Signed-off-by: Juan López Fernández <juanlopez@eprosima.com>
1 parent b45d396 commit bfb55e7

4 files changed

Lines changed: 168 additions & 7 deletions

File tree

cpp_utils/include/cpp_utils/time/time_utils.hpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ CPP_UTILS_DllAPI Timestamp the_end_of_time() noexcept;
4747
//! Returns the minimum time available for \c Timestamp
4848
CPP_UTILS_DllAPI Timestamp the_beginning_of_time() noexcept;
4949

50+
//! Construct a \c Timestamp given a date and time.
5051
CPP_UTILS_DllAPI Timestamp date_to_timestamp(
5152
unsigned int year,
5253
unsigned int month,
@@ -55,6 +56,12 @@ CPP_UTILS_DllAPI Timestamp date_to_timestamp(
5556
unsigned int minute = 0,
5657
unsigned int second = 0);
5758

59+
//! Construct a \c Timestamp given a time (uses current date).
60+
CPP_UTILS_DllAPI Timestamp time_to_timestamp(
61+
unsigned int hour = 0,
62+
unsigned int minute = 0,
63+
unsigned int second = 0);
64+
5865
/**
5966
* @brief Convert a \c Timestamp to a string following a specific format.
6067
*
@@ -71,6 +78,20 @@ CPP_UTILS_DllAPI std::string timestamp_to_string(
7178
const std::string& format = "%Y-%m-%d_%H-%M-%S",
7279
bool local_time = false);
7380

81+
/**
82+
* @brief Convert to \c Timestamp a string following a specific format.
83+
*
84+
* @param timestamp string to parse.
85+
* @param format string formatting the date.
86+
* @param local_time whether to use the local time zone or UTC.
87+
*
88+
* @return timestamp from the string in the format given
89+
*/
90+
CPP_UTILS_DllAPI Timestamp string_to_timestamp(
91+
const std::string& timestamp,
92+
const std::string& format = "%Y-%m-%d_%H-%M-%S",
93+
bool local_time = false);
94+
7495
CPP_UTILS_DllAPI std::chrono::milliseconds duration_to_ms(
7596
const Duration_ms& duration) noexcept;
7697

cpp_utils/src/cpp/time/time_utils.cpp

Lines changed: 57 additions & 0 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/Log.hpp>
2728

2829
// These functions has different names in windows
2930
#if _EPROSIMA_WINDOWS_PLATFORM
@@ -68,6 +69,26 @@ Timestamp date_to_timestamp(
6869
return std::chrono::system_clock::from_time_t(timegm(&tm));
6970
}
7071

72+
Timestamp time_to_timestamp(
73+
unsigned int hour /* = 0 */,
74+
unsigned int minute /* = 0 */,
75+
unsigned int second /* = 0 */)
76+
{
77+
std::tm tm;
78+
79+
// 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();
83+
tm = *std::gmtime(&duration_seconds);
84+
85+
tm.tm_sec = static_cast<int>(second);
86+
tm.tm_min = static_cast<int>(minute);
87+
tm.tm_hour = static_cast<int>(hour);
88+
89+
return std::chrono::system_clock::from_time_t(timegm(&tm));
90+
}
91+
7192
std::string timestamp_to_string(
7293
const Timestamp& timestamp,
7394
const std::string& format /* = "%Z_%Y-%m-%d_%H-%M-%S" */,
@@ -99,6 +120,42 @@ std::string timestamp_to_string(
99120
return ss.str();
100121
}
101122

123+
Timestamp string_to_timestamp(
124+
const std::string& timestamp,
125+
const std::string& format /* = "%Z_%Y-%m-%d_%H-%M-%S" */,
126+
bool local_time /* = false */)
127+
{
128+
std::istringstream ss(timestamp);
129+
std::tm tm{};
130+
ss >> std::get_time(&tm, format.c_str());
131+
if (ss.fail())
132+
{
133+
throw PreconditionNotMet(
134+
STR_ENTRY << "Format <" << format << "> to convert string to Timestamp is not valid for timestamp " << timestamp <<
135+
".");
136+
}
137+
138+
std::time_t utc_time;
139+
if (local_time)
140+
{
141+
// Attempt to automatically determine if DST in effect
142+
tm.tm_isdst = -1;
143+
// WARNING: might not be available in all systems, mktime sets this value to 0/1 if succesfully found this info
144+
utc_time = std::mktime(&tm);
145+
if (tm.tm_isdst < 0)
146+
{
147+
logWarning(
148+
UTILS_TIME,
149+
"DST information could not be found in the system, converted timestamp might be an hour off.");
150+
}
151+
}
152+
else
153+
{
154+
utc_time = timegm(&tm);
155+
}
156+
return std::chrono::system_clock::from_time_t(utc_time);
157+
}
158+
102159
std::chrono::milliseconds duration_to_ms(
103160
const Duration_ms& duration) noexcept
104161
{

cpp_utils/test/unittest/time/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ set(TEST_SOURCES
2929
)
3030

3131
set(TEST_LIST
32-
timestamp_to_string
33-
timestamp_to_string_local
32+
timestamp_to_string_to_timestamp
33+
timestamp_to_string_to_timestamp_local
3434
timestamp_to_string_format
3535
)
3636

cpp_utils/test/unittest/time/time_utils_test.cpp

Lines changed: 88 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,16 @@
2525
using namespace eprosima::utils;
2626

2727
/**
28-
* Test function timestamp_to_string with a time stamp.
28+
* Test function timestamp_to_string with a time stamp, as well the inverse conversion string_to_timestamp.
2929
*
3030
* CASES:
3131
* - now
32+
* - now with alternative format
3233
* - old time
3334
* - future time
35+
* - some time today
3436
*/
35-
TEST(time_utils_test, timestamp_to_string)
37+
TEST(time_utils_test, timestamp_to_string_to_timestamp)
3638
{
3739
// now
3840
{
@@ -51,7 +53,40 @@ TEST(time_utils_test, timestamp_to_string)
5153
<< "-" << number_trailing_zeros_format(local_tm.tm_min, 2)
5254
<< "-" << number_trailing_zeros_format(local_tm.tm_sec, 2);
5355

56+
// Test timestamp_to_string
5457
ASSERT_EQ(now_time_str, expected_string_os.str());
58+
59+
// Test string_to_timestamp
60+
Timestamp now_time_from_str = string_to_timestamp(now_time_str);
61+
// NOTE: cannot directly compare timestamps because some precision is lost during ts->str conversion
62+
ASSERT_EQ(timestamp_to_string(now_time_from_str), expected_string_os.str());
63+
}
64+
65+
// now with alternative format
66+
{
67+
Timestamp now_time = now();
68+
std::string format = "%S-%M-%H___%d-%m-%Y";
69+
std::string now_time_str = timestamp_to_string(now_time, format);
70+
71+
time_t time = std::chrono::system_clock::to_time_t(now_time);
72+
std::tm local_tm = *gmtime(&time);
73+
74+
std::ostringstream expected_string_os;
75+
expected_string_os
76+
<< number_trailing_zeros_format(local_tm.tm_sec, 2)
77+
<< "-" << number_trailing_zeros_format(local_tm.tm_min, 2)
78+
<< "-" << number_trailing_zeros_format(local_tm.tm_hour, 2)
79+
<< "___" << number_trailing_zeros_format(local_tm.tm_mday, 2)
80+
<< "-" << number_trailing_zeros_format(local_tm.tm_mon + 1, 2)
81+
<< "-" << number_trailing_zeros_format(local_tm.tm_year + 1900, 4);
82+
83+
// Test timestamp_to_string
84+
ASSERT_EQ(now_time_str, expected_string_os.str());
85+
86+
// Test string_to_timestamp
87+
Timestamp now_time_from_str = string_to_timestamp(now_time_str, format);
88+
// NOTE: cannot directly compare timestamps because some precision is lost during ts->str conversion
89+
ASSERT_EQ(timestamp_to_string(now_time_from_str, format), expected_string_os.str());
5590
}
5691

5792
// old time
@@ -68,7 +103,13 @@ TEST(time_utils_test, timestamp_to_string)
68103
<< "-" << 39
69104
<< "-" << 42;
70105

106+
// Test timestamp_to_string
71107
ASSERT_EQ(old_time_str, expected_string_os.str());
108+
109+
// Test string_to_timestamp
110+
Timestamp old_time_from_str = string_to_timestamp(old_time_str);
111+
// NOTE: cannot directly compare timestamps because some precision is lost during ts->str conversion
112+
ASSERT_EQ(timestamp_to_string(old_time_from_str), expected_string_os.str());
72113
}
73114

74115
// future time
@@ -85,19 +126,55 @@ TEST(time_utils_test, timestamp_to_string)
85126
<< "-" << "00"
86127
<< "-" << "00";
87128

129+
// Test timestamp_to_string
88130
ASSERT_EQ(future_time_str, expected_string_os.str());
131+
132+
// Test string_to_timestamp
133+
Timestamp future_time_from_str = string_to_timestamp(future_time_str);
134+
// NOTE: cannot directly compare timestamps because some precision is lost during ts->str conversion
135+
ASSERT_EQ(timestamp_to_string(future_time_from_str), expected_string_os.str());
136+
}
137+
138+
// some time today
139+
{
140+
Timestamp some_time_today = time_to_timestamp(13u, 13u, 13u);
141+
std::string some_time_today_str = timestamp_to_string(some_time_today);
142+
143+
// Get current timestamp and use its date to construct expected string
144+
Timestamp now_ts = now();
145+
std::string now_time_str = timestamp_to_string(now_ts);
146+
time_t now_time = std::chrono::system_clock::to_time_t(now_ts);
147+
std::tm now_tm = *gmtime(&now_time);
148+
149+
std::ostringstream expected_string_os;
150+
expected_string_os
151+
<< number_trailing_zeros_format(now_tm.tm_year + 1900, 4)
152+
<< "-" << number_trailing_zeros_format(now_tm.tm_mon + 1, 2)
153+
<< "-" << number_trailing_zeros_format(now_tm.tm_mday, 2)
154+
<< "_" << "13"
155+
<< "-" << "13"
156+
<< "-" << "13";
157+
158+
// Test timestamp_to_string
159+
ASSERT_EQ(some_time_today_str, expected_string_os.str());
160+
161+
// Test string_to_timestamp
162+
Timestamp some_time_today_from_str = string_to_timestamp(some_time_today_str);
163+
// NOTE: cannot directly compare timestamps because some precision is lost during ts->str conversion
164+
ASSERT_EQ(timestamp_to_string(some_time_today_from_str), expected_string_os.str());
89165
}
90166
}
91167

92168
/**
93-
* Test function timestamp_to_string with a time stamp with local time.
169+
* Test function timestamp_to_string with a time stamp with local time, as well the inverse conversion string_to_timestamp.
94170
*
95171
* NOTE: only case is now because the local time zone depends on the part of the year and the test becomes a mess.
96172
*/
97-
TEST(time_utils_test, timestamp_to_string_local)
173+
TEST(time_utils_test, timestamp_to_string_to_timestamp_local)
98174
{
99175
Timestamp now_time = now();
100-
std::string now_time_str = timestamp_to_string(now_time, "%Y-%m-%d_%H-%M-%S", true);
176+
std::string format = "%Y-%m-%d_%H-%M-%S";
177+
std::string now_time_str = timestamp_to_string(now_time, format, true);
101178

102179
time_t time = std::chrono::system_clock::to_time_t(now_time);
103180
std::tm local_tm = *localtime(&time);
@@ -112,7 +189,13 @@ TEST(time_utils_test, timestamp_to_string_local)
112189
<< "-" << number_trailing_zeros_format(local_tm.tm_min, 2)
113190
<< "-" << number_trailing_zeros_format(local_tm.tm_sec, 2);
114191

192+
// Test timestamp_to_string
115193
ASSERT_EQ(now_time_str, expected_string_os.str());
194+
195+
// Test string_to_timestamp
196+
Timestamp now_time_from_str = string_to_timestamp(now_time_str, format, true);
197+
// NOTE: cannot directly compare timestamps because some precision is lost during ts->str conversion
198+
ASSERT_EQ(timestamp_to_string(now_time_from_str, format, true), expected_string_os.str());
116199
}
117200

118201
/**

0 commit comments

Comments
 (0)