Improve the QDateTime support
This commit is contained in:
+1
-2
@@ -44,9 +44,8 @@ public:
|
||||
String,
|
||||
Number,
|
||||
Formula,
|
||||
ArrayFormula,
|
||||
Boolean,
|
||||
DateTime
|
||||
Error
|
||||
};
|
||||
|
||||
DataType dataType() const;
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
by default.
|
||||
/*!
|
||||
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. 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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user