Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-128493

QIBASE Driver: Timestamps with timezones are not stored as UTC

    XMLWordPrintable

Details

    • Bug
    • Resolution: Invalid
    • P2: Important
    • None
    • 6.7.2, 6.8.0
    • SQL Support
    • None
    • All
    • 8d8805214 (dev), b0d73b947 (6.8), 4be1b3666 (6.7)

    Description

      The firebird docu says:

      Storage of Time Zone Types
      The time zone types are stored as values at UTC (offset 0), using the structure of TIME or TIMESTAMP + two extra bytes for time zone information (either an offset in minutes, or the id of a named time zone). Storing as UTC allows Firebird to index and compare two values in different time zones.

      But the two functions (fromTimeStampTz, toTimeStampTz) in qsql_ibase.cpp doesn't respect this:

      QDateTime fromTimeStampTz(char *buffer)
      {
      ...
          if (!timeZoneName.isEmpty())
              return QDateTime(d, t, QTimeZone(timeZoneName)); // should convert from UTC here
          else
              return {};
      ...
      }
      
      ISC_TIMESTAMP_TZ toTimeStampTz(const QDateTime &dt)
      {
          ...
          // dt is local time, but should be converted to UTC
          ts.utc_timestamp.timestamp_time = midnight.msecsTo(dt.time()) * 10;
          ts.utc_timestamp.timestamp_date = basedate.daysTo(dt.date());
          ts.time_zone = qIanaIdToFbTzIdMap()->value(dt.timeZone().id().simplified(), 0);
          ...
      }
      

      Howto prove the bug:

          auto db = QSqlDatabase::addDatabase("QIBASE", "test");
          db.setDatabaseName("employee");
          db.setHostName("localhost");
          db.setPort(3050);
          db.setUserName("SYSDBA");
          db.setPassword("masterkey");
          QVERIFY(db.open());
          const auto now = QDateTime::currentDateTime();
      
          QSqlQuery qry{db};
          QVERIFY2(qry.exec(u"select current_timestamp from rdb$database"_s),
                   qPrintable(getErrorMessage(qry.lastError())));
          QVERIFY2(qry.isActive(), qPrintable(getErrorMessage(qry.lastError())));
          QVERIFY2(qry.next(), qPrintable(getErrorMessage(qry.lastError())));
          const auto then = qry.value(0).toDateTime();
          QCOMPARE(now.date(), then.date());
          QCOMPARE(now.offsetFromUtc(), then.offsetFromUtc());
          QCOMPARE(now.isDaylightTime(), then.isDaylightTime());
      
          // this reveals the bug
          QCOMPARE(now.time().hour(), then.time().hour());
      

      How to fix it:

      QDateTime fromTimeStampTz(char *buffer)
      {
      ...
          if (!timeZoneName.isEmpty())
          {
              // stored timestamp is in UTC
              const auto utc = QDateTime(d, t, Qt::UTC);
              return utc.toTimeZone(QTimeZone(timeZoneName));
          }
          else
              return {};
      ...
      }
      
      ISC_TIMESTAMP_TZ toTimeStampTz(const QDateTime &dt)
      {
          ...
          // convert to UTC before storing
          const auto dtUtc = dt.toUTC(); 
          ISC_TIMESTAMP_TZ ts;
          ts.utc_timestamp.timestamp_time = midnight.msecsTo(dtUtc.time()) * 10;
          ts.utc_timestamp.timestamp_date = basedate.daysTo(dtUtc.date());
          ts.time_zone = qIanaIdToFbTzIdMap()->value(dt.timeZone().id().simplified(), 0);
          ...
      }
      

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            chehrlic Christian Ehrlicher
            jonjonas681 Johann Anhofer
            Votes:
            1 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: