Skip to content

Commit 848151e

Browse files
Add date/time parser tests.
1 parent 07bf5c1 commit 848151e

File tree

2 files changed

+305
-1
lines changed

2 files changed

+305
-1
lines changed

tests/core/usage/CMakeLists.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ set(test_files
3333
BooleanExpression.cpp
3434
CustomQuery.cpp
3535
DateTime.cpp
36+
DateTimeParser.cpp
3637
Interpret.cpp
3738
Insert.cpp
3839
Remove.cpp
@@ -77,4 +78,4 @@ endforeach()
7778
# OUTPUT "${CMAKE_CURRENT_LIST_DIR}/Sample.h"
7879
# COMMAND "${PYTHON_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/scripts/ddl2cpp" "${CMAKE_CURRENT_LIST_DIR}/sample.sql" Sample test
7980
# DEPENDS "${CMAKE_CURRENT_LIST_DIR}/sample.sql"
80-
# VERBATIM)
81+
# VERBATIM)

tests/core/usage/DateTimeParser.cpp

+303
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
/*
2+
* Copyright (c) 2023, Vesselin Atanasov
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without modification,
6+
* are permitted provided that the following conditions are met:
7+
*
8+
* * Redistributions of source code must retain the above copyright notice,
9+
* this list of conditions and the following disclaimer.
10+
* * Redistributions in binary form must reproduce the above copyright notice,
11+
* this list of conditions and the following disclaimer in the documentation
12+
* and/or other materials provided with the distribution.
13+
*
14+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17+
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
18+
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19+
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21+
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
22+
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
23+
* OF THE POSSIBILITY OF SUCH DAMAGE.
24+
*/
25+
26+
#include <sqlpp11/detail/parse_date_time.h>
27+
#include <sqlpp11/sqlpp11.h>
28+
29+
#include <iostream>
30+
#include <vector>
31+
32+
namespace
33+
{
34+
template <typename L, typename R>
35+
void require_equal(int line, const L& l, const R& r)
36+
{
37+
if (l != r)
38+
{
39+
std::cerr << line << ": ";
40+
serialize(sqlpp::wrap_operand_t<L>{l}, std::cerr);
41+
std::cerr << " != ";
42+
serialize(sqlpp::wrap_operand_t<R>{r}, std::cerr);
43+
std::cerr << std::endl;
44+
throw std::runtime_error("Unexpected result");
45+
}
46+
}
47+
48+
void test_valid_dates()
49+
{
50+
using namespace sqlpp::chrono;
51+
using namespace date;
52+
53+
for (const auto& date_pair : std::vector<std::pair<const char*, day_point>>{
54+
// Minimum and maximum dates
55+
{"0001-01-01", year{1}/1/1}, {"9999-12-31", year{9999}/12/31},
56+
// Month minimum and maximum days
57+
{"1999-01-01", year{1999}/1/1}, {"1999-01-31", year{1999}/1/31},
58+
{"1999-02-01", year{1999}/2/1}, {"1999-02-28", year{1999}/2/28},
59+
{"1999-03-01", year{1999}/3/1}, {"1999-03-31", year{1999}/3/31},
60+
{"1999-04-01", year{1999}/4/1}, {"1999-04-30", year{1999}/4/30},
61+
{"1999-05-01", year{1999}/5/1}, {"1999-05-31", year{1999}/5/31},
62+
{"1999-06-01", year{1999}/6/1}, {"1999-06-30", year{1999}/6/30},
63+
{"1999-07-01", year{1999}/7/1}, {"1999-07-31", year{1999}/7/31},
64+
{"1999-08-01", year{1999}/8/1}, {"1999-08-31", year{1999}/8/31},
65+
{"1999-09-01", year{1999}/9/1}, {"1999-09-30", year{1999}/9/30},
66+
{"1999-10-01", year{1999}/10/1}, {"1999-10-31", year{1999}/10/31},
67+
{"1999-11-01", year{1999}/11/1}, {"1999-11-30", year{1999}/11/30},
68+
{"1999-12-01", year{1999}/12/1}, {"1999-12-31", year{1999}/12/31},
69+
// YYYY-02-29
70+
{"2396-02-29", year{2396}/2/29},
71+
{"2400-02-29", year{2400}/2/29}
72+
})
73+
{
74+
day_point dp;
75+
if (sqlpp::detail::parse_date(dp, date_pair.first) == false)
76+
{
77+
std::cerr << "Could not parse a valid date string: " << date_pair.first << std::endl;
78+
throw std::runtime_error{"Parse error"};
79+
}
80+
require_equal(__LINE__, dp, date_pair.second);
81+
}
82+
}
83+
84+
void test_invalid_dates()
85+
{
86+
using namespace sqlpp::chrono;
87+
88+
for (const auto& date_str : std::vector<const char*>{
89+
// Invalid year
90+
"", "1", "12", "123" , "1234", "A",
91+
// Invalid month
92+
"1980--02", "1980-1-02", "1980-00-02", "1980-123-02", "1980-13-02", "1980-W-02",
93+
// Invalid day
94+
"1980-01-", "1980-01-0", "1980-01-00", "1980-01-32", "1980-01-123", "1980-01-Q",
95+
// Invalid separator
96+
"1980 01 02", "1980- 01-02", "1980 -01-02", "1980-01 -02", "1980-01- 02", "1980-01T02"
97+
// Invalid YYYY-02-29
98+
"1981-02-29", "2100-02-29",
99+
// Trailing characters
100+
"1980-01-02 ", "1980-01-02T", "1980-01-02 UTC", "1980-01-02EST", "1980-01-02+01"
101+
})
102+
{
103+
day_point dp;
104+
if (sqlpp::detail::parse_date(dp, date_str))
105+
{
106+
std::cerr << "Parsed successfully an invalid date string " << date_str << ", value ";
107+
serialize(sqlpp::wrap_operand_t<day_point>{dp}, std::cerr);
108+
std::cerr << std::endl;
109+
throw std::runtime_error{"Parse error"};
110+
}
111+
}
112+
}
113+
114+
void test_valid_time_of_day()
115+
{
116+
using namespace std::chrono;
117+
118+
for (const auto& tod_pair : std::vector<std::pair<const char*, microseconds>>{
119+
// Minimum value
120+
{"00:00:00", hours{0}},
121+
// Maximum hours
122+
{"23:00:00", hours{23}},
123+
// Maximum minutes
124+
{"00:59:00", minutes{59}},
125+
// Maximum seconds
126+
{"00:00:59", seconds{59}},
127+
// Second fractions
128+
{"01:23:54.000001", hours{1} + minutes{23} + seconds{54} + microseconds{1}},
129+
{"01:23:54.999999", hours{1} + minutes{23} + seconds{54} + microseconds{999999}},
130+
// Timezone offsets
131+
{"10:09:08+03", hours{10} + minutes{9} + seconds{8} - hours{3}},
132+
{"10:09:08-03", hours{10} + minutes{9} + seconds{8} + hours{3}},
133+
{"10:09:08+03:02", hours{10} + minutes{9} + seconds{8} - (hours{3} + minutes{2})},
134+
{"10:09:08-03:02", hours{10} + minutes{9} + seconds{8} + (hours{3} + minutes{2})},
135+
{"10:09:08+13:12:11", hours{10} + minutes{9} + seconds{8} - (hours{13} + minutes{12} + seconds{11})},
136+
{"10:09:08-13:12:11", hours{10} + minutes{9} + seconds{8} + (hours{13} + minutes{12} + seconds{11})},
137+
// Second fraction and timezone offset
138+
{"10:09:08.1+03", hours{10} + minutes{9} + seconds{8} + microseconds{100000} - hours{3}},
139+
{"10:09:08.12-07:40", hours{10} + minutes{9} + seconds{8} + microseconds{120000} + (hours{7} + minutes{40})},
140+
{"10:09:08.123+12:38:49", hours{10} + minutes{9} + seconds{8} + microseconds{123000} - (hours{12} + minutes{38} + seconds{49})}
141+
})
142+
{
143+
microseconds us;
144+
if (sqlpp::detail::parse_time_of_day(us, tod_pair.first) == false)
145+
{
146+
std::cerr << "Could not parse a valid time-of-day string: " << tod_pair.first << std::endl;
147+
throw std::runtime_error{"Parse error"};
148+
}
149+
require_equal(__LINE__, us, tod_pair.second);
150+
}
151+
}
152+
153+
void test_invalid_time_of_day()
154+
{
155+
using namespace std::chrono;
156+
157+
for (const auto& tod_str : std::vector<const char*>{
158+
// Generic string
159+
"A", "BC", "!()",
160+
// Invalid hour
161+
"-01:23:45", "25:00:10", "AA:10:11",
162+
// Invalid minute
163+
"13::07", "13:A:07", "13:1:07", "13:-01:07"
164+
// Invalid second
165+
"04:07:", "04:07:A", "04:07:1", "04:07:-01"
166+
// Invalid fraction
167+
"01:02:03.", "01:02:03.A", "01:02:03.1234567", "01:02:03.1A2",
168+
// Invalid timezone
169+
"01:03:03!01", "01:03:03+A", "01:03:03+1", "01:03:03+1A", "01:03:03+456",
170+
"01:03:03+12:", "01:03:03+12:1", "01:03:03+12:1A", "01:03:03+12:01:",
171+
"01:03:03+12:01:1", "01:03:03+12:01:1A"
172+
})
173+
{
174+
microseconds us;
175+
if (sqlpp::detail::parse_time_of_day(us, tod_str))
176+
{
177+
std::cerr << "Parsed successfully an invalid time-of-day string " << tod_str << ", value ";
178+
serialize(sqlpp::wrap_operand_t<microseconds>{us}, std::cerr);
179+
std::cerr << std::endl;
180+
throw std::runtime_error{"Parse error"};
181+
}
182+
}
183+
}
184+
185+
void test_valid_timestamp()
186+
{
187+
using namespace sqlpp::chrono;
188+
using namespace date;
189+
using namespace std::chrono;
190+
191+
for (const auto& timestamp_pair : std::vector<std::pair<const char*, microsecond_point>>{
192+
// Minimum and maximum timestamps
193+
{"0001-01-01 00:00:00", sys_days{year{1}/1/1}}, {"9999-12-31 23:59:59.999999", sys_days{year{9999}/12/31} + hours{23} + minutes{59} + seconds{59} + microseconds{999999}},
194+
// Timestamp with time zone
195+
{"1234-03-25 23:17:08.479210+10:17:29", sys_days{year{1234}/3/25} + hours{23} + minutes{17} + seconds{8} + microseconds{479210} - (hours{10} + minutes{17} + seconds{29})}
196+
})
197+
{
198+
microsecond_point tp;
199+
if (sqlpp::detail::parse_timestamp(tp, timestamp_pair.first) == false)
200+
{
201+
std::cerr << "Could not parse a valid timestamp string: " << timestamp_pair.first << std::endl;
202+
throw std::runtime_error{"Parse error"};
203+
}
204+
require_equal(__LINE__, tp, timestamp_pair.second);
205+
}
206+
}
207+
208+
void test_invalid_timestamp()
209+
{
210+
using namespace sqlpp::chrono;
211+
212+
for (const auto& timestamp_str : std::vector<const char*>{
213+
// Generic string
214+
"", "B", ")-#\\",
215+
// Invalid date
216+
"197%-03-17 10:32:09",
217+
// Invalid time of day
218+
"2020-02-18 22:2:28"
219+
// Invalid time zone
220+
"1924-02-28 18:35:36+1"
221+
// Leading space
222+
" 2030-17-01 15:20:30",
223+
// Trailing space
224+
"2030-17-01 15:20:30 "
225+
})
226+
{
227+
microsecond_point tp;
228+
if (sqlpp::detail::parse_timestamp(tp, timestamp_str))
229+
{
230+
std::cerr << "Parsed successfully an invalid timestamp string " << timestamp_str << ", value ";
231+
serialize(sqlpp::wrap_operand_t<microsecond_point>{tp}, std::cerr);
232+
std::cerr << std::endl;
233+
throw std::runtime_error{"Parse error"};
234+
}
235+
}
236+
}
237+
238+
void test_valid_date_or_timestamp()
239+
{
240+
using namespace sqlpp::chrono;
241+
using namespace date;
242+
using namespace std::chrono;
243+
244+
for (const auto& timestamp_pair : std::vector<std::pair<const char*, microsecond_point>>{
245+
// Valid date
246+
{"1998-02-03", sys_days{year{1998}/2/3}},
247+
// Valid timestamp
248+
{"2015-07-08 06:32:45.872+23:14:39", sys_days{year{2015}/7/8} + hours{6} + minutes{32} + seconds{45} + microseconds{872000} - (hours{23} + minutes{14} + seconds{39})}
249+
})
250+
{
251+
microsecond_point tp;
252+
if (sqlpp::detail::parse_date_or_timestamp(tp, timestamp_pair.first) == false)
253+
{
254+
std::cerr << "Could not parse a valid date or timestamp string: " << timestamp_pair.first << std::endl;
255+
throw std::runtime_error{"Parse error"};
256+
}
257+
require_equal(__LINE__, tp, timestamp_pair.second);
258+
}
259+
}
260+
261+
void test_invalid_date_or_timestamp()
262+
{
263+
using namespace sqlpp::chrono;
264+
265+
for (const auto& timestamp_str : std::vector<const char*>{
266+
// Generic string
267+
"", "C", "/=",
268+
// Invalid dates
269+
"A123-01-02", "1980-E-04", "1981-09-",
270+
// Invalid timestamps
271+
"2023-12-31 1:02:03", "2024-03-04 05::06",
272+
// Invalid time zone
273+
"1930-03-18 17:30:31+01:",
274+
// Leading space
275+
" 1930-03-18 17:30:31+01",
276+
// Trailing space
277+
"1930-03-18 17:30:31+01 "
278+
})
279+
{
280+
microsecond_point tp;
281+
if (sqlpp::detail::parse_date_or_timestamp(tp, timestamp_str))
282+
{
283+
std::cerr << "Parsed successfully an invalid date or timestamp string " << timestamp_str << ", value ";
284+
serialize(sqlpp::wrap_operand_t<microsecond_point>{tp}, std::cerr);
285+
std::cerr << std::endl;
286+
throw std::runtime_error{"Parse error"};
287+
}
288+
}
289+
}
290+
}
291+
292+
int DateTimeParser(int, char*[])
293+
{
294+
test_valid_dates();
295+
test_invalid_dates();
296+
test_valid_time_of_day();
297+
test_invalid_time_of_day();
298+
test_valid_timestamp();
299+
test_invalid_timestamp();
300+
test_valid_date_or_timestamp();
301+
test_invalid_date_or_timestamp();
302+
return 0;
303+
}

0 commit comments

Comments
 (0)