Browse Source

Improve the QDateTime support

master
Debao Zhang 11 years ago
parent
commit
afa8c959e8
  1. 3
      src/xlsx/xlsxcell.h
  2. 26
      src/xlsx/xlsxutility.cpp
  3. 3
      src/xlsx/xlsxutility_p.h
  4. 17
      src/xlsx/xlsxworkbook.cpp
  5. 17
      src/xlsx/xlsxworksheet.cpp
  6. 56
      tests/auto/utility/tst_utilitytest.cpp

3
src/xlsx/xlsxcell.h

@ -44,9 +44,8 @@ public:
String,
Number,
Formula,
ArrayFormula,
Boolean,
DateTime
Error
};
DataType dataType() const;

26
src/xlsx/xlsxutility.cpp

@ -30,6 +30,8 @@
#include <QMap>
#include <QStringList>
#include <QColor>
#include <QDateTime>
#include <QDebug>
namespace QXlsx {
@ -62,6 +64,30 @@ QColor fromARGBString(const QString &c)
return color;
}
double datetimeToNumber(const QDateTime &dt, bool is1904)
{
//Note, for number 0, Excel2007 shown as 1900-1-0, which should be 1899-12-31
QDateTime epoch(is1904 ? QDate(1904, 1, 1): QDate(1899, 12, 31), QTime(0,0), Qt::UTC);
double excel_time = epoch.msecsTo(dt) / (1000*60*60*24.0);
if (!is1904 && excel_time > 59) {//31+28
//Account for Excel erroneously treating 1900 as a leap year.
excel_time += 1;
}
return excel_time;
}
QDateTime datetimeFromNumber(double num, bool is1904)
{
if (!is1904 && num > 60)
num = num - 1;
qint64 msecs = static_cast<qint64>(num * 1000*60*60*24.0);
QDateTime epoch(is1904 ? QDate(1904, 1, 1): QDate(1899, 12, 31), QTime(0,0), Qt::UTC);
return epoch.addMSecs(msecs);
}
QPoint xl_cell_to_rowcol(const QString &cell_str)
{
if (cell_str.isEmpty())

3
src/xlsx/xlsxutility_p.h

@ -30,12 +30,15 @@ class QPoint;
class QString;
class QStringList;
class QColor;
class QDateTime;
namespace QXlsx {
XLSX_AUTOTEST_EXPORT int intPow(int x, int p);
XLSX_AUTOTEST_EXPORT QStringList splitPath(const QString &path);
XLSX_AUTOTEST_EXPORT QColor fromARGBString(const QString &c);
XLSX_AUTOTEST_EXPORT double datetimeToNumber(const QDateTime &dt, bool is1904=false);
XLSX_AUTOTEST_EXPORT QDateTime datetimeFromNumber(double num, bool is1904=false);
XLSX_AUTOTEST_EXPORT QPoint xl_cell_to_rowcol(const QString &cell_str);
XLSX_AUTOTEST_EXPORT QString xl_col_to_name(int col_num);

17
src/xlsx/xlsxworkbook.cpp

@ -51,7 +51,7 @@ WorkbookPrivate::WorkbookPrivate(Workbook *q) :
strings_to_numbers_enabled = false;
date1904 = false;
defaultDateFormat = QStringLiteral("dd/mm/yyyy hh:mm");
defaultDateFormat = QStringLiteral("yyyy-mm-ddThh:mm:ss");
activesheet = 0;
firstsheet = 0;
table_count = 0;
@ -74,12 +74,15 @@ bool Workbook::isDate1904() const
return d->date1904;
}
/*
/*!
Excel for Windows uses a default epoch of 1900 and Excel
for Mac uses an epoch of 1904. However, Excel on either
platform will convert automatically between one system
and the other. QtXlsxWriter stores dates in the 1900 format
and the other. Qt Xlsx stores dates in the 1900 format
by default.
\note This function should be called before any date/time
has been written.
*/
void Workbook::setDate1904(bool date1904)
{
@ -310,7 +313,7 @@ QByteArray Workbook::saveToXmlData()
QSharedPointer<Workbook> Workbook::loadFromXmlFile(QIODevice *device)
{
Workbook *book = new Workbook;
QSharedPointer<Workbook> book(new Workbook);
XmlStreamReader reader(device);
while(!reader.atEnd()) {
@ -321,10 +324,14 @@ QSharedPointer<Workbook> Workbook::loadFromXmlFile(QIODevice *device)
QString sheetName = attributes.value(QLatin1String("name")).toString();
QString rId = attributes.value(QLatin1String("r:id")).toString();
book->d_func()->sheetNameIdPairList.append(QPair<QString, QString>(sheetName, rId));
} else if (reader.name() == QLatin1String("workbookPr")) {
QXmlStreamAttributes attrs = reader.attributes();
if (attrs.hasAttribute(QLatin1String("date1904")))
book->d_ptr->date1904 = true;
}
}
}
return QSharedPointer<Workbook>(book);
return book;
}
QSharedPointer<Workbook> Workbook::loadFromXmlData(const QByteArray &data)

17
src/xlsx/xlsxworksheet.cpp

@ -423,7 +423,10 @@ int Worksheet::writeDateTime(int row, int column, const QDateTime &dt, Format *f
format = d->workbook->createFormat();
format->setNumberFormat(d->workbook->defaultDateFormat());
}
d->cellTable[row][column] = QSharedPointer<Cell>(new Cell(dt, Cell::DateTime, format));
double value = datetimeToNumber(dt, d->workbook->isDate1904());
d->cellTable[row][column] = QSharedPointer<Cell>(new Cell(value, Cell::Number, format));
d->workbook->styles()->addFormat(format);
return 0;
@ -729,23 +732,11 @@ void WorksheetPrivate::writeCellData(XmlStreamWriter &writer, int row, int col,
writer.writeAttribute(QStringLiteral("t"), QStringLiteral("str"));
writer.writeTextElement(QStringLiteral("f"), cell->formula());
writer.writeTextElement(QStringLiteral("v"), cell->value().toString());
} else if (cell->dataType() == Cell::ArrayFormula) {
} else if (cell->dataType() == Cell::Boolean) {
writer.writeAttribute(QStringLiteral("t"), QStringLiteral("b"));
writer.writeTextElement(QStringLiteral("v"), cell->value().toBool() ? QStringLiteral("1") : QStringLiteral("0"));
} else if (cell->dataType() == Cell::Blank) {
//Ok, empty here.
} else if (cell->dataType() == Cell::DateTime) {
QDateTime epoch(QDate(1899, 12, 31));
if (workbook->isDate1904())
epoch = QDateTime(QDate(1904, 1, 1));
qint64 delta = epoch.msecsTo(cell->value().toDateTime());
double excel_time = delta / (1000*60*60*24);
//Account for Excel erroneously treating 1900 as a leap year.
if (!workbook->isDate1904() && excel_time > 59)
excel_time += 1;
writer.writeTextElement(QStringLiteral("v"), QString::number(excel_time, 'g', 15));
}
writer.writeEndElement(); //c
}

56
tests/auto/utility/tst_utilitytest.cpp

@ -25,6 +25,7 @@
#include "private/xlsxutility_p.h"
#include <QString>
#include <QtTest>
#include <QDateTime>
class UtilityTest : public QObject
{
@ -39,6 +40,12 @@ private Q_SLOTS:
void test_rowcol_to_cell();
void test_rowcol_to_cell_data();
void test_datetimeToNumber_data();
void test_datetimeToNumber();
void test_datetimeFromNumber_data();
void test_datetimeFromNumber();
};
UtilityTest::UtilityTest()
@ -101,6 +108,55 @@ void UtilityTest::test_rowcol_to_cell_data()
QTest::newRow("...") << 1048576 << 16384 << false << false << "XFE1048577";
}
void UtilityTest::test_datetimeToNumber_data()
{
QTest::addColumn<QDateTime>("dt");
QTest::addColumn<bool>("is1904");
QTest::addColumn<double>("num");
//Note, for number 0, Excel2007 shown as 1900-1-0, which should be 1899-12-31
QTest::newRow("0") << QDateTime(QDate(1899, 12, 31), QTime(0,0), Qt::UTC) << false << 0.0;
QTest::newRow("1.25") << QDateTime(QDate(1900, 1, 1), QTime(6, 0), Qt::UTC) << false << 1.25;
QTest::newRow("59") << QDateTime(QDate(1900, 2, 28), QTime(0, 0), Qt::UTC) << false << 59.0;
QTest::newRow("61") << QDateTime(QDate(1900, 3, 1), QTime(0, 0), Qt::UTC) << false << 61.0;
QTest::newRow("1904: 0") << QDateTime(QDate(1904, 1, 1), QTime(0,0), Qt::UTC) << true << 0.0;
QTest::newRow("1904: 1.25") << QDateTime(QDate(1904, 1, 2), QTime(6, 0), Qt::UTC) << true << 1.25;
}
void UtilityTest::test_datetimeToNumber()
{
QFETCH(QDateTime, dt);
QFETCH(bool, is1904);
QFETCH(double, num);
QCOMPARE(QXlsx::datetimeToNumber(dt, is1904), num);
}
void UtilityTest::test_datetimeFromNumber_data()
{
QTest::addColumn<QDateTime>("dt");
QTest::addColumn<bool>("is1904");
QTest::addColumn<double>("num");
QTest::newRow("0") << QDateTime(QDate(1899, 12, 31), QTime(0,0), Qt::UTC) << false << 0.0;
QTest::newRow("1.25") << QDateTime(QDate(1900, 1, 1), QTime(6, 0), Qt::UTC) << false << 1.25;
QTest::newRow("59") << QDateTime(QDate(1900, 2, 28), QTime(0,0), Qt::UTC) << false << 59.0;
QTest::newRow("61") << QDateTime(QDate(1900, 3, 1), QTime(0,0), Qt::UTC) << false << 61.0;
QTest::newRow("1904: 0") << QDateTime(QDate(1904, 1, 1), QTime(0,0), Qt::UTC) << true << 0.0;
QTest::newRow("1904: 1.25") << QDateTime(QDate(1904, 1, 2), QTime(6, 0), Qt::UTC) << true << 1.25;
}
void UtilityTest::test_datetimeFromNumber()
{
QFETCH(QDateTime, dt);
QFETCH(bool, is1904);
QFETCH(double, num);
QCOMPARE(QXlsx::datetimeFromNumber(num, is1904), dt);
}
QTEST_APPLESS_MAIN(UtilityTest)
#include "tst_utilitytest.moc"

Loading…
Cancel
Save