include/sax/tools/converter.hxx | 6 +++ oox/source/core/xmlfilterbase.cxx | 29 ++++++++++++++++++ sax/source/tools/converter.cxx | 60 ++++++++++++++++++++++++++------------ 3 files changed, 77 insertions(+), 18 deletions(-)
New commits: commit 5a9e966b323a1605976a53ca6bac750741036eba Author: Noel Grandin <[email protected]> AuthorDate: Tue Nov 4 13:56:56 2025 +0200 Commit: Noel Grandin <[email protected]> CommitDate: Sat Nov 8 13:11:38 2025 +0100 mso-test: invalid datetime in props This is using the test document from tdf119234-3.odt. When importing and exporting to DOCX, we generate an invalid entry in docprops/custom.xml because we dont know how to handle a DateTimeWithTimezone value.. Change-Id: I79981f8a09db432730a9b0c1cf186305b0055923 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193400 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Michael Stahl <[email protected]> (cherry picked from commit 97cf170c20c7ff1e1b3820a769cd3167a57d0c46) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193600 Reviewed-by: Noel Grandin <[email protected]> Tested-by: Jenkins diff --git a/include/sax/tools/converter.hxx b/include/sax/tools/converter.hxx index 6b18cac11bf4..f39e293bdf98 100644 --- a/include/sax/tools/converter.hxx +++ b/include/sax/tools/converter.hxx @@ -259,6 +259,12 @@ public: sal_Int16 const* pTimeZoneOffset, bool bAddTimeIf0AM = false ); + /** convert util::DateTime to XMLSchema-2 "date" or "dateTime" string */ + static void convertDateTime( OStringBuffer& rBuffer, + const css::util::DateTime& rDateTime, + sal_Int16 const* pTimeZoneOffset, + bool bAddTimeIf0AM = false ); + /** convert util::DateTime to XMLSchema-2 "time" or "dateTime" string */ static void convertTimeOrDateTime(OUStringBuffer& rBuffer, const css::util::DateTime& rDateTime); diff --git a/oox/source/core/xmlfilterbase.cxx b/oox/source/core/xmlfilterbase.cxx index 6fe4ec4c226d..173b60d8ac7b 100644 --- a/oox/source/core/xmlfilterbase.cxx +++ b/oox/source/core/xmlfilterbase.cxx @@ -28,6 +28,7 @@ #include <com/sun/star/embed/XRelationshipAccess.hpp> #include <com/sun/star/frame/XModel.hpp> #include <com/sun/star/text/XTextDocument.hpp> +#include <com/sun/star/util/DateTimeWithTimezone.hpp> #include <com/sun/star/xml/sax/XFastSAXSerializable.hpp> #include <com/sun/star/xml/sax/XSAXSerializable.hpp> #include <com/sun/star/xml/sax/Writer.hpp> @@ -624,6 +625,31 @@ writeElement( const FSHelperPtr& pDoc, sal_Int32 nXmlElement, const util::DateTi pDoc->endElement( nXmlElement ); } +static void +writeElement( const FSHelperPtr& pDoc, sal_Int32 nXmlElement, const util::DateTimeWithTimezone& rTime ) +{ + if (rTime.Timezone == 0) + { + writeElement(pDoc, nXmlElement, rTime.DateTimeInTZ); + return; + } + + if( rTime.DateTimeInTZ.Year == 0 ) + return; + + if ( ( nXmlElement >> 16 ) != XML_dcterms ) + pDoc->startElement(nXmlElement); + else + pDoc->startElement(nXmlElement, FSNS(XML_xsi, XML_type), "dcterms:W3CDTF"); + + OStringBuffer aStr; + sax::Converter::convertDateTime(aStr, rTime.DateTimeInTZ, &rTime.Timezone); + + pDoc->write( aStr ); + + pDoc->endElement( nXmlElement ); +} + static void writeElement( const FSHelperPtr& pDoc, sal_Int32 nXmlElement, const Sequence< OUString >& aItems ) { @@ -976,6 +1002,7 @@ writeCustomProperties( XmlFilterBase& rSelf, const Reference< XDocumentPropertie util::Date aDate; util::Duration aDuration; util::DateTime aDateTime; + util::DateTimeWithTimezone aDateTimeTZ; if ( rProp.Value >>= num ) { // i4 - 4-byte signed integer @@ -995,6 +1022,8 @@ writeCustomProperties( XmlFilterBase& rSelf, const Reference< XDocumentPropertie } else if ( rProp.Value >>= aDateTime ) writeElement( pAppProps, FSNS( XML_vt, XML_filetime ), aDateTime ); + else if ( rProp.Value >>= aDateTimeTZ ) + writeElement( pAppProps, FSNS( XML_vt, XML_filetime ), aDateTimeTZ ); else //no other options OSL_FAIL( "XMLFilterBase::writeCustomProperties unsupported value type!" ); diff --git a/sax/source/tools/converter.cxx b/sax/source/tools/converter.cxx index fd1c125ad663..e6863ada9d31 100644 --- a/sax/source/tools/converter.cxx +++ b/sax/source/tools/converter.cxx @@ -1572,8 +1572,9 @@ bool Converter::convertDuration(util::Duration& rDuration, return convertDurationHelper(rDuration, o3tl::trim(rString)); } +template<typename TStringBuffer> static void -lcl_AppendTimezone(OUStringBuffer & i_rBuffer, int const nOffset) +lcl_AppendTimezone(TStringBuffer & i_rBuffer, int const nOffset) { if (0 == nOffset) { @@ -1618,18 +1619,19 @@ void Converter::convertDate( convertDateTime(i_rBuffer, dt, pTimeZoneOffset); } +template<typename TStringBuffer, typename TString> static void convertTime( - OUStringBuffer& i_rBuffer, + TStringBuffer& i_rBuffer, const css::util::DateTime& i_rDateTime) { if (i_rDateTime.Hours < 10) { i_rBuffer.append('0'); } - i_rBuffer.append( OUString::number(static_cast<sal_Int32>(i_rDateTime.Hours)) + ":"); + i_rBuffer.append( TString::number(static_cast<sal_Int32>(i_rDateTime.Hours)) + ":"); if (i_rDateTime.Minutes < 10) { i_rBuffer.append('0'); } - i_rBuffer.append( OUString::number(static_cast<sal_Int32>(i_rDateTime.Minutes) ) + ":"); + i_rBuffer.append( TString::number(static_cast<sal_Int32>(i_rDateTime.Minutes) ) + ":"); if (i_rDateTime.Seconds < 10) { i_rBuffer.append('0'); } @@ -1637,16 +1639,17 @@ static void convertTime( if (i_rDateTime.NanoSeconds > 0) { OSL_ENSURE(i_rDateTime.NanoSeconds < 1000000000,"NanoSeconds cannot be more than 999 999 999"); i_rBuffer.append('.'); - std::ostringstream ostr; - ostr.fill('0'); - ostr.width(9); - ostr << i_rDateTime.NanoSeconds; - i_rBuffer.appendAscii(ostr.str().c_str()); + // pad to 9 characters length + auto sNanoStr = TString::number(i_rDateTime.NanoSeconds); + for (int i=0; i < 9 - sNanoStr.length; i++) + i_rBuffer.append('0'); + i_rBuffer.append(sNanoStr); } } +template<typename TStringBuffer> static void convertTimeZone( - OUStringBuffer& i_rBuffer, + TStringBuffer& i_rBuffer, const css::util::DateTime& i_rDateTime, sal_Int16 const* pTimeZoneOffset) { @@ -1669,7 +1672,7 @@ void Converter::convertTimeOrDateTime( i_rDateTime.Month < 1 || i_rDateTime.Month > 12 || i_rDateTime.Day < 1 || i_rDateTime.Day > 31) { - convertTime(i_rBuffer, i_rDateTime); + convertTime<OUStringBuffer, OUString>(i_rBuffer, i_rDateTime); convertTimeZone(i_rBuffer, i_rDateTime, nullptr); } else @@ -1679,14 +1682,15 @@ void Converter::convertTimeOrDateTime( } /** convert util::DateTime to ISO "date" or "dateTime" string */ -void Converter::convertDateTime( - OUStringBuffer& i_rBuffer, +template<typename TStringBuffer, typename TString, typename TStringChar> +static void lcl_convertDateTime( + TStringBuffer& i_rBuffer, const css::util::DateTime& i_rDateTime, sal_Int16 const*const pTimeZoneOffset, bool i_bAddTimeIf0AM ) { - const sal_Unicode dash('-'); - const sal_Unicode zero('0'); + const char dash('-'); + const char zero('0'); sal_Int32 const nYear(abs(i_rDateTime.Year)); if (i_rDateTime.Year < 0) { @@ -1701,11 +1705,11 @@ void Converter::convertDateTime( if (nYear < 10) { i_rBuffer.append(zero); } - i_rBuffer.append( OUString::number(nYear) + OUStringChar(dash) ); + i_rBuffer.append( TString::number(nYear) + TStringChar(dash) ); if( i_rDateTime.Month < 10 ) { i_rBuffer.append(zero); } - i_rBuffer.append( OUString::number(i_rDateTime.Month) + OUStringChar(dash) ); + i_rBuffer.append( TString::number(i_rDateTime.Month) + TStringChar(dash) ); if( i_rDateTime.Day < 10 ) { i_rBuffer.append(zero); } @@ -1717,12 +1721,32 @@ void Converter::convertDateTime( i_bAddTimeIf0AM ) { i_rBuffer.append('T'); - convertTime(i_rBuffer, i_rDateTime); + convertTime<TStringBuffer, TString>(i_rBuffer, i_rDateTime); } convertTimeZone(i_rBuffer, i_rDateTime, pTimeZoneOffset); } +/** convert util::DateTime to ISO "date" or "dateTime" string */ +void Converter::convertDateTime( + OUStringBuffer& i_rBuffer, + const css::util::DateTime& i_rDateTime, + sal_Int16 const*const pTimeZoneOffset, + bool i_bAddTimeIf0AM ) +{ + lcl_convertDateTime<OUStringBuffer, OUString, OUStringChar>(i_rBuffer, i_rDateTime, pTimeZoneOffset, i_bAddTimeIf0AM); +} + +/** convert util::DateTime to ISO "date" or "dateTime" string */ +void Converter::convertDateTime( + OStringBuffer& i_rBuffer, + const css::util::DateTime& i_rDateTime, + sal_Int16 const*const pTimeZoneOffset, + bool i_bAddTimeIf0AM ) +{ + lcl_convertDateTime<OStringBuffer, OString, OStringChar>(i_rBuffer, i_rDateTime, pTimeZoneOffset, i_bAddTimeIf0AM); +} + /** convert ISO "date" or "dateTime" string to util::DateTime */ bool Converter::parseDateTime( util::DateTime& rDateTime, std::u16string_view rString )
