summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/sql/drivers/psql/qsql_psql.cpp43
-rw-r--r--tests/auto/sql/kernel/qsqldatabase/tst_databases.h2
-rw-r--r--tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp42
3 files changed, 60 insertions, 27 deletions
diff --git a/src/sql/drivers/psql/qsql_psql.cpp b/src/sql/drivers/psql/qsql_psql.cpp
index e76dcc26a0..4268ea06f6 100644
--- a/src/sql/drivers/psql/qsql_psql.cpp
+++ b/src/sql/drivers/psql/qsql_psql.cpp
@@ -426,11 +426,8 @@ QVariant QPSQLResult::data(int i)
#ifndef QT_NO_DATESTRING
if (str.isEmpty())
return QVariant(QTime());
- if (str.at(str.length() - 3) == QLatin1Char('+') || str.at(str.length() - 3) == QLatin1Char('-'))
- // strip the timezone
- // TODO: fix this when timestamp support comes into QDateTime
- return QVariant(QTime::fromString(str.left(str.length() - 3), Qt::ISODate));
- return QVariant(QTime::fromString(str, Qt::ISODate));
+ else
+ return QVariant(QTime::fromString(str, Qt::ISODate));
#else
return QVariant(str);
#endif
@@ -438,19 +435,13 @@ QVariant QPSQLResult::data(int i)
case QVariant::DateTime: {
QString dtval = QString::fromLatin1(val);
#ifndef QT_NO_DATESTRING
- if (dtval.length() < 10)
- return QVariant(QDateTime());
- // remove the timezone
- // TODO: fix this when timestamp support comes into QDateTime
- if (dtval.at(dtval.length() - 3) == QLatin1Char('+') || dtval.at(dtval.length() - 3) == QLatin1Char('-'))
- dtval.chop(3);
- // milliseconds are sometimes returned with 2 digits only
- if (dtval.at(dtval.length() - 3).isPunct())
- dtval += QLatin1Char('0');
- if (dtval.isEmpty())
+ if (dtval.length() < 10) {
return QVariant(QDateTime());
- else
- return QVariant(QDateTime::fromString(dtval, Qt::ISODate));
+ } else {
+ QChar sign = dtval[dtval.size() - 3];
+ if (sign == QLatin1Char('-') || sign == QLatin1Char('+')) dtval += QLatin1String(":00");
+ return QVariant(QDateTime::fromString(dtval, Qt::ISODate).toLocalTime());
+ }
#else
return QVariant(dtval);
#endif
@@ -536,6 +527,11 @@ QSqlRecord QPSQLResult::record() const
int precision = PQfmod(d->result, i);
switch (ptype) {
+ case QTIMESTAMPOID:
+ case QTIMESTAMPTZOID:
+ precision = 3;
+ break;
+
case QNUMERICOID:
if (precision != -1) {
len = (precision >> 16);
@@ -1263,15 +1259,10 @@ QString QPSQLDriver::formatValue(const QSqlField &field, bool trimStrings) const
case QVariant::DateTime:
#ifndef QT_NO_DATESTRING
if (field.value().toDateTime().isValid()) {
- QDate dt = field.value().toDateTime().date();
- QTime tm = field.value().toDateTime().time();
- // msecs need to be right aligned otherwise psql interprets them wrong
- r = QLatin1Char('\'') + QString::number(dt.year()) + QLatin1Char('-')
- + QString::number(dt.month()).rightJustified(2, QLatin1Char('0')) + QLatin1Char('-')
- + QString::number(dt.day()).rightJustified(2, QLatin1Char('0')) + QLatin1Char(' ')
- + tm.toString() + QLatin1Char('.')
- + QString::number(tm.msec()).rightJustified(3, QLatin1Char('0'))
- + QLatin1Char('\'');
+ // we force the value to be considered with a timezone information, and we force it to be UTC
+ // this is safe since postgresql stores only the UTC value and not the timezone offset (only used
+ // while parsing), so we have correct behavior in both case of with timezone and without tz
+ r = QLatin1String("TIMESTAMP WITH TIME ZONE ") + QLatin1Char('\'') + field.value().toDateTime().toUTC().toString(QLatin1String("yyyy-MM-ddThh:mm:ss.zzz")) + QLatin1Char('Z') + QLatin1Char('\'');
} else {
r = QLatin1String("NULL");
}
diff --git a/tests/auto/sql/kernel/qsqldatabase/tst_databases.h b/tests/auto/sql/kernel/qsqldatabase/tst_databases.h
index af6dc6b9f4..8b080324d5 100644
--- a/tests/auto/sql/kernel/qsqldatabase/tst_databases.h
+++ b/tests/auto/sql/kernel/qsqldatabase/tst_databases.h
@@ -493,7 +493,7 @@ public:
{
const QSqlDriver::DbmsType dbType = tst_Databases::getDatabaseType(db);
if (dbType == QSqlDriver::PostgreSQL)
- return QLatin1String("timestamp");
+ return QLatin1String("timestamptz");
if (dbType == QSqlDriver::Oracle && getOraVersion(db) >= 9)
return QLatin1String("timestamp(0)");
return QLatin1String("datetime");
diff --git a/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp b/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp
index f5d71a75cf..1943b6ec92 100644
--- a/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp
+++ b/tests/auto/sql/kernel/qsqlquery/tst_qsqlquery.cpp
@@ -224,6 +224,9 @@ private slots:
void QTBUG_2192_data() { generic_data(); }
void QTBUG_2192();
+ void QTBUG_36211_data() { generic_data("QPSQL"); }
+ void QTBUG_36211();
+
void sqlite_constraint_data() { generic_data("QSQLITE"); }
void sqlite_constraint();
@@ -3558,6 +3561,45 @@ void tst_QSqlQuery::QTBUG_2192()
}
}
+void tst_QSqlQuery::QTBUG_36211()
+{
+ QFETCH( QString, dbName );
+ QSqlDatabase db = QSqlDatabase::database( dbName );
+ CHECK_DATABASE( db );
+ if (tst_Databases::getDatabaseType(db) == QSqlDriver::PostgreSQL) {
+ const QString tableName(qTableName("bug36211", __FILE__, db));
+ tst_Databases::safeDropTable( db, tableName );
+
+ QSqlQuery q(db);
+ QVERIFY_SQL(q, exec(QString("CREATE TABLE %1 (dtwtz timestamptz, dtwotz timestamp)").arg(tableName)));
+
+ QTimeZone l_tzBrazil("BRT");
+ QTimeZone l_tzChina("CST");
+ QDateTime dt = QDateTime(QDate(2014, 10, 30), QTime(14, 12, 02, 357));
+ QVERIFY_SQL(q, prepare("INSERT INTO " + tableName + " (dtwtz, dtwotz) VALUES (:dt, :dt)"));
+ q.bindValue(":dt", dt);
+ QVERIFY_SQL(q, exec());
+ q.bindValue(":dt", dt.toTimeZone(l_tzBrazil));
+ QVERIFY_SQL(q, exec());
+ q.bindValue(":dt", dt.toTimeZone(l_tzChina));
+ QVERIFY_SQL(q, exec());
+
+ QVERIFY_SQL(q, exec("SELECT dtwtz, dtwotz FROM " + tableName));
+
+ for (int i = 0; i < 3; ++i) {
+ QVERIFY_SQL(q, next());
+
+ for (int j = 0; j < 2; ++j) {
+ // Check if retrieved value preserves reported precision
+ int precision = qMax(0, q.record().field(j).precision());
+ int diff = qAbs(q.value(j).toDateTime().msecsTo(dt));
+ int keep = qMin(1000, (int)qPow(10.0, precision));
+ QVERIFY(diff <= 1000 - keep);
+ }
+ }
+ }
+}
+
void tst_QSqlQuery::oraOCINumber()
{
QFETCH( QString, dbName );