tdf#139809 sc ooxml export autofiltered date columns
Export XML_dateGroupItem, XML_year, XML_month, XML_day,
XML_dateTimeGrouping based on the OOXML standard.
Follow up commit of: d101a6f8579df8241dcd8100908868cd7d7c3f8a
(tdf#116818 sc OOXML: import autofiltered date columns)
Note: DOCX round-trip is not lossless here, because
instead of exporting the original yearly and monthly
dataGroupItem filtering, the export contains daily level
dataGroupItem elements for each day.
Change-Id: I456b2240213839725e2b416622986ee4a9cf60d5
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/109699
Tested-by: László Németh <nemeth@numbertext.org>
Reviewed-by: László Németh <nemeth@numbertext.org>
diff --git a/sc/qa/unit/data/xlsx/dateAutofilter.xlsx b/sc/qa/unit/data/xlsx/dateAutofilter.xlsx
new file mode 100644
index 0000000..1c83651
--- /dev/null
+++ b/sc/qa/unit/data/xlsx/dateAutofilter.xlsx
Binary files differ
diff --git a/sc/qa/unit/subsequent_export-test.cxx b/sc/qa/unit/subsequent_export-test.cxx
index 972004c..2b8738d 100644
--- a/sc/qa/unit/subsequent_export-test.cxx
+++ b/sc/qa/unit/subsequent_export-test.cxx
@@ -211,6 +211,7 @@ public:
void testTdf95640_ods_to_xlsx();
void testTdf95640_ods_to_xlsx_with_standard_list();
void testTdf95640_xlsx_to_xlsx();
void testDateAutofilterXLSX();
void testRefStringXLSX();
void testRefStringConfigXLSX();
@@ -390,6 +391,7 @@ public:
CPPUNIT_TEST(testTdf95640_ods_to_xlsx);
CPPUNIT_TEST(testTdf95640_ods_to_xlsx_with_standard_list);
CPPUNIT_TEST(testTdf95640_xlsx_to_xlsx);
CPPUNIT_TEST(testDateAutofilterXLSX);
CPPUNIT_TEST(testRefStringXLSX);
CPPUNIT_TEST(testRefStringConfigXLSX);
@@ -4444,6 +4446,29 @@ void ScExportTest::testTdf95640_xlsx_to_xlsx()
"Low,Medium,High");
}
void ScExportTest::testDateAutofilterXLSX()
{
// XLSX Roundtripping autofilter with date list
ScDocShellRef xDocSh = loadDoc(u"dateAutofilter.", FORMAT_XLSX);
CPPUNIT_ASSERT(xDocSh.is());
xmlDocUniquePtr pDoc = XPathHelper::parseExport2(*this, *xDocSh, m_xSFactory, "xl/worksheets/sheet1.xml", FORMAT_XLSX);
CPPUNIT_ASSERT(pDoc);
assertXPath(pDoc, "//x:autoFilter", "ref", "A1:B4");
assertXPath(pDoc, "//x:autoFilter/x:filterColumn/x:filters/x:dateGroupItem[1]", "day", "02");
assertXPath(pDoc, "//x:autoFilter/x:filterColumn/x:filters/x:dateGroupItem[1]", "month", "03");
assertXPath(pDoc, "//x:autoFilter/x:filterColumn/x:filters/x:dateGroupItem[1]", "year", "2017");
assertXPath(pDoc, "//x:autoFilter/x:filterColumn/x:filters/x:dateGroupItem[1]", "dateTimeGrouping", "day");
assertXPath(pDoc, "//x:autoFilter/x:filterColumn/x:filters/x:dateGroupItem[2]", "day", "01");
assertXPath(pDoc, "//x:autoFilter/x:filterColumn/x:filters/x:dateGroupItem[2]", "month", "10");
assertXPath(pDoc, "//x:autoFilter/x:filterColumn/x:filters/x:dateGroupItem[2]", "year", "2014");
assertXPath(pDoc, "//x:autoFilter/x:filterColumn/x:filters/x:dateGroupItem[2]", "dateTimeGrouping", "day");
xDocSh->DoClose();
}
void ScExportTest::testTdf88657ODS()
{
ScDocShellRef xDocSh = loadDoc(u"tdf88657.", FORMAT_ODS);
diff --git a/sc/source/filter/excel/excrecds.cxx b/sc/source/filter/excel/excrecds.cxx
index 421c0e8..c31c81d 100644
--- a/sc/source/filter/excel/excrecds.cxx
+++ b/sc/source/filter/excel/excrecds.cxx
@@ -24,6 +24,7 @@
#include <svl/zforlist.hxx>
#include <sal/log.hxx>
#include <sax/fastattribs.hxx>
#include <string.h>
@@ -781,7 +782,7 @@ void XclExpAutofilter::AddMultiValueEntry( const ScQueryEntry& rEntry )
meType = MultiValue;
const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
for (const auto& rItem : rItems)
maMultiValues.push_back(rItem.maString.getString());
maMultiValues.push_back( std::make_pair(rItem.maString.getString(), rItem.meType == ScQueryEntry::ByDate) );
}
void XclExpAutofilter::WriteBody( XclExpStream& rStrm )
@@ -839,9 +840,31 @@ void XclExpAutofilter::SaveXml( XclExpXmlStream& rStrm )
rWorksheet->startElement(XML_filters);
for (const auto& rMultiValue : maMultiValues)
{
OString aStr = OUStringToOString(rMultiValue, RTL_TEXTENCODING_UTF8);
const char* pz = aStr.getStr();
rWorksheet->singleElement(XML_filter, XML_val, pz);
OString aStr = OUStringToOString(rMultiValue.first, RTL_TEXTENCODING_UTF8);
if( !rMultiValue.second )
{
const char* pz = aStr.getStr();
rWorksheet->singleElement(XML_filter, XML_val, pz);
}
else
{
sax_fastparser::FastAttributeList* pAttrList = sax_fastparser::FastSerializerHelper::createAttrList();
sal_Int32 aDateGroup[3] = { XML_year, XML_month, XML_day };
sal_Int32 idx = 0;
for (size_t i = 0; idx >= 0 && i < 3; i++)
{
OString kw = aStr.getToken(0, '-', idx);
kw = kw.trim();
if (!kw.isEmpty())
{
pAttrList->add(aDateGroup[i], kw);
}
}
// TODO: date filter can only handle YYYY-MM-DD date formats, so XML_dateTimeGrouping value
// will be "day" as default, until date filter cannot handle HH:MM:SS.
pAttrList->add(XML_dateTimeGrouping, "day");
rWorksheet->singleElement(XML_dateGroupItem, pAttrList);
}
}
rWorksheet->endElement(XML_filters);
}
diff --git a/sc/source/filter/inc/excrecds.hxx b/sc/source/filter/inc/excrecds.hxx
index 986654fb..0a4c65d 100644
--- a/sc/source/filter/inc/excrecds.hxx
+++ b/sc/source/filter/inc/excrecds.hxx
@@ -366,7 +366,7 @@ private:
sal_uInt16 nCol;
sal_uInt16 nFlags;
ExcFilterCondition aCond[ 2 ];
std::vector<OUString> maMultiValues;
std::vector<std::pair<OUString, bool>> maMultiValues; // first->values, second->bDateFormat
bool AddCondition( ScQueryConnect eConn, sal_uInt8 nType,
sal_uInt8 nOp, double fVal, const OUString* pText,
diff --git a/sc/source/filter/oox/autofilterbuffer.cxx b/sc/source/filter/oox/autofilterbuffer.cxx
index b479aef..757981a 100644
--- a/sc/source/filter/oox/autofilterbuffer.cxx
+++ b/sc/source/filter/oox/autofilterbuffer.cxx
@@ -237,6 +237,8 @@ void DiscreteFilter::importAttribs( sal_Int32 nElement, const AttributeList& rAt
case XLS_TOKEN( dateGroupItem ):
{
OUString aDateValue;
// it is just a fallback, we do not need the XML_day as default value,
// because if the dateGroupItem exists also XML_dateTimeGrouping exists!
sal_uInt16 nToken = rAttribs.getToken(XML_dateTimeGrouping, XML_day);
if( nToken == XML_year || nToken == XML_month || nToken == XML_day )
{
diff --git a/sc/source/ui/unoobj/datauno.cxx b/sc/source/ui/unoobj/datauno.cxx
index 03f5825..7d78766 100644
--- a/sc/source/ui/unoobj/datauno.cxx
+++ b/sc/source/ui/unoobj/datauno.cxx
@@ -1134,6 +1134,7 @@ void fillQueryParam(
aItem.maString = rPool.intern(aStr);
}
// filter all dates starting with the given date filter YYYY or YYYY-MM
if( aItem.meType == ScQueryEntry::ByDate && aItem.maString.getLength() < 10 )
{
ScFilterEntries aFilterEntries;