Improve the QDateTime support
This commit is contained in:
+1
-2
@@ -44,9 +44,8 @@ public:
|
|||||||
String,
|
String,
|
||||||
Number,
|
Number,
|
||||||
Formula,
|
Formula,
|
||||||
ArrayFormula,
|
|
||||||
Boolean,
|
Boolean,
|
||||||
DateTime
|
Error
|
||||||
};
|
};
|
||||||
|
|
||||||
DataType dataType() const;
|
DataType dataType() const;
|
||||||
|
|||||||
@@ -30,6 +30,8 @@
|
|||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
namespace QXlsx {
|
namespace QXlsx {
|
||||||
|
|
||||||
@@ -62,6 +64,30 @@ QColor fromARGBString(const QString &c)
|
|||||||
return color;
|
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)
|
QPoint xl_cell_to_rowcol(const QString &cell_str)
|
||||||
{
|
{
|
||||||
if (cell_str.isEmpty())
|
if (cell_str.isEmpty())
|
||||||
|
|||||||
@@ -30,12 +30,15 @@ class QPoint;
|
|||||||
class QString;
|
class QString;
|
||||||
class QStringList;
|
class QStringList;
|
||||||
class QColor;
|
class QColor;
|
||||||
|
class QDateTime;
|
||||||
|
|
||||||
namespace QXlsx {
|
namespace QXlsx {
|
||||||
|
|
||||||
XLSX_AUTOTEST_EXPORT int intPow(int x, int p);
|
XLSX_AUTOTEST_EXPORT int intPow(int x, int p);
|
||||||
XLSX_AUTOTEST_EXPORT QStringList splitPath(const QString &path);
|
XLSX_AUTOTEST_EXPORT QStringList splitPath(const QString &path);
|
||||||
XLSX_AUTOTEST_EXPORT QColor fromARGBString(const QString &c);
|
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 QPoint xl_cell_to_rowcol(const QString &cell_str);
|
||||||
XLSX_AUTOTEST_EXPORT QString xl_col_to_name(int col_num);
|
XLSX_AUTOTEST_EXPORT QString xl_col_to_name(int col_num);
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ WorkbookPrivate::WorkbookPrivate(Workbook *q) :
|
|||||||
|
|
||||||
strings_to_numbers_enabled = false;
|
strings_to_numbers_enabled = false;
|
||||||
date1904 = false;
|
date1904 = false;
|
||||||
defaultDateFormat = QStringLiteral("dd/mm/yyyy hh:mm");
|
defaultDateFormat = QStringLiteral("yyyy-mm-ddThh:mm:ss");
|
||||||
activesheet = 0;
|
activesheet = 0;
|
||||||
firstsheet = 0;
|
firstsheet = 0;
|
||||||
table_count = 0;
|
table_count = 0;
|
||||||
@@ -74,12 +74,15 @@ bool Workbook::isDate1904() const
|
|||||||
return d->date1904;
|
return d->date1904;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*!
|
||||||
Excel for Windows uses a default epoch of 1900 and Excel
|
Excel for Windows uses a default epoch of 1900 and Excel
|
||||||
for Mac uses an epoch of 1904. However, Excel on either
|
for Mac uses an epoch of 1904. However, Excel on either
|
||||||
platform will convert automatically between one system
|
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.
|
by default.
|
||||||
|
|
||||||
|
\note This function should be called before any date/time
|
||||||
|
has been written.
|
||||||
*/
|
*/
|
||||||
void Workbook::setDate1904(bool date1904)
|
void Workbook::setDate1904(bool date1904)
|
||||||
{
|
{
|
||||||
@@ -310,7 +313,7 @@ QByteArray Workbook::saveToXmlData()
|
|||||||
|
|
||||||
QSharedPointer<Workbook> Workbook::loadFromXmlFile(QIODevice *device)
|
QSharedPointer<Workbook> Workbook::loadFromXmlFile(QIODevice *device)
|
||||||
{
|
{
|
||||||
Workbook *book = new Workbook;
|
QSharedPointer<Workbook> book(new Workbook);
|
||||||
|
|
||||||
XmlStreamReader reader(device);
|
XmlStreamReader reader(device);
|
||||||
while(!reader.atEnd()) {
|
while(!reader.atEnd()) {
|
||||||
@@ -321,10 +324,14 @@ QSharedPointer<Workbook> Workbook::loadFromXmlFile(QIODevice *device)
|
|||||||
QString sheetName = attributes.value(QLatin1String("name")).toString();
|
QString sheetName = attributes.value(QLatin1String("name")).toString();
|
||||||
QString rId = attributes.value(QLatin1String("r:id")).toString();
|
QString rId = attributes.value(QLatin1String("r:id")).toString();
|
||||||
book->d_func()->sheetNameIdPairList.append(QPair<QString, QString>(sheetName, rId));
|
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)
|
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 = d->workbook->createFormat();
|
||||||
format->setNumberFormat(d->workbook->defaultDateFormat());
|
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);
|
d->workbook->styles()->addFormat(format);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -729,23 +732,11 @@ void WorksheetPrivate::writeCellData(XmlStreamWriter &writer, int row, int col,
|
|||||||
writer.writeAttribute(QStringLiteral("t"), QStringLiteral("str"));
|
writer.writeAttribute(QStringLiteral("t"), QStringLiteral("str"));
|
||||||
writer.writeTextElement(QStringLiteral("f"), cell->formula());
|
writer.writeTextElement(QStringLiteral("f"), cell->formula());
|
||||||
writer.writeTextElement(QStringLiteral("v"), cell->value().toString());
|
writer.writeTextElement(QStringLiteral("v"), cell->value().toString());
|
||||||
} else if (cell->dataType() == Cell::ArrayFormula) {
|
|
||||||
|
|
||||||
} else if (cell->dataType() == Cell::Boolean) {
|
} else if (cell->dataType() == Cell::Boolean) {
|
||||||
writer.writeAttribute(QStringLiteral("t"), QStringLiteral("b"));
|
writer.writeAttribute(QStringLiteral("t"), QStringLiteral("b"));
|
||||||
writer.writeTextElement(QStringLiteral("v"), cell->value().toBool() ? QStringLiteral("1") : QStringLiteral("0"));
|
writer.writeTextElement(QStringLiteral("v"), cell->value().toBool() ? QStringLiteral("1") : QStringLiteral("0"));
|
||||||
} else if (cell->dataType() == Cell::Blank) {
|
} else if (cell->dataType() == Cell::Blank) {
|
||||||
//Ok, empty here.
|
//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
|
writer.writeEndElement(); //c
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include "private/xlsxutility_p.h"
|
#include "private/xlsxutility_p.h"
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QtTest>
|
#include <QtTest>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
class UtilityTest : public QObject
|
class UtilityTest : public QObject
|
||||||
{
|
{
|
||||||
@@ -39,6 +40,12 @@ private Q_SLOTS:
|
|||||||
|
|
||||||
void test_rowcol_to_cell();
|
void test_rowcol_to_cell();
|
||||||
void test_rowcol_to_cell_data();
|
void test_rowcol_to_cell_data();
|
||||||
|
|
||||||
|
void test_datetimeToNumber_data();
|
||||||
|
void test_datetimeToNumber();
|
||||||
|
|
||||||
|
void test_datetimeFromNumber_data();
|
||||||
|
void test_datetimeFromNumber();
|
||||||
};
|
};
|
||||||
|
|
||||||
UtilityTest::UtilityTest()
|
UtilityTest::UtilityTest()
|
||||||
@@ -101,6 +108,55 @@ void UtilityTest::test_rowcol_to_cell_data()
|
|||||||
QTest::newRow("...") << 1048576 << 16384 << false << false << "XFE1048577";
|
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)
|
QTEST_APPLESS_MAIN(UtilityTest)
|
||||||
|
|
||||||
#include "tst_utilitytest.moc"
|
#include "tst_utilitytest.moc"
|
||||||
|
|||||||
Reference in New Issue
Block a user