I'm connecting from c++ to postgreSQL using libpq library. I request and obtain the date (timestamp without time zone) from postgreSQL, but the result has an offset that I don't know how to fix.
Postgres table:
id date
integer timestamp without time zone
29996 2014-02-28 23:59:00
result in C++ code:
id: 29996, Date: Sat Mar 01 10:59:00 2014
You can see that the date has the offset. Below is the code that I'm using. Any help will be greatly appreciated
PGconn *m_connection;
PGresult *res;
string strConn = "dbname=test host=localhost user=username password=secret";
m_connection = PQconnectdb(strConn.c_str());
string query = "SELECT id, extract(epoch from date)::bigint ";
query += "FROM table_test ORDER BY id DESC LIMIT 1";
// send query
res = PQexecParams(m_connection, query.c_str(), 0, 0, 0, 0, 0, 0);
string id = PQgetvalue(res, 0, 0);
unsigned char *data = (unsigned char*)PQgetvalue( res, 0, 1 );
unsigned int length = PQgetlength( res, 0 , 1 );
time_t time = _atoi64( (char*)data );
PQclear(res);
std::cout << "id:"<< id << ", Date: " << ctime(&time) << "\n";
The issue is that ctime uses localtime, so that ends up in the offset.
If you want GMT, then you should use asctime(gmtime(&time)), which will give you a date/time without localtime influences.
ctime is the equivalent of asctime(localtime(&time))
Related
I am trying to call a stored procedure which has two output arguments, and get the output programmatically in a C++ program.
Code snippet for executing a query:
char const* const query =
"DECLARE #iMM int, #sMM varchar(100); "
"EXEC sp_myproc #sFoo = 'abcdef', #sBar = 'ghij', #iQux = 1,"
"#iOutInt = #iMM output, #sOutString = #sMM output;"
"PRINT #iMM; PRINT #sMM;";
auto result = SQLExecDirectA(handle, (SQLCHAR*)query, SQL_NTS);
This returns SQL_SUCCESS_WITH_INFO ., but SQLGetDiagRecA does not retrieve the printed output . SQLMoreResults returns 1, SQLParamData returns -1.
The same query does execute successfully in SSMS but I can't figure out how to get the output parameter values programmatically. There are various code examples using SQLBindParameter to retrieve multiple output parameters but none of them use named parameters.
I managed to get the data this way (maybe not optimal but at least it works):
char const* const query =
"EXEC sp_myproc #sFoo = 'abcdef', #sBar = 'ghij', #iQux = 1,"
"#iOutInt = ? output, #sOutString = ? output;"
short out_int;
SQLBindParameter(handle, 1, SQL_PARAM_OUTPUT, SQL_C_SSHORT, SQL_SMALLINT,
0, 0, &out_int, 1, NULL);
char outbuf[61];
SQLBindParameter(handle, 2, SQL_PARAM_OUTPUT, SQL_C_CHAR, SQL_VARCHAR,
60, 0, outbuf, 60, NULL);
auto result = SQLExecDirectA(handle, (SQLCHAR*)query, SQL_NTS);
if ( result == SQL_SUCCESS_WITH_INFO )
{
std::cout << out_int << ", '" << outbuf << "'\n";
}
Using SQL_CHAR instead of SQL_VARCHAR meant the result string was always right-padded with spaces .
I've been learning the Boost Log library
http://www.boost.org/doc/libs/develop/libs/log/doc/html/index.html
but I've been unable to figure out how to display the user's time zone. There is a %q and %Q format option that looks promising but doesn't seem to work (I'm using MSVC++ 2013). Using this format "%Y-%m-%d %H:%M:%S.%f%Q", I get the following output:
1 [2015-08-18 21:27:16.860724] main.cpp#11, Test App Started.
but I would have expected
1 [2015-08-18 21:27:16.860724-08.00] main.cpp#11, Test App Started.
as explained in:
http://www.boost.org/doc/libs/develop/libs/log/doc/html/log/detailed/expressions.html#log.detailed.expressions.formatters
Here's the code I've been trying and a few commented out lines that I have also tried with no luck:
void Log::init() const
{
boost::log::core::get()->add_global_attribute("TimeStamp", boost::log::attributes::utc_clock());
// boost::log::core::get()->add_global_attribute("TimeStamp", boost::log::attributes::local_clock());
boost::log::register_simple_formatter_factory<Severity, char>("Severity");
// boost::log::register_formatter_factory("TimeStamp", boost::make_shared<timestamp_formatter_factory>());
boost::log::add_common_attributes();
boost::log::add_file_log
(
boost::log::keywords::file_name = "appname_%N.log",
boost::log::keywords::rotation_size = 10 * 1024 * 1024,
boost::log::keywords::time_based_rotation = boost::log::sinks::file::rotation_at_time_point(0, 0, 0),
boost::log::keywords::format =
boost::log::expressions::stream
<< boost::log::expressions::attr<unsigned>("LineID") << " "
<< "[" << boost::log::expressions::format_date_time<boost::posix_time::ptime>("TimeStamp", "%Y-%m-%d %H:%M:%S.%f%Q"<< "]" << " "
<< "<" << boost::log::expressions::attr<Severity>("Severity") << _NSTR(">") << _NSTR(" ")
<< boost::log::expressions::smessage
// "%LineID% [%TimeStamp(format=\"%Y-%m-%d %H:%M:%S.%f%Q\")%] <%Severity%>: %%Message%"
);
const auto severity = boost::log::expressions::attr<Severity>("Severity");
boost::log::core::get()->set_filter
(
severity >= severityThreshold_
);
}
Any suggestions on what I might be doing wrong?
Both utc_clock and local_clock produce values of type boost::posix_time::ptime, which do not have information of a time zone. The difference between the clocks is what time ptime represents - UTC or local time according to the time zone set on the machine. The formatter has no use for %Q and %q and replaces them with an empty string.
The time zone is present in the boost::local_time::local_date_time type, the %Q and %q placeholders will work for it. The library does not have a clock attribute that produces local_date_time, so you will have to write one yourself. See here for an example.
I'm trying to insert a string-variable into a varchar(100)-field, but if the string is longer than 15 elements only junk is inserted (e.g. "0‰?").
First my setup:
Development: Win7 (64bit) / VS2013 / C++11 / 64bit Application
Database: Win8 (64bit) / Microsoft SQL Server Express 2014 (64bit)
Driver: SQL Server Native Client 11.0
Second the binding of the paramter:
std::string mMessageText;
SQLHANDLE mSqlStatementHandle;
std::string mExecString;
bool initConnection()
{
mExecString = "{? = CALL dbo.InsertTestProcedure(?, ?, ?, ?, ?)}";
(...)
// bind parameters
SQLBindParameter(mSqlStatementHandle, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_LONGVARCHAR, 100, 0, (SQLPOINTER)mMessageText.c_str(), mMessageText.length(), NULL);
(...)
// prepare handle with execution string
if (SQL_SUCCESS != SQLPrepare(mSqlStatementHandle, (SQLCHAR*)mExecString.c_str(), (SQLSMALLINT)mExecString.length()))
{
throwError(SQL_HANDLE_STMT, mSqlStatementHandle);
return false;
}
}
Third the query execution:
bool fillDb()
{
(...)
mMessageText = "This text is longer than 15";
// execute SQL statement
if (SQL_SUCCESS != SQLExecute(mSqlStatementHandle))
{
throwError(SQL_HANDLE_STMT, mSqlStatementHandle);
return false;
}
(...)
}
Header of the procedure:
ALTER PROCEDURE [dbo].[InsertTestProcedure]
#MessageComp VARCHAR(20),
#MessageType VARCHAR(20),
#MessageAction VARCHAR(20),
#MessageText VARCHAR(100),
#MessageName VARCHAR(20)
AS
If the string is shorter than 15 elements, it works fine. And calling the procedure from SQL Management Studio with value lengths > 15 works fine too.
One thing that comes to my mind is the procedure you are calling. Maybe you have table with varchar(100) column, but the procedure has only varchar(15) parameter. Could you post header of that procedure?
Thanks to #erg, here is the solution that worked for me:
char mMessageText[100];
bool initConnection()
{
(...)
// bind parameters
SQLBindParameter(mSqlStatementHandle, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_LONGVARCHAR, 100, 0, (SQLPOINTER)mMessageText, 100, NULL);
(...)
}
bool fillDb()
{
(...)
std::string lMessageText = "This text is longer than 15";
strcpy(mMessageText, lMessageText.c_str());
mMessageText[sizeof(mMessageText) - 1] = 0;
(...)
}
I have a requirement to create a custom timezone (New York + 7 hours with US DST setting) and parse/format date using this.
In Java I would typically do this:
TimeZone tz = TimeZone.getTimeZone("America/New_York"); // Get new york tz
tz.setRawOffset(tz.getRawOffset() + 7 * 3600 * 1000); // add 7 hrs
DateFormat nyp7 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
nyp7.setTimeZone(tz);
DateFormat utc = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
utc.setTimeZone(TimeZone.getTimeZone("UTC"));
// US DST not in effect, NY is UTC-5, NY+7 is UTC+2
// below result is "2013-03-01 14:34:55"
nyp7.format(utc.parse("2013-03-01 12:34:55"));
// US DST in effect, NY is UTC-4, NY+7 is UTC+3
// below result is "2013-04-01 15:34:55"
nyp7.format(utc.parse("2013-04-01 12:34:55"));
How to do equivalent in C++? I've been digging boost date time library but I'm lost.
Found the solution by creating a custom timezone:
using namespace boost;
using namespace local_time;
using namespace gregorian;
using posix_time::time_duration;
using posix_time::ptime;
// Create a new timezone names
time_zone_names tzn("New York Plus 7 Time", "NYP7",
"New York Plus 7 Daylight Time", "NYP7D");
// With base UTC offset +2
int base_utc_offset_hr = 2;
time_duration utc_offset(base_utc_offset_hr,0,0);
// DST offset is +1 and starts and ends at 9am (2am + 7)
local_time::dst_adjustment_offsets adj_offsets(time_duration(1,0,0),
time_duration(9,0,0),
time_duration(9,0,0));
// DST starts the second Sunday of March and ends last Sunday of Nov
nth_kday_of_month start_rule(nth_kday_of_month::second, Sunday, Mar);
last_day_of_the_week_in_month end_rule(Sunday, Nov);
shared_ptr<dst_calc_rule> nyp7_rules(new nth_last_dst_rule(start_rule, end_rule));
// Construct the custom timezone
time_zone_ptr nyp7(new custom_time_zone(tzn, utc_offset, adj_offsets, nyp7_rules));
This can then be used to detect whether or not DST is in effect, and offset it into the source time
// Determine whether or not dst in effect for time 't'
ptime dst_start = nyp7->dst_local_start_time(year);
ptime dst_end = nyp7->dst_local_end_time(year);
bool dst_on = dst_start <= t && t < dst_end;
And subsequently the parsing and formatting can be done according to boost's user manual:
http://www.boost.org/doc/libs/1_51_0/doc/html/date_time/date_time_io.html
I'm using pqlib with postgresql version 9.1.11
I have the following code
const char *spid = std::to_string(pid).c_str();
PGresult *res;
const char *paramValues[2] = {u->getID().c_str(), spid};
std::string table;
table = table.append("public.\"").append(Constants::USER_PATTERNS_TABLE).append("\"");
std::string param_name_pid = Constants::RELATION_TABLE_PATTERN_ID;
std::string param_name_uid = Constants::RELATION_TABLE_USER_ID;
std::string command = Constants::INSERT_COMMAND + table + " (" + param_name_uid + ", " + param_name_pid + ") VALUES ($1, $2::int)";
std::cout << "command: " << command << std::endl;
res = PQexecParams(conn, command.c_str(), 2, NULL, paramValues, NULL, NULL,0);
Where
INSERT_COMMAND = "INSERT INTO " (string)
USER_PATTERN_TABLE = "User_Patterns" (string)
RELATION_TABLE_PATTERN_ID = "pattern_id" (string)
RELATION_TABLE_USER_ID = "user_id" (string)
pid = an int
u->getID() = a string
conn = the connection to the db
The table "User_Patterns" is defined as
CREATE TABLE "User_Patterns"(
user_id TEXT references public."User" (id) ON UPDATE CASCADE ON DELETE CASCADE
,pattern_id BIGSERIAL references public."Pattern" (id) ON UPDATE CASCADE
,CONSTRAINT user_patterns_pkey PRIMARY KEY (user_id,pattern_id) -- explicit pk
)WITH (
OIDS=FALSE
);
I already have a user and a pattern loaded into their respective tables.
The command generated is :
INSERT INTO public."User_Patterns" (user_id, pattern_id) VALUES ($1, $2::int)
I also tried with $2, $2::bigint, $2::int4
The problem is:
I receive the error :
ERROR: invalid input syntax for integer: "public.""
I already use PQexecParams to store users and patterns, the only difference is that they all have text/xml fields (the only int field on patterns is a serial one and I don't store that value myself) but because the user_patterns is a relation table I need to store and int for the pattern_id.
I already read the docs for pqlib and saw the examples, both are useless.
The problem is in the lines:
const char *spid = std::to_string(pid).c_str();
const char *paramValues[2] = {u->getID().c_str(), spid};
std::to_string(pid) creates temporary string and .c_str() returns a pointer to an internal representation of this string, which is destroyed at the end of the line, resulting in a dead pointer. You may also see answer to the question
stringstream::str copy lifetime