Skip to content

Commit 25e49b3

Browse files
adrogolyubAndrey Drogolyub
andauthored
Rework datetime (#20)
* add overloads * add test * rework CurrentDateTimeString * add test * rework ToStr * rework test for clang * use variadic templates * remove comment * link to libm --------- Co-authored-by: Andrey Drogolyub <[email protected]>
1 parent a4e177e commit 25e49b3

File tree

4 files changed

+201
-17
lines changed

4 files changed

+201
-17
lines changed

prj/cmake/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ include_directories(${ROOT_DIR}/src)
88
file(GLOB_RECURSE TEST_SRC ${ROOT_DIR}/src/Test/Test*.cpp)
99
add_executable(Test ${TEST_SRC})
1010

11-
target_link_libraries(Test -lpthread -lstdc++ -lstdc++fs)
11+
target_link_libraries(Test -lpthread -lstdc++ -lstdc++fs -lm)
1212

1313
if(UNIX)
1414
target_link_libraries(Test -ldl)

src/Cpl/String.h

Lines changed: 101 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,84 @@
3434
#include <cstddef>
3535
#include <memory>
3636

37+
#if _WIN32
38+
39+
#ifndef NOMINMAX
40+
#define NOMINMAX
41+
#endif
42+
43+
#include "windows.h"
44+
#include "winsock.h"
45+
46+
#define CPL_CURRENT_DATE_TIME_PRECISION 3
47+
48+
namespace
49+
{
50+
// PostgreSQL's implementation of gettimeofday for Windows
51+
/*
52+
* gettimeofday.c
53+
* Win32 gettimeofday() replacement
54+
*
55+
* src/port/gettimeofday.c
56+
*
57+
* Copyright (c) 2003 SRA, Inc.
58+
* Copyright (c) 2003 SKC, Inc.
59+
*
60+
* Permission to use, copy, modify, and distribute this software and
61+
* its documentation for any purpose, without fee, and without a
62+
* written agreement is hereby granted, provided that the above
63+
* copyright notice and this paragraph and the following two
64+
* paragraphs appear in all copies.
65+
*
66+
* IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT,
67+
* INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
68+
* LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
69+
* DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED
70+
* OF THE POSSIBILITY OF SUCH DAMAGE.
71+
*
72+
* THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
73+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
74+
* A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS
75+
* IS" BASIS, AND THE AUTHOR HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE,
76+
* SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
77+
*/
78+
79+
/* FILETIME of Jan 1 1970 00:00:00. */
80+
static const unsigned __int64 epoch = ((unsigned __int64)116444736000000000ULL);
81+
82+
/*
83+
* timezone information is stored outside the kernel so tzp isn't used anymore.
84+
*
85+
* Note: this function is not for Win32 high precision timing purpose. See
86+
* elapsed_time().
87+
*/
88+
int gettimeofday(struct timeval* tp, struct timezone* tzp)
89+
{
90+
FILETIME file_time;
91+
SYSTEMTIME system_time;
92+
ULARGE_INTEGER ularge;
93+
94+
GetSystemTime(&system_time);
95+
SystemTimeToFileTime(&system_time, &file_time);
96+
ularge.LowPart = file_time.dwLowDateTime;
97+
ularge.HighPart = file_time.dwHighDateTime;
98+
99+
tp->tv_sec = (long)((ularge.QuadPart - epoch) / 10000000L);
100+
tp->tv_usec = (long)(system_time.wMilliseconds * 1000);
101+
102+
return 0;
103+
}
104+
}
105+
106+
#elif __linux__
107+
#include <sys/time.h>
108+
109+
#define CPL_CURRENT_DATE_TIME_PRECISION 6
110+
111+
#endif
112+
113+
114+
37115
namespace Cpl
38116
{
39117
template<class T> CPL_INLINE String ToStr(const T& value)
@@ -43,6 +121,13 @@ namespace Cpl
43121
return ss.str();
44122
}
45123

124+
template<class T> CPL_INLINE String ToStr(T value, int width)
125+
{
126+
std::stringstream ss;
127+
ss << std::setfill('0') << std::setw(width) << value;
128+
return ss.str();
129+
}
130+
46131
template<> CPL_INLINE String ToStr<size_t>(const size_t& value)
47132
{
48133
return ToStr((ptrdiff_t)value);
@@ -66,20 +151,6 @@ namespace Cpl
66151

67152
//-----------------------------------------------------------------------------------
68153

69-
CPL_INLINE String ToStr(int value, int width)
70-
{
71-
std::stringstream ss;
72-
ss << std::setfill('0') << std::setw(width) << value;
73-
return ss.str();
74-
}
75-
76-
CPL_INLINE String ToStr(size_t value, int width)
77-
{
78-
std::stringstream ss;
79-
ss << std::setfill('0') << std::setw(width) << value;
80-
return ss.str();
81-
}
82-
83154
CPL_INLINE String ToStr(double value, int precision, bool zero = true)
84155
{
85156
std::stringstream ss;
@@ -346,18 +417,32 @@ namespace Cpl
346417
#pragma warning(push)
347418
#pragma warning(disable: 4996)
348419
#endif
349-
CPL_INLINE String CurrentDateTimeString(bool date = true, bool time = true)
420+
// For Windows time precision is milliseconds
421+
CPL_INLINE String CurrentDateTimeString(bool date = true, bool time = true, int msDigits = CPL_CURRENT_DATE_TIME_PRECISION)
350422
{
351423
std::time_t t;
352424
std::time(&t);
353425
std::tm* tm = ::localtime(&t);
426+
struct timeval current_time;
427+
gettimeofday(&current_time, NULL);
354428
std::stringstream ss;
429+
355430
if (date)
356431
ss << ToStr(tm->tm_year + 1900, 4) << "." << ToStr(tm->tm_mon + 1, 2) << "." << ToStr(tm->tm_mday, 2);
357432
if (date && time)
358433
ss << " ";
359-
if(time)
434+
if (time)
435+
{
360436
ss << ToStr(tm->tm_hour, 2) << ":" << ToStr(tm->tm_min, 2) << ":" << ToStr(tm->tm_sec, 2);
437+
if (msDigits > 0)
438+
{
439+
if (msDigits > CPL_CURRENT_DATE_TIME_PRECISION)
440+
msDigits = CPL_CURRENT_DATE_TIME_PRECISION;
441+
// 6 because we are working with microseconds
442+
auto sInt = static_cast<decltype(current_time.tv_usec)>((double)current_time.tv_usec * pow(10, (int)msDigits - 6));
443+
ss << "." << ToStr(sInt, msDigits);
444+
}
445+
}
361446
return ss.str();
362447
}
363448
#ifdef _MSC_VER

src/Test/Test.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,11 @@ namespace Test
6262
TEST_ADD(StartsWith);
6363
TEST_ADD(EndsWith);
6464

65+
TEST_ADD(CurrentDateTimeString);
6566
TEST_ADD(SeparateString);
6667
TEST_ADD(SeparateStringMulti);
6768
TEST_ADD(TimeToStr);
69+
TEST_ADD(ToStr);
6870

6971
TEST_ADD(PolygonHasPoint);
7072
TEST_ADD(PolygonOverlapsRectangle);

src/Test/TestString.cpp

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,103 @@ namespace Test
217217
return true;
218218
}
219219

220+
template <typename T>
221+
void toStrTestImpl(const T& x) {
222+
Cpl::String s = Cpl::ToStr(x);
223+
std::cout << " ToStr((" << typeid(T).name() << ")" << x << ")='" << s << "'" << std::endl;
224+
};
225+
template<typename T, typename... Ts>
226+
void toStrTestImpl(const T& x, Ts... ts) {
227+
toStrTestImpl(x);
228+
toStrTestImpl(ts...);
229+
};
230+
231+
bool ToStrTest()
232+
{
233+
/*
234+
This approach is used to maintain compatibility for old compilers.
235+
Modern approach would be:
236+
std::tuple vals = {
237+
(size_t)1, (int)(-1), (unsigned int)1, (long int)(-1),
238+
(unsigned long int)1, (float)1, (double)1,
239+
};
240+
241+
std::apply([](auto&&...elems)
242+
{
243+
Cpl::String s;
244+
((s = Cpl::ToStr(elems)), ...);
245+
}, vals);
246+
*/
247+
toStrTestImpl(
248+
(size_t)1,
249+
(int)(-1),
250+
(unsigned int)1,
251+
(long int)(-1),
252+
(unsigned long int)1,
253+
(float)1,
254+
(double)1);
255+
256+
return true;
257+
}
258+
259+
bool CurrentDateTimeStringTest()
260+
{
261+
std::vector<std::pair<Cpl::String, size_t>> testCases =
262+
{
263+
#if _WIN32
264+
{Cpl::CurrentDateTimeString(true, false), 10},
265+
{Cpl::CurrentDateTimeString(false, true), 12},
266+
{Cpl::CurrentDateTimeString(false, true, 0), 8},
267+
{Cpl::CurrentDateTimeString(false, true, 1), 10},
268+
{Cpl::CurrentDateTimeString(false, true, 2), 11},
269+
{Cpl::CurrentDateTimeString(false, true, 3), 12},
270+
{Cpl::CurrentDateTimeString(false, true, 4), 12},
271+
{Cpl::CurrentDateTimeString(true, true), 23},
272+
{Cpl::CurrentDateTimeString(true, true, 0), 19},
273+
{Cpl::CurrentDateTimeString(true, true, 1), 21},
274+
{Cpl::CurrentDateTimeString(true, true, 2), 22},
275+
{Cpl::CurrentDateTimeString(true, true, 3), 23},
276+
#elif __linux__
277+
{Cpl::CurrentDateTimeString(true, false), 10},
278+
{Cpl::CurrentDateTimeString(false, true), 15},
279+
{Cpl::CurrentDateTimeString(false, true, 0), 8},
280+
{Cpl::CurrentDateTimeString(false, true, 1), 10},
281+
{Cpl::CurrentDateTimeString(false, true, 2), 11},
282+
{Cpl::CurrentDateTimeString(false, true, 3), 12},
283+
{Cpl::CurrentDateTimeString(false, true, 4), 13},
284+
{Cpl::CurrentDateTimeString(false, true, 5), 14},
285+
{Cpl::CurrentDateTimeString(false, true, 6), 15},
286+
{Cpl::CurrentDateTimeString(false, true, 10), 15},
287+
{Cpl::CurrentDateTimeString(true, true), 26},
288+
{Cpl::CurrentDateTimeString(true, true, 0), 19},
289+
{Cpl::CurrentDateTimeString(true, true, 1), 21},
290+
{Cpl::CurrentDateTimeString(true, true, 2), 22},
291+
{Cpl::CurrentDateTimeString(true, true, 3), 23},
292+
{Cpl::CurrentDateTimeString(true, true, 4), 24},
293+
{Cpl::CurrentDateTimeString(true, true, 5), 25},
294+
{Cpl::CurrentDateTimeString(true, true, 6), 26},
295+
{Cpl::CurrentDateTimeString(true, true, 10), 26},
296+
#endif
297+
};
298+
299+
size_t i = 0;
300+
for (const auto& testCase : testCases)
301+
{
302+
if (testCase.first.length() != testCase.second)
303+
{
304+
CPL_LOG_SS(Error, "Test case " << i << ": '" << testCase.first << "' has length " << testCase.first.length() << " instead of " << testCase.second);
305+
return false;
306+
}
307+
else
308+
{
309+
CPL_LOG_SS(Info, "Test case " << i << ": '" << testCase.first << "' length " << testCase.second);
310+
}
311+
++i;
312+
}
313+
314+
return true;
315+
}
316+
220317
bool TimeToStrTest()
221318
{
222319
std::vector<std::pair<double, std::pair<Cpl::String, Cpl::String>>> testCases =

0 commit comments

Comments
 (0)