mirror of
https://github.com/rbock/sqlpp11.git
synced 2024-11-15 20:31:16 +08:00
Treat PostgreSQL time values as being in UTC time zone (#487)
* When inserting values into "timestamp with time zone" fields treat the value as being in the UTC time zone. * Simplify parsing of PostgreSQL date/time responses by using regular expressions. Always convert response times with time zone to UTC. * Add tests which check if timestamp and date fields are treated as having a UTC time zone. * Clarify the test comments.
This commit is contained in:
parent
38aba217d4
commit
a72b172a52
@ -67,7 +67,7 @@ namespace sqlpp
|
|||||||
const auto dp = ::sqlpp::chrono::floor<::date::days>(t._t);
|
const auto dp = ::sqlpp::chrono::floor<::date::days>(t._t);
|
||||||
const auto time = ::date::make_time(t._t - dp);
|
const auto time = ::date::make_time(t._t - dp);
|
||||||
const auto ymd = ::date::year_month_day{dp};
|
const auto ymd = ::date::year_month_day{dp};
|
||||||
context << "TIMESTAMP '" << ymd << ' ' << time << "'";
|
context << "TIMESTAMP WITH TIME ZONE '" << ymd << ' ' << time << "+00'";
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
} // namespace sqlpp
|
} // namespace sqlpp
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <regex>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#include "detail/prepared_statement_handle.h"
|
#include "detail/prepared_statement_handle.h"
|
||||||
@ -225,7 +226,6 @@ namespace sqlpp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// same parsing logic as SQLite connector
|
|
||||||
// PostgreSQL will return one of those (using the default ISO client):
|
// PostgreSQL will return one of those (using the default ISO client):
|
||||||
//
|
//
|
||||||
// 2010-10-11 01:02:03 - ISO timestamp without timezone
|
// 2010-10-11 01:02:03 - ISO timestamp without timezone
|
||||||
@ -234,71 +234,6 @@ namespace sqlpp
|
|||||||
// 1992-10-10 01:02:03-06:30 - for some timezones with non-hour offset
|
// 1992-10-10 01:02:03-06:30 - for some timezones with non-hour offset
|
||||||
// 1900-01-01 - date only
|
// 1900-01-01 - date only
|
||||||
// we do not support time-only values !
|
// we do not support time-only values !
|
||||||
namespace detail
|
|
||||||
{
|
|
||||||
inline auto check_first_digit(const char* text, bool digitFlag) -> bool
|
|
||||||
{
|
|
||||||
if (digitFlag)
|
|
||||||
{
|
|
||||||
if (not std::isdigit(*text))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (std::isdigit(*text) or *text == '\0')
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto check_date_digits(const char* text) -> bool
|
|
||||||
{
|
|
||||||
for (const auto digitFlag : {true, true, true, true, false, true, true, false, true, true}) // YYYY-MM-DD
|
|
||||||
{
|
|
||||||
if (not check_first_digit(text, digitFlag))
|
|
||||||
return false;
|
|
||||||
++text;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto check_time_digits(const char* text) -> bool
|
|
||||||
{
|
|
||||||
for (const auto digitFlag : {true, true, false, true, true, false, true, true}) // hh:mm:ss
|
|
||||||
{
|
|
||||||
if (not check_first_digit(text, digitFlag))
|
|
||||||
return false;
|
|
||||||
++text;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto check_us_digits(const char* text) -> bool
|
|
||||||
{
|
|
||||||
for (const auto digitFlag : {true, true, true, true, true, true})
|
|
||||||
{
|
|
||||||
if (not check_first_digit(text, digitFlag))
|
|
||||||
return false;
|
|
||||||
++text;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto check_tz_digits(const char* text) -> bool
|
|
||||||
{
|
|
||||||
for (const auto digitFlag : {false, true, true, false, true, true})
|
|
||||||
{
|
|
||||||
if (not check_first_digit(text, digitFlag))
|
|
||||||
return false;
|
|
||||||
++text;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
inline void bind_result_t::_bind_date_result(size_t _index, ::sqlpp::chrono::day_point* value, bool* is_null)
|
inline void bind_result_t::_bind_date_result(size_t _index, ::sqlpp::chrono::day_point* value, bool* is_null)
|
||||||
{
|
{
|
||||||
@ -320,16 +255,19 @@ namespace sqlpp
|
|||||||
std::cerr << "PostgreSQL debug: date string: " << date_string << std::endl;
|
std::cerr << "PostgreSQL debug: date string: " << date_string << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (detail::check_date_digits(date_string))
|
static const std::regex rx {"(\\d{4})-(\\d{2})-(\\d{2})"};
|
||||||
{
|
std::cmatch mr;
|
||||||
const auto ymd =
|
if (std::regex_match (date_string, mr, rx)) {
|
||||||
::date::year(std::atoi(date_string)) / std::atoi(date_string + 5) / std::atoi(date_string + 8);
|
*value =
|
||||||
*value = ::sqlpp::chrono::day_point(ymd);
|
::sqlpp::chrono::day_point{
|
||||||
}
|
::date::year{std::atoi(date_string + mr.position(1))} / // Year
|
||||||
else
|
std::atoi(date_string + mr.position(2)) / // Month
|
||||||
{
|
std::atoi(date_string + mr.position(3)) // Day of month
|
||||||
if (_handle->debug())
|
};
|
||||||
|
} else {
|
||||||
|
if (_handle->debug()) {
|
||||||
std::cerr << "PostgreSQL debug: got invalid date '" << date_string << "'" << std::endl;
|
std::cerr << "PostgreSQL debug: got invalid date '" << date_string << "'" << std::endl;
|
||||||
|
}
|
||||||
*value = {};
|
*value = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -339,7 +277,7 @@ namespace sqlpp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// always returns local time for timestamp with time zone
|
// always returns UTC time for timestamp with time zone
|
||||||
inline void bind_result_t::_bind_date_time_result(size_t _index, ::sqlpp::chrono::microsecond_point* value, bool* is_null)
|
inline void bind_result_t::_bind_date_time_result(size_t _index, ::sqlpp::chrono::microsecond_point* value, bool* is_null)
|
||||||
{
|
{
|
||||||
auto index = static_cast<int>(_index);
|
auto index = static_cast<int>(_index);
|
||||||
@ -358,52 +296,44 @@ namespace sqlpp
|
|||||||
{
|
{
|
||||||
std::cerr << "PostgreSQL debug: got date_time string: " << date_string << std::endl;
|
std::cerr << "PostgreSQL debug: got date_time string: " << date_string << std::endl;
|
||||||
}
|
}
|
||||||
if (detail::check_date_digits(date_string))
|
|
||||||
{
|
static const std::regex rx {
|
||||||
const auto ymd =
|
"(\\d{4})-(\\d{2})-(\\d{2}) "
|
||||||
::date::year(std::atoi(date_string)) / std::atoi(date_string + 5) / std::atoi(date_string + 8);
|
"(\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d{1,6}))?"
|
||||||
*value = ::sqlpp::chrono::day_point(ymd);
|
"(?:([+-])(\\d{2})(?::(\\d{2})(?::(\\d{2}))?)?)?"
|
||||||
|
};
|
||||||
|
std::cmatch mr;
|
||||||
|
if (std::regex_match (date_string, mr, rx)) {
|
||||||
|
*value =
|
||||||
|
::sqlpp::chrono::day_point{
|
||||||
|
::date::year{std::atoi(date_string + mr.position(1))} / // Year
|
||||||
|
std::atoi(date_string + mr.position(2)) / // Month
|
||||||
|
std::atoi(date_string + mr.position(3)) // Day of month
|
||||||
|
} +
|
||||||
|
std::chrono::hours{std::atoi(date_string + mr.position(4))} + // Hour
|
||||||
|
std::chrono::minutes{std::atoi(date_string + mr.position(5))} + // Minute
|
||||||
|
std::chrono::seconds{std::atoi(date_string + mr.position(6))} + // Second
|
||||||
|
::std::chrono::microseconds{ // Microsecond
|
||||||
|
mr[7].matched ? std::stoi((mr[7].str() + "000000").substr(0, 6)) : 0
|
||||||
|
};
|
||||||
|
if (mr[8].matched) {
|
||||||
|
const auto tz_sign = (date_string[mr.position(8)] == '+') ? 1 : -1;
|
||||||
|
const auto tz_offset =
|
||||||
|
std::chrono::hours{std::atoi(date_string + mr.position(9))} +
|
||||||
|
std::chrono::minutes{mr[10].matched ? std::atoi(date_string + mr.position(10)) : 0} +
|
||||||
|
std::chrono::seconds{mr[11].matched ? std::atoi(date_string + mr.position(11)) : 0};
|
||||||
|
*value -= tz_sign * tz_offset;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_handle->debug()) {
|
||||||
|
std::cerr << "PostgreSQL debug: got invalid date_time '" << date_string << "'" << std::endl;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_handle->debug())
|
|
||||||
std::cerr << "PostgreSQL debug: got invalid date_time" << std::endl;
|
|
||||||
*value = {};
|
*value = {};
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std::strlen(date_string) <= 11)
|
|
||||||
return;
|
|
||||||
const auto time_string = date_string + 11; // YYYY-MM-DDT
|
|
||||||
if (detail::check_time_digits(time_string))
|
|
||||||
{
|
|
||||||
*value += std::chrono::hours(std::atoi(time_string)) + std::chrono::minutes(std::atoi(time_string + 3)) +
|
|
||||||
std::chrono::seconds(std::atoi(time_string + 6));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (std::strlen(time_string) <= 9)
|
|
||||||
return;
|
|
||||||
auto us_string = time_string + 9; // hh:mm:ss.
|
|
||||||
int usec = 0;
|
|
||||||
for (size_t i = 0u; i < 6u; ++i)
|
|
||||||
{
|
|
||||||
if (std::isdigit(us_string[0]))
|
|
||||||
{
|
|
||||||
usec = 10 * usec + (us_string[0] - '0');
|
|
||||||
++us_string;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
usec *= 10;
|
|
||||||
}
|
|
||||||
*value += ::std::chrono::microseconds(usec);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// always returns local time for time with time zone
|
// always returns UTC time for time with time zone
|
||||||
inline void bind_result_t::_bind_time_of_day_result(size_t _index, ::std::chrono::microseconds* value, bool* is_null)
|
inline void bind_result_t::_bind_time_of_day_result(size_t _index, ::std::chrono::microseconds* value, bool* is_null)
|
||||||
{
|
{
|
||||||
auto index = static_cast<int>(_index);
|
auto index = static_cast<int>(_index);
|
||||||
@ -423,31 +353,33 @@ namespace sqlpp
|
|||||||
std::cerr << "PostgreSQL debug: got time string: " << time_string << std::endl;
|
std::cerr << "PostgreSQL debug: got time string: " << time_string << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (detail::check_time_digits(time_string))
|
static const std::regex rx {
|
||||||
{
|
"(\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d{1,6}))?"
|
||||||
*value += std::chrono::hours(std::atoi(time_string)) + std::chrono::minutes(std::atoi(time_string + 3)) +
|
"(?:([+-])(\\d{2})(?::(\\d{2})(?::(\\d{2}))?)?)?"
|
||||||
std::chrono::seconds(std::atoi(time_string + 6));
|
};
|
||||||
|
std::cmatch mr;
|
||||||
|
if (std::regex_match (time_string, mr, rx)) {
|
||||||
|
*value =
|
||||||
|
std::chrono::hours{std::atoi(time_string + mr.position(1))} + // Hour
|
||||||
|
std::chrono::minutes{std::atoi(time_string + mr.position(2))} + // Minute
|
||||||
|
std::chrono::seconds{std::atoi(time_string + mr.position(3))} + // Second
|
||||||
|
::std::chrono::microseconds{ // Microsecond
|
||||||
|
mr[4].matched ? std::stoi((mr[4].str() + "000000").substr(0, 6)) : 0
|
||||||
|
};
|
||||||
|
if (mr[5].matched) {
|
||||||
|
const auto tz_sign = (time_string[mr.position(5)] == '+') ? 1 : -1;
|
||||||
|
const auto tz_offset =
|
||||||
|
std::chrono::hours{std::atoi(time_string + mr.position(6))} +
|
||||||
|
std::chrono::minutes{mr[7].matched ? std::atoi(time_string + mr.position(7)) : 0} +
|
||||||
|
std::chrono::seconds{mr[8].matched ? std::atoi(time_string + mr.position(8)) : 0};
|
||||||
|
*value -= tz_sign * tz_offset;
|
||||||
}
|
}
|
||||||
else
|
} else {
|
||||||
{
|
if (_handle->debug()) {
|
||||||
return;
|
std::cerr << "PostgreSQL debug: got invalid time '" << time_string << "'" << std::endl;
|
||||||
}
|
}
|
||||||
|
*value = {};
|
||||||
if (std::strlen(time_string) <= 9)
|
|
||||||
return;
|
|
||||||
auto us_string = time_string + 9; // hh:mm:ss.
|
|
||||||
int usec = 0;
|
|
||||||
for (size_t i = 0u; i < 6u; ++i)
|
|
||||||
{
|
|
||||||
if (std::isdigit(us_string[0]))
|
|
||||||
{
|
|
||||||
usec = 10 * usec + (us_string[0] - '0');
|
|
||||||
++us_string;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
usec *= 10;
|
|
||||||
}
|
|
||||||
*value += ::std::chrono::microseconds(usec);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@ set(test_files
|
|||||||
InsertOnConflict.cpp
|
InsertOnConflict.cpp
|
||||||
Returning.cpp
|
Returning.cpp
|
||||||
Select.cpp
|
Select.cpp
|
||||||
|
TimeZone.cpp
|
||||||
Transaction.cpp
|
Transaction.cpp
|
||||||
Type.cpp
|
Type.cpp
|
||||||
)
|
)
|
||||||
|
164
tests/postgresql/usage/TimeZone.cpp
Normal file
164
tests/postgresql/usage/TimeZone.cpp
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2023 Vesselin Atanasov
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <sqlpp11/postgresql/postgresql.h>
|
||||||
|
#include <sqlpp11/sqlpp11.h>
|
||||||
|
|
||||||
|
#include "make_test_connection.h"
|
||||||
|
#include "TabDateTime.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
void save_regular (sqlpp::postgresql::connection& dbc, sqlpp::chrono::microsecond_point tp, std::chrono::microseconds tod, sqlpp::chrono::day_point dp)
|
||||||
|
{
|
||||||
|
model::TabDateTime tab {};
|
||||||
|
dbc(
|
||||||
|
update(tab)
|
||||||
|
.set(
|
||||||
|
tab.c_timepoint = tp,
|
||||||
|
tab.c_time = tod,
|
||||||
|
tab.c_day = dp
|
||||||
|
)
|
||||||
|
.unconditionally()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void save_prepared (sqlpp::postgresql::connection& dbc, sqlpp::chrono::microsecond_point tp, std::chrono::microseconds tod, sqlpp::chrono::day_point dp)
|
||||||
|
{
|
||||||
|
model::TabDateTime tab {};
|
||||||
|
auto prepared_update = dbc.prepare(
|
||||||
|
update(tab)
|
||||||
|
.set(
|
||||||
|
tab.c_timepoint = parameter(tab.c_timepoint),
|
||||||
|
tab.c_time = parameter(tab.c_time),
|
||||||
|
tab.c_day = parameter(tab.c_day)
|
||||||
|
)
|
||||||
|
.unconditionally()
|
||||||
|
);
|
||||||
|
prepared_update.params.c_timepoint = tp;
|
||||||
|
prepared_update.params.c_time = tod;
|
||||||
|
prepared_update.params.c_day = dp;
|
||||||
|
dbc(prepared_update);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename L, typename R>
|
||||||
|
void require_equal(int line, const L& l, const R& r)
|
||||||
|
{
|
||||||
|
if (l != r)
|
||||||
|
{
|
||||||
|
std::cerr << line << ": ";
|
||||||
|
serialize(::sqlpp::wrap_operand_t<L>{l}, std::cerr);
|
||||||
|
std::cerr << " != ";
|
||||||
|
serialize(::sqlpp::wrap_operand_t<R>{r}, std::cerr);
|
||||||
|
throw std::runtime_error("Unexpected result");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void check_saved_values(sqlpp::postgresql::connection& dbc, sqlpp::chrono::microsecond_point tp, std::chrono::microseconds tod, sqlpp::chrono::day_point dp)
|
||||||
|
{
|
||||||
|
model::TabDateTime tab {};
|
||||||
|
|
||||||
|
const auto &rows_1 = dbc(
|
||||||
|
select(
|
||||||
|
// c_timepoint as microseconds from the start of the UNIX epoch (1970-01-01 00:00:00 UTC)
|
||||||
|
sqlpp::verbatim<sqlpp::integer>("floor(extract(epoch from c_timepoint)*1000000)::int8").as(sqlpp::alias::a),
|
||||||
|
// c_time as microseconds from the start of the day (00:00:00 UTC)
|
||||||
|
sqlpp::verbatim<sqlpp::integer>("floor(extract(epoch from c_time)*1000000)::int8").as(sqlpp::alias::b),
|
||||||
|
// c_day as days from 1970-01-01 (timezone is not applicable to date fields)
|
||||||
|
sqlpp::verbatim<sqlpp::integer>("floor(extract(epoch from c_day)/86400)::int8").as(sqlpp::alias::c)
|
||||||
|
)
|
||||||
|
.from(tab)
|
||||||
|
.unconditionally()
|
||||||
|
);
|
||||||
|
// Check if the internal values of our C++ time variables match the internal values of the PostgreSQL date/time fields.
|
||||||
|
// This tests the conversion of date/time types from C++ to PostgreSQL while skipping the conversion from C++ to PostgreSQL.
|
||||||
|
const auto &row_1 = rows_1.front();
|
||||||
|
require_equal(__LINE__, row_1.a.value(), tp.time_since_epoch().count());
|
||||||
|
require_equal(__LINE__, row_1.b.value(), tod.count());
|
||||||
|
require_equal(__LINE__, row_1.c.value(), dp.time_since_epoch().count());
|
||||||
|
|
||||||
|
// Check if saving date/time variables from C++ to PostgreSQL and then reading them back yields the same values.
|
||||||
|
// This tests the conversion of date/time types from C++ to PostgreSQL and then back from PostgreSQL to C++.
|
||||||
|
const auto rows_2 = dbc(select(all_of(tab)).from(tab).unconditionally());
|
||||||
|
const auto &row_2 = rows_2.front();
|
||||||
|
require_equal(__LINE__, row_2.c_timepoint.value(), tp);
|
||||||
|
require_equal(__LINE__, row_2.c_time.value(), tod);
|
||||||
|
require_equal(__LINE__, row_2.c_day.value(), dp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_time_point(sqlpp::postgresql::connection& dbc, sqlpp::chrono::microsecond_point tp)
|
||||||
|
{
|
||||||
|
auto dp = date::floor<sqlpp::chrono::days> (tp);
|
||||||
|
auto tod = tp - dp; // Time of day
|
||||||
|
|
||||||
|
// Test time values passed in a regular (non-prepared) statement
|
||||||
|
save_regular(dbc, tp, tod, dp);
|
||||||
|
check_saved_values(dbc, tp, tod, dp);
|
||||||
|
|
||||||
|
// Test time values passed in a prepared statement
|
||||||
|
save_prepared(dbc, tp, tod, dp);
|
||||||
|
check_saved_values(dbc, tp, tod, dp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int TimeZone(int, char*[])
|
||||||
|
{
|
||||||
|
namespace sql = sqlpp::postgresql;
|
||||||
|
|
||||||
|
auto dbc = sql::make_test_connection();
|
||||||
|
|
||||||
|
dbc.execute("DROP TABLE IF EXISTS tabdatetime;");
|
||||||
|
dbc.execute(
|
||||||
|
"CREATE TABLE tabdatetime "
|
||||||
|
"("
|
||||||
|
"c_timepoint timestamp with time zone,"
|
||||||
|
"c_time time with time zone,"
|
||||||
|
"c_day date"
|
||||||
|
")"
|
||||||
|
);
|
||||||
|
|
||||||
|
model::TabDateTime tab {};
|
||||||
|
try {
|
||||||
|
dbc(insert_into(tab).default_values());
|
||||||
|
|
||||||
|
std::vector<sqlpp::chrono::microsecond_point> tps {
|
||||||
|
static_cast<date::sys_days>(date::January/1/1970) + std::chrono::hours{1} + std::chrono::minutes{20} + std::chrono::seconds{14} + std::chrono::microseconds{1},
|
||||||
|
static_cast<date::sys_days>(date::June/13/1986) + std::chrono::hours{12} + std::chrono::minutes{0} + std::chrono::seconds{1} + std::chrono::microseconds{123},
|
||||||
|
static_cast<date::sys_days>(date::December/31/2022) + std::chrono::hours{0} + std::chrono::minutes{59} + std::chrono::seconds{59} + std::chrono::microseconds{987654}
|
||||||
|
};
|
||||||
|
for (const auto &tp : tps) {
|
||||||
|
test_time_point(dbc, tp);
|
||||||
|
}
|
||||||
|
} catch (const sql::failure& e) {
|
||||||
|
std::cerr << "Exception: " << e.what() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user