Blame SOURCES/0056-sax-xmloff-fix-ODF-import-export-of-text-time-text-t.patch

f0633d
From 4ac4c151af9f743203728792f9840c4bafbb9390 Mon Sep 17 00:00:00 2001
f0633d
From: Michael Stahl <mstahl@redhat.com>
f0633d
Date: Tue, 10 Dec 2013 15:14:00 +0100
f0633d
Subject: [PATCH 056/109] sax, xmloff: fix ODF import/export of
f0633d
 text:time/text:time-value
f0633d
f0633d
The value written for an Impress time field is something like
f0633d
text:time-value="0000-00-00T23:28:07" (in LO 3.5+) or
f0633d
text:time-value="0-00-00T23:28:07" (in OOo 3.3) which contains an
f0633d
invalid all-zero date.  Such values are actually rejected by the
f0633d
ODF import since commit ae3e2f170045a1525f67e9f3e9b7e03d94f2b56b.
f0633d
f0633d
Actually there was no real support to read the RelaxNG type
f0633d
timeOrDateTime before.
f0633d
f0633d
So fix that by:
f0633d
- adding convertTimeOrDateTime/parseTimeOrDateTime functions to
f0633d
  sax::Converter
f0633d
- recognizing and ignoring the 2 invalid all-zero values written by
f0633d
  LO 3.5 and historic OOo respectively
f0633d
- writing a bare "time" in text:time-value if the DateTime struct
f0633d
  contains zero Date members
f0633d
  (Older OOo versions and AOO cannot actually read that, but everything
f0633d
  they _can_ read is invalid ODF...)
f0633d
f0633d
(cherry picked from commit cc407e50e8a1a74f9d1ed29d444dce9bd2e9167a)
f0633d
f0633d
The backport contains one change:
f0633d
XMLTextFieldExport::ProcessTimeOrDateTime() still writes the invalid
f0633d
value (to not add new backwards compat issues in stable branch),
f0633d
so this patch only fixes the import side of things.
f0633d
f0633d
Conflicts:
f0633d
	sax/source/tools/converter.cxx
f0633d
	xmloff/source/text/txtfldi.cxx
f0633d
f0633d
Change-Id: I754076caee74a5163ed3f972af0f23796aa14f9f
f0633d
Reviewed-on: https://gerrit.libreoffice.org/7026
f0633d
Reviewed-by: Eike Rathke <erack@redhat.com>
f0633d
Tested-by: Eike Rathke <erack@redhat.com>
f0633d
---
f0633d
 include/sax/tools/converter.hxx   |   9 ++
f0633d
 sax/qa/cppunit/test_converter.cxx | 138 +++++++++++++++++++++++-
f0633d
 sax/source/tools/converter.cxx    | 213 ++++++++++++++++++++++++++++----------
f0633d
 xmloff/inc/txtflde.hxx            |   6 ++
f0633d
 xmloff/source/text/txtflde.cxx    |  24 ++++-
f0633d
 xmloff/source/text/txtfldi.cxx    |   4 +-
f0633d
 6 files changed, 331 insertions(+), 63 deletions(-)
f0633d
f0633d
diff --git a/include/sax/tools/converter.hxx b/include/sax/tools/converter.hxx
f0633d
index 615308c..683fadb 100644
f0633d
--- a/include/sax/tools/converter.hxx
f0633d
+++ b/include/sax/tools/converter.hxx
f0633d
@@ -160,10 +160,19 @@ public:
f0633d
                                 const com::sun::star::util::DateTime& rDateTime,
f0633d
                                    bool bAddTimeIf0AM = false );
f0633d
 
f0633d
+    /** convert util::DateTime to ISO "time" or "dateTime" string */
f0633d
+    static void convertTimeOrDateTime(OUStringBuffer& rBuffer,
f0633d
+                            const com::sun::star::util::DateTime& rDateTime,
f0633d
+                            sal_Int16 const* pTimeZoneOffset);
f0633d
+
f0633d
     /** convert ISO "date" or "dateTime" string to util::DateTime */
f0633d
     static bool convertDateTime( com::sun::star::util::DateTime& rDateTime,
f0633d
                                  const OUString& rString );
f0633d
 
f0633d
+    /** convert ISO "time" or "dateTime" string to util::DateTime */
f0633d
+    static bool parseTimeOrDateTime(com::sun::star::util::DateTime& rDateTime,
f0633d
+                                 const OUString& rString);
f0633d
+
f0633d
     /** convert ISO "date" or "dateTime" string to util::DateTime or
f0633d
         util::Date */
f0633d
     static bool convertDateOrDateTime(
f0633d
diff --git a/sax/qa/cppunit/test_converter.cxx b/sax/qa/cppunit/test_converter.cxx
f0633d
index cfda248..f4b0c12 100644
f0633d
--- a/sax/qa/cppunit/test_converter.cxx
f0633d
+++ b/sax/qa/cppunit/test_converter.cxx
f0633d
@@ -53,6 +53,7 @@ public:
f0633d
 
f0633d
     void testDuration();
f0633d
     void testDateTime();
f0633d
+    void testTime();
f0633d
     void testDouble();
f0633d
     void testMeasure();
f0633d
     void testBool();
f0633d
@@ -64,6 +65,7 @@ public:
f0633d
     CPPUNIT_TEST_SUITE(ConverterTest);
f0633d
     CPPUNIT_TEST(testDuration);
f0633d
     CPPUNIT_TEST(testDateTime);
f0633d
+    CPPUNIT_TEST(testTime);
f0633d
     CPPUNIT_TEST(testDouble);
f0633d
     CPPUNIT_TEST(testMeasure);
f0633d
     CPPUNIT_TEST(testBool);
f0633d
@@ -240,7 +242,7 @@ void ConverterTest::testDateTime()
f0633d
     doTestDateTimeF( "0001-13-01T00:00:00" ); // invalid: M > 12
f0633d
     doTestDateTimeF( "0001-01-32T00:00:00" ); // invalid: D > 31
f0633d
     doTestDateTimeF( "0001-01-01T25:00:00" ); // invalid: H > 24
f0633d
-    doTestDateTimeF( "0001-01-01T00:60:00" ); // invalid: H > 59
f0633d
+    doTestDateTimeF( "0001-01-01T00:60:00" ); // invalid: M > 59
f0633d
     doTestDateTimeF( "0001-01-01T00:00:60" ); // invalid: S > 59
f0633d
     doTestDateTimeF( "0001-01-01T24:01:00" ); // invalid: H=24, but M != 0
f0633d
     doTestDateTimeF( "0001-01-01T24:00:01" ); // invalid: H=24, but S != 0
f0633d
@@ -251,9 +253,143 @@ void ConverterTest::testDateTime()
f0633d
     doTestDateTimeF( "0001-01-02T00:00:00-14:01" ); // invalid: TZ < -14:00
f0633d
     doTestDateTimeF( "2100-02-29T00:00:00-00:00" ); // invalid: no leap year
f0633d
     doTestDateTimeF( "1900-02-29T00:00:00-00:00" ); // invalid: no leap year
f0633d
+    doTestDateTimeF( "00:00:00" ); // invalid: no date
f0633d
+    doTestDateTimeF( "T00:00:00" ); // invalid: no date
f0633d
     SAL_INFO("sax.cppunit","\nSAX CONVERTER TEST END");
f0633d
 }
f0633d
 
f0633d
+static void doTestTime(util::DateTime const & rdt, char const*const pis,
f0633d
+        char const*const i_pos = 0)
f0633d
+{
f0633d
+    char const*const pos((i_pos) ? i_pos : pis);
f0633d
+    OUString is(OUString::createFromAscii(pis));
f0633d
+    util::DateTime odt;
f0633d
+    SAL_INFO("sax.cppunit","about to convert '" << is << "'");
f0633d
+    bool bSuccess( Converter::parseTimeOrDateTime(odt, is) );
f0633d
+    SAL_INFO("sax.cppunit","Y:" << odt.Year << " M:" << odt.Month << " D:" << odt.Day << "  H:" << odt.Hours << " M:" << odt.Minutes << " S:" << odt.Seconds << " nS:" << odt.NanoSeconds << " UTC: " << (bool)odt.IsUTC);
f0633d
+    CPPUNIT_ASSERT(bSuccess);
f0633d
+    CPPUNIT_ASSERT(eqDateTime(rdt, odt));
f0633d
+    OUStringBuffer buf;
f0633d
+    Converter::convertTimeOrDateTime(buf, odt, 0);
f0633d
+    SAL_INFO("sax.cppunit","" << buf.toString());
f0633d
+    CPPUNIT_ASSERT_EQUAL(OUString::createFromAscii(pos),
f0633d
+                         buf.makeStringAndClear());
f0633d
+}
f0633d
+
f0633d
+static void doTestTimeF(char const*const pis)
f0633d
+{
f0633d
+    util::DateTime odt;
f0633d
+    bool bSuccess = Converter::parseTimeOrDateTime(odt,
f0633d
+            OUString::createFromAscii(pis));
f0633d
+    SAL_INFO("sax.cppunit","Y:" << odt.Year << " M:" << odt.Month << " D:" << odt.Day << "  H:" << odt.Hours << "H M:" << odt.Minutes << " S:" << odt.Seconds << " nS:" << odt.NanoSeconds);
f0633d
+    CPPUNIT_ASSERT(!bSuccess);
f0633d
+}
f0633d
+
f0633d
+void ConverterTest::testTime() // time or dateTime + horrible backcompat mess
f0633d
+{
f0633d
+    doTestTime( util::DateTime(0, 0, 0, 0, 1, 1, 1, false),
f0633d
+            "0001-01-01T00:00:00" );
f0633d
+    doTestTime( util::DateTime(0, 0, 0, 0, 1, 1, 1, false),
f0633d
+            "0001-01-01T00:00:00" );
f0633d
+    doTestTime( util::DateTime(0, 0, 0, 0, 1, 1, 1, true),
f0633d
+            "0001-01-01T00:00:00Z" );
f0633d
+    doTestTime( util::DateTime(0, 0, 0, 0, 1, 1, -1, false),
f0633d
+            "-0001-01-01T00:00:00");
f0633d
+    doTestTime( util::DateTime(0, 0, 0, 0, 1, 1, -1, true),
f0633d
+            "-0001-01-01T01:00:00+01:00", "-0001-01-01T00:00:00Z");
f0633d
+    doTestTime( util::DateTime(0, 0, 0, 0, 1, 1, -324, false),
f0633d
+            "-0324-01-01T00:00:00" );
f0633d
+    doTestTime( util::DateTime(0, 0, 0, 0, 1, 1, 1, true),
f0633d
+            "0001-01-01T00:00:00-00:00", "0001-01-01T00:00:00Z" );
f0633d
+    doTestTime( util::DateTime(0, 0, 0, 0, 1, 1, 1, true),
f0633d
+            "0001-01-01T00:00:00+00:00", "0001-01-01T00:00:00Z" );
f0633d
+    doTestTime( util::DateTime(0, 0, 0, 12, 2, 1, 1, true),
f0633d
+            "0001-01-02T00:00:00-12:00", "0001-01-02T12:00:00Z" );
f0633d
+    doTestTime( util::DateTime(0, 0, 0, 12, 1, 1, 1, true),
f0633d
+            "0001-01-02T00:00:00+12:00", "0001-01-01T12:00:00Z"  );
f0633d
+    doTestTime( util::DateTime(990000000, 59, 59, 23, 31, 12, 9999, false),
f0633d
+            "9999-12-31T23:59:59.99",  "9999-12-31T23:59:59.990000000" );
f0633d
+    doTestTime( util::DateTime(990000000, 59, 59, 23, 31, 12, 9999, true),
f0633d
+            "9999-12-31T23:59:59.99Z", "9999-12-31T23:59:59.990000000Z" );
f0633d
+    doTestTime( util::DateTime(999999999, 59, 59, 23, 31, 12, 9999, false),
f0633d
+            "9999-12-31T23:59:59.9999999999999999999999999999999999999",
f0633d
+            "9999-12-31T23:59:59.999999999" );
f0633d
+    doTestTime( util::DateTime(999999999, 59, 59, 23, 31, 12, 9999, true),
f0633d
+            "9999-12-31T23:59:59.9999999999999999999999999999999999999Z",
f0633d
+            "9999-12-31T23:59:59.999999999Z" );
f0633d
+    doTestTime( util::DateTime(0, 0, 0, 0, 29, 2, 2000, true), // leap year
f0633d
+            "2000-02-29T00:00:00-00:00", "2000-02-29T00:00:00Z" );
f0633d
+    doTestTime( util::DateTime(0, 0, 0, 0, 29, 2, 1600, true), // leap year
f0633d
+            "1600-02-29T00:00:00-00:00", "1600-02-29T00:00:00Z" );
f0633d
+    doTestTime( util::DateTime(0, 0, 0, 24, 1, 1, 333, false)
f0633d
+                /*(0, 0, 0, 0, 2, 1, 333)*/,
f0633d
+            "0333-01-01T24:00:00"/*, "0333-01-02T00:00:00"*/ );
f0633d
+    // While W3C XMLSchema specifies a minimum of 4 year digits we are lenient
f0633d
+    // in what we accept.
f0633d
+    doTestTime( util::DateTime(0, 0, 0, 0, 1, 1, 1, false),
f0633d
+            "1-01-01T00:00:00", "0001-01-01T00:00:00" );
f0633d
+
f0633d
+    doTestTime( util::DateTime(0, 0, 0, 0, 0, 0, 0, false), "00:00:00" );
f0633d
+    doTestTime( util::DateTime(0, 0, 0, 24, 0, 0, 0, false), "24:00:00" );
f0633d
+    doTestTime( util::DateTime(0, 0, 59, 0, 0, 0, 0, false), "00:59:00" );
f0633d
+    doTestTime( util::DateTime(0, 1, 2, 4, 0, 0, 0, true), "04:02:01Z" );
f0633d
+    doTestTime( util::DateTime(0, 1, 2, 4, 0, 0, 0, true),
f0633d
+            "05:02:01+01:00", "04:02:01Z" );
f0633d
+    doTestTime( util::DateTime(0, 11, 12, 9, 0, 0, 0, true),
f0633d
+            "05:12:11-04:00", "09:12:11Z" );
f0633d
+    doTestTime( util::DateTime(990000000, 59, 59, 23, 0, 0, 0, false),
f0633d
+            "23:59:59.99",  "23:59:59.990000000" );
f0633d
+    doTestTime( util::DateTime(990000000, 59, 59, 23, 0, 0, 0, true),
f0633d
+            "23:59:59.99Z",  "23:59:59.990000000Z" );
f0633d
+    // backwards compatible: recognize invalid 0000-00-00 date (LO 3.5)
f0633d
+    doTestTime( util::DateTime(0, 1, 0, 0, 0, 0, 0, false),
f0633d
+            "0000-00-00T00:00:01", "00:00:01" );
f0633d
+    // backwards compatible: recognize invalid 0-00-00 date (OOo)
f0633d
+    doTestTime( util::DateTime(0, 0, 1, 0, 0, 0, 0, false),
f0633d
+            "0-00-00T00:01:00", "00:01:00" );
f0633d
+
f0633d
+    doTestTimeF( "+0001-01-01T00:00:00" ); // invalid: ^+
f0633d
+    doTestTimeF( "0001-1-01T00:00:00" ); // invalid: < 2 M
f0633d
+    doTestTimeF( "0001-01-1T00:00:00" ); // invalid: < 2 D
f0633d
+    doTestTimeF( "0001-01-01T0:00:00" ); // invalid: < 2 H
f0633d
+    doTestTimeF( "0001-01-01T00:0:00" ); // invalid: < 2 M
f0633d
+    doTestTimeF( "0001-01-01T00:00:0" ); // invalid: < 2 S
f0633d
+    doTestTimeF( "0001-01-01T00:00:00." ); // invalid: .$
f0633d
+    doTestTimeF( "0001-01-01T00:00:00+1:00" ); // invalid: < 2 TZ H
f0633d
+    doTestTimeF( "0001-01-01T00:00:00+00:1" ); // invalid: < 2 TZ M
f0633d
+    doTestTimeF( "0001-13-01T00:00:00" ); // invalid: M > 12
f0633d
+    doTestTimeF( "0001-01-32T00:00:00" ); // invalid: D > 31
f0633d
+    doTestTimeF( "0001-01-01T25:00:00" ); // invalid: H > 24
f0633d
+    doTestTimeF( "0001-01-01T00:60:00" ); // invalid: M > 59
f0633d
+    doTestTimeF( "0001-01-01T00:00:60" ); // invalid: S > 59
f0633d
+    doTestTimeF( "0001-01-01T24:01:00" ); // invalid: H=24, but M != 0
f0633d
+    doTestTimeF( "0001-01-01T24:00:01" ); // invalid: H=24, but S != 0
f0633d
+    doTestTimeF( "0001-01-01T24:00:00.1" ); // invalid: H=24, but H != 0
f0633d
+    doTestTimeF( "0001-01-02T00:00:00+15:00" ); // invalid: TZ > +14:00
f0633d
+    doTestTimeF( "0001-01-02T00:00:00+14:01" ); // invalid: TZ > +14:00
f0633d
+    doTestTimeF( "0001-01-02T00:00:00-15:00" ); // invalid: TZ < -14:00
f0633d
+    doTestTimeF( "0001-01-02T00:00:00-14:01" ); // invalid: TZ < -14:00
f0633d
+    doTestTimeF( "2100-02-29T00:00:00-00:00" ); // invalid: no leap year
f0633d
+    doTestTimeF( "1900-02-29T00:00:00-00:00" ); // invalid: no leap year
f0633d
+    doTestTimeF( "T00:00:00" ); // invalid: T
f0633d
+    doTestTimeF( "0:00:00" ); // invalid: < 2 H
f0633d
+    doTestTimeF( "00:0:00" ); // invalid: < 2 M
f0633d
+    doTestTimeF( "00:00:0" ); // invalid: < 2 S
f0633d
+    doTestTimeF( "00:00:00." ); // invalid: .$
f0633d
+    doTestTimeF( "00:00:00+1:00" ); // invalid: < 2 TZ H
f0633d
+    doTestTimeF( "00:00:00+00:1" ); // invalid: < 2 TZ M
f0633d
+    doTestTimeF( "25:00:00" ); // invalid: H > 24
f0633d
+    doTestTimeF( "00:60:00" ); // invalid: M > 59
f0633d
+    doTestTimeF( "00:00:60" ); // invalid: S > 59
f0633d
+    doTestTimeF( "24:01:00" ); // invalid: H=24, but M != 0
f0633d
+    doTestTimeF( "24:00:01" ); // invalid: H=24, but S != 0
f0633d
+    doTestTimeF( "24:00:00.1" ); // invalid: H=24, but H != 0
f0633d
+    doTestTimeF( "00:00:00+15:00" ); // invalid: TZ > +14:00
f0633d
+    doTestTimeF( "00:00:00+14:01" ); // invalid: TZ > +14:00
f0633d
+    doTestTimeF( "00:00:00-15:00" ); // invalid: TZ < -14:00
f0633d
+    doTestTimeF( "00:00:00-14:01" ); // invalid: TZ < -14:00
f0633d
+}
f0633d
+
f0633d
 void doTestDouble(char const*const pis, double const rd,
f0633d
         sal_Int16 const nSourceUnit, sal_Int16 const nTargetUnit)
f0633d
 {
f0633d
diff --git a/sax/source/tools/converter.cxx b/sax/source/tools/converter.cxx
f0633d
index e99690e..bc8b0c1 100644
f0633d
--- a/sax/source/tools/converter.cxx
f0633d
+++ b/sax/source/tools/converter.cxx
f0633d
@@ -1231,6 +1231,69 @@ void Converter::convertDate(
f0633d
     convertDateTime(i_rBuffer, dt, false);
f0633d
 }
f0633d
 
f0633d
+static void convertTime(
f0633d
+        OUStringBuffer& i_rBuffer,
f0633d
+        const com::sun::star::util::DateTime& i_rDateTime)
f0633d
+{
f0633d
+    if (i_rDateTime.Hours   < 10) {
f0633d
+        i_rBuffer.append(sal_Unicode('0'));
f0633d
+    }
f0633d
+    i_rBuffer.append( static_cast<sal_Int32>(i_rDateTime.Hours)   )
f0633d
+             .append(sal_Unicode(':'));
f0633d
+    if (i_rDateTime.Minutes < 10) {
f0633d
+        i_rBuffer.append(sal_Unicode('0'));
f0633d
+    }
f0633d
+    i_rBuffer.append( static_cast<sal_Int32>(i_rDateTime.Minutes) )
f0633d
+             .append(sal_Unicode(':'));
f0633d
+    if (i_rDateTime.Seconds < 10) {
f0633d
+        i_rBuffer.append(sal_Unicode('0'));
f0633d
+    }
f0633d
+    i_rBuffer.append( static_cast<sal_Int32>(i_rDateTime.Seconds) );
f0633d
+    if (i_rDateTime.NanoSeconds > 0) {
f0633d
+        OSL_ENSURE(i_rDateTime.NanoSeconds < 1000000000,"NanoSeconds cannot be more than 999 999 999");
f0633d
+        i_rBuffer.append(sal_Unicode('.'));
f0633d
+        std::ostringstream ostr;
f0633d
+        ostr.fill('0');
f0633d
+        ostr.width(9);
f0633d
+        ostr << i_rDateTime.NanoSeconds;
f0633d
+        i_rBuffer.append(OUString::createFromAscii(ostr.str().c_str()));
f0633d
+    }
f0633d
+}
f0633d
+
f0633d
+static void convertTimeZone(
f0633d
+        OUStringBuffer& i_rBuffer,
f0633d
+        const com::sun::star::util::DateTime& i_rDateTime,
f0633d
+        sal_Int16 const* pTimeZoneOffset)
f0633d
+{
f0633d
+    if (pTimeZoneOffset)
f0633d
+    {
f0633d
+        lcl_AppendTimezone(i_rBuffer, *pTimeZoneOffset);
f0633d
+    }
f0633d
+    else if (i_rDateTime.IsUTC)
f0633d
+    {
f0633d
+        lcl_AppendTimezone(i_rBuffer, 0);
f0633d
+    }
f0633d
+}
f0633d
+
f0633d
+/** convert util::DateTime to ISO "time" or "dateTime" string */
f0633d
+void Converter::convertTimeOrDateTime(
f0633d
+        OUStringBuffer& i_rBuffer,
f0633d
+        const com::sun::star::util::DateTime& i_rDateTime,
f0633d
+        sal_Int16 const* pTimeZoneOffset)
f0633d
+{
f0633d
+    if (i_rDateTime.Year == 0 ||
f0633d
+        i_rDateTime.Month < 1 || i_rDateTime.Month > 12 ||
f0633d
+        i_rDateTime.Day < 1 || i_rDateTime.Day > 31)
f0633d
+    {
f0633d
+        convertTime(i_rBuffer, i_rDateTime);
f0633d
+        convertTimeZone(i_rBuffer, i_rDateTime, pTimeZoneOffset);
f0633d
+    }
f0633d
+    else
f0633d
+    {
f0633d
+        convertDateTime(i_rBuffer, i_rDateTime, true);
f0633d
+    }
f0633d
+}
f0633d
+
f0633d
 /** convert util::DateTime to ISO "date" or "dateTime" string */
f0633d
 void Converter::convertDateTime(
f0633d
         OUStringBuffer& i_rBuffer,
f0633d
@@ -1238,10 +1301,7 @@ void Converter::convertDateTime(
f0633d
         bool i_bAddTimeIf0AM )
f0633d
 {
f0633d
     const sal_Unicode dash('-');
f0633d
-    const sal_Unicode col (':');
f0633d
-    const sal_Unicode dot ('.');
f0633d
     const sal_Unicode zero('0');
f0633d
-    const sal_Unicode tee ('T');
f0633d
 
f0633d
     sal_Int32 const nYear(abs(i_rDateTime.Year));
f0633d
     if (i_rDateTime.Year < 0) {
f0633d
@@ -1271,42 +1331,11 @@ void Converter::convertDateTime(
f0633d
         i_rDateTime.Hours   != 0 ||
f0633d
         i_bAddTimeIf0AM )
f0633d
     {
f0633d
-        i_rBuffer.append(tee);
f0633d
-        if( i_rDateTime.Hours   < 10 ) {
f0633d
-            i_rBuffer.append(zero);
f0633d
-        }
f0633d
-        i_rBuffer.append( static_cast<sal_Int32>(i_rDateTime.Hours)   )
f0633d
-                 .append(col);
f0633d
-        if( i_rDateTime.Minutes < 10 ) {
f0633d
-            i_rBuffer.append(zero);
f0633d
-        }
f0633d
-        i_rBuffer.append( static_cast<sal_Int32>(i_rDateTime.Minutes) )
f0633d
-                 .append(col);
f0633d
-        if( i_rDateTime.Seconds < 10 ) {
f0633d
-            i_rBuffer.append(zero);
f0633d
-        }
f0633d
-        i_rBuffer.append( static_cast<sal_Int32>(i_rDateTime.Seconds) );
f0633d
-        if( i_rDateTime.NanoSeconds > 0 ) {
f0633d
-            OSL_ENSURE(i_rDateTime.NanoSeconds < 1000000000,"NanoSeconds cannot be more than 999 999 999");
f0633d
-            i_rBuffer.append(dot);
f0633d
-            std::ostringstream ostr;
f0633d
-            ostr.fill('0');
f0633d
-            ostr.width(9);
f0633d
-            ostr << i_rDateTime.NanoSeconds;
f0633d
-            i_rBuffer.append(OUString::createFromAscii(ostr.str().c_str()));
f0633d
-        }
f0633d
+        i_rBuffer.append(sal_Unicode('T'));
f0633d
+        convertTime(i_rBuffer, i_rDateTime);
f0633d
     }
f0633d
 
f0633d
-    sal_uInt16 * pTimezone(0); // FIXME pass this as parameter
f0633d
-    if (pTimezone)
f0633d
-    {
f0633d
-        lcl_AppendTimezone(i_rBuffer, *pTimezone);
f0633d
-    }
f0633d
-    else if (i_rDateTime.IsUTC)
f0633d
-    {
f0633d
-        // append local time
f0633d
-        lcl_AppendTimezone(i_rBuffer, 0);
f0633d
-    }
f0633d
+    convertTimeZone(i_rBuffer, i_rDateTime, 0);
f0633d
 }
f0633d
 
f0633d
 /** convert ISO "date" or "dateTime" string to util::DateTime */
f0633d
@@ -1379,11 +1408,17 @@ static void lcl_ConvertToUTC(
f0633d
         {
f0633d
             return;
f0633d
         }
f0633d
+        sal_Int16 nDayAdd(0);
f0633d
         while (24 <= o_rHours)
f0633d
         {
f0633d
             o_rHours -= 24;
f0633d
-            ++o_rDay;
f0633d
+            ++nDayAdd;
f0633d
         }
f0633d
+        if (o_rDay == 0)
f0633d
+        {
f0633d
+            return; // handle time without date - don't adjust what isn't there
f0633d
+        }
f0633d
+        o_rDay += nDayAdd;
f0633d
         sal_Int16 const nDaysInMonth(lcl_MaxDaysPerMonth(o_rMonth, o_rYear));
f0633d
         if (o_rDay <= nDaysInMonth)
f0633d
         {
f0633d
@@ -1414,6 +1449,10 @@ static void lcl_ConvertToUTC(
f0633d
             ++nDaySubtract;
f0633d
         }
f0633d
         o_rHours -= nOffsetHours;
f0633d
+        if (o_rDay == 0)
f0633d
+        {
f0633d
+            return; // handle time without date - don't adjust what isn't there
f0633d
+        }
f0633d
         if (nDaySubtract < o_rDay)
f0633d
         {
f0633d
             o_rDay -= nDaySubtract;
f0633d
@@ -1453,18 +1492,17 @@ readDateTimeComponent(const OUString & rString,
f0633d
     return true;
f0633d
 }
f0633d
 
f0633d
-
f0633d
-
f0633d
 /** convert ISO "date" or "dateTime" string to util::DateTime or util::Date */
f0633d
-bool Converter::convertDateOrDateTime(
f0633d
-                util::Date & rDate, util::DateTime & rDateTime,
f0633d
-                bool & rbDateTime, const OUString & rString )
f0633d
+static bool lcl_parseDate(
f0633d
+                bool & isNegative,
f0633d
+                sal_Int32 & nYear, sal_Int32 & nMonth, sal_Int32 & nDay,
f0633d
+                bool & bHaveTime,
f0633d
+                sal_Int32 & nPos,
f0633d
+                const OUString & string,
f0633d
+                bool const bIgnoreInvalidOrMissingDate)
f0633d
 {
f0633d
     bool bSuccess = true;
f0633d
-    bool isNegative(false);
f0633d
 
f0633d
-    const OUString string = rString.trim().toAsciiUpperCase();
f0633d
-    sal_Int32 nPos(0);
f0633d
     if (string.getLength() > nPos)
f0633d
     {
f0633d
         if (sal_Unicode('-') == string[nPos])
f0633d
@@ -1474,13 +1512,15 @@ bool Converter::convertDateOrDateTime(
f0633d
         }
f0633d
     }
f0633d
 
f0633d
-    sal_Int32 nYear(0);
f0633d
     {
f0633d
         // While W3C XMLSchema specifies years with a minimum of 4 digits, be
f0633d
         // leninent in what we accept for years < 1000. One digit is acceptable
f0633d
         // if the remainders match.
f0633d
         bSuccess = readDateTimeComponent(string, nPos, nYear, 1, false);
f0633d
-        bSuccess &= (0 < nYear);
f0633d
+        if (!bIgnoreInvalidOrMissingDate)
f0633d
+        {
f0633d
+            bSuccess &= (0 < nYear);
f0633d
+        }
f0633d
         bSuccess &= (nPos < string.getLength()); // not last token
f0633d
     }
f0633d
     if (bSuccess && (sal_Unicode('-') != string[nPos])) // separator
f0633d
@@ -1492,11 +1532,14 @@ bool Converter::convertDateOrDateTime(
f0633d
         ++nPos;
f0633d
     }
f0633d
 
f0633d
-    sal_Int32 nMonth(0);
f0633d
     if (bSuccess)
f0633d
     {
f0633d
         bSuccess = readDateTimeComponent(string, nPos, nMonth, 2, true);
f0633d
-        bSuccess &= (0 < nMonth) && (nMonth <= 12);
f0633d
+        if (!bIgnoreInvalidOrMissingDate)
f0633d
+        {
f0633d
+            bSuccess &= (0 < nMonth);
f0633d
+        }
f0633d
+        bSuccess &= (nMonth <= 12);
f0633d
         bSuccess &= (nPos < string.getLength()); // not last token
f0633d
     }
f0633d
     if (bSuccess && (sal_Unicode('-') != string[nPos])) // separator
f0633d
@@ -1508,14 +1551,16 @@ bool Converter::convertDateOrDateTime(
f0633d
         ++nPos;
f0633d
     }
f0633d
 
f0633d
-    sal_Int32 nDay(0);
f0633d
     if (bSuccess)
f0633d
     {
f0633d
         bSuccess = readDateTimeComponent(string, nPos, nDay, 2, true);
f0633d
-        bSuccess &= (0 < nDay) && (nDay <= lcl_MaxDaysPerMonth(nMonth, nYear));
f0633d
+        if (!bIgnoreInvalidOrMissingDate)
f0633d
+        {
f0633d
+            bSuccess &= (0 < nDay);
f0633d
+        }
f0633d
+        bSuccess &= (nDay <= lcl_MaxDaysPerMonth(nMonth, nYear));
f0633d
     }
f0633d
 
f0633d
-    bool bHaveTime(false);
f0633d
     if (bSuccess && (nPos < string.getLength()))
f0633d
     {
f0633d
         if (sal_Unicode('T') == string[nPos]) // time separator
f0633d
@@ -1525,6 +1570,40 @@ bool Converter::convertDateOrDateTime(
f0633d
         }
f0633d
     }
f0633d
 
f0633d
+    return bSuccess;
f0633d
+}
f0633d
+
f0633d
+/** convert ISO "date" or "dateTime" string to util::DateTime or util::Date */
f0633d
+static bool lcl_parseDateTime(
f0633d
+                util::Date *const pDate, util::DateTime & rDateTime,
f0633d
+                bool & rbDateTime,
f0633d
+                const OUString & rString,
f0633d
+                bool const bIgnoreInvalidOrMissingDate)
f0633d
+{
f0633d
+    bool bSuccess = true;
f0633d
+
f0633d
+    const OUString string = rString.trim().toAsciiUpperCase();
f0633d
+
f0633d
+    bool isNegative(false);
f0633d
+    sal_Int32 nYear(0);
f0633d
+    sal_Int32 nMonth(0);
f0633d
+    sal_Int32 nDay(0);
f0633d
+    sal_Int32 nPos(0);
f0633d
+    bool bHaveTime(false);
f0633d
+
f0633d
+    if (    !bIgnoreInvalidOrMissingDate
f0633d
+        ||  string.indexOf(':') == -1  // no time?
f0633d
+        ||  (string.indexOf('-') != -1
f0633d
+             && string.indexOf('-') < string.indexOf(':')))
f0633d
+    {
f0633d
+        bSuccess &= lcl_parseDate(isNegative, nYear, nMonth, nDay,
f0633d
+                bHaveTime, nPos, string, bIgnoreInvalidOrMissingDate);
f0633d
+    }
f0633d
+    else
f0633d
+    {
f0633d
+        bHaveTime = true;
f0633d
+    }
f0633d
+
f0633d
     sal_Int32 nHours(0);
f0633d
     sal_Int32 nMinutes(0);
f0633d
     sal_Int32 nSeconds(0);
f0633d
@@ -1658,7 +1737,7 @@ bool Converter::convertDateOrDateTime(
f0633d
         sal_uInt16 * pTimezone(0); // FIXME pass this as parameter
f0633d
         sal_Int16 const nTimezoneOffset = ((bHaveTimezoneMinus) ? (-1) : (+1))
f0633d
                         * ((nTimezoneHours * 60) + nTimezoneMinutes);
f0633d
-        if (bHaveTime) // time is optional
f0633d
+        if (!pDate || bHaveTime) // time is optional
f0633d
         {
f0633d
             rDateTime.Year =
f0633d
                 ((isNegative) ? (-1) : (+1)) * static_cast<sal_Int16>(nYear);
f0633d
@@ -1691,10 +1770,10 @@ bool Converter::convertDateOrDateTime(
f0633d
         }
f0633d
         else
f0633d
         {
f0633d
-            rDate.Year =
f0633d
+            pDate->Year =
f0633d
                 ((isNegative) ? (-1) : (+1)) * static_cast<sal_Int16>(nYear);
f0633d
-            rDate.Month = static_cast<sal_uInt16>(nMonth);
f0633d
-            rDate.Day = static_cast<sal_uInt16>(nDay);
f0633d
+            pDate->Month = static_cast<sal_uInt16>(nMonth);
f0633d
+            pDate->Day = static_cast<sal_uInt16>(nDay);
f0633d
             if (bHaveTimezone)
f0633d
             {
f0633d
                 if (pTimezone)
f0633d
@@ -1713,6 +1792,26 @@ bool Converter::convertDateOrDateTime(
f0633d
     return bSuccess;
f0633d
 }
f0633d
 
f0633d
+/** convert ISO "time" or "dateTime" string to util::DateTime */
f0633d
+bool Converter::parseTimeOrDateTime(
f0633d
+                util::DateTime & rDateTime,
f0633d
+                const OUString & rString)
f0633d
+{
f0633d
+    bool dummy;
f0633d
+    return lcl_parseDateTime(
f0633d
+                0, rDateTime, dummy, rString, true);
f0633d
+}
f0633d
+
f0633d
+/** convert ISO "date" or "dateTime" string to util::DateTime or util::Date */
f0633d
+bool Converter::convertDateOrDateTime(
f0633d
+                util::Date & rDate, util::DateTime & rDateTime,
f0633d
+                bool & rbDateTime,
f0633d
+                const OUString & rString )
f0633d
+{
f0633d
+    return lcl_parseDateTime(
f0633d
+                &rDate, rDateTime, rbDateTime, rString, false);
f0633d
+}
f0633d
+
f0633d
 
f0633d
 /** gets the position of the first comma after npos in the string
f0633d
     rStr. Commas inside '"' pairs are not matched */
f0633d
diff --git a/xmloff/inc/txtflde.hxx b/xmloff/inc/txtflde.hxx
f0633d
index 30e607b..53b84cf 100644
f0633d
--- a/xmloff/inc/txtflde.hxx
f0633d
+++ b/xmloff/inc/txtflde.hxx
f0633d
@@ -366,6 +366,12 @@ protected:
f0633d
         sal_Bool bIsDate,           /// export as date (rather than date/time)?
f0633d
         sal_uInt16 nPrefix = XML_NAMESPACE_TEXT);   /// attribute name prefix
f0633d
 
f0633d
+    /// export time or dateTime
f0633d
+    void ProcessTimeOrDateTime(
f0633d
+        enum ::xmloff::token::XMLTokenEnum eXMLName,    /// attribute token
f0633d
+        const ::com::sun::star::util::DateTime& rTime,  /// date/time value
f0633d
+        sal_uInt16 nPrefix = XML_NAMESPACE_TEXT);   /// attribute name prefix
f0633d
+
f0633d
     /// export all attributes for bibliography data fields
f0633d
     void ProcessBibliographyData(
f0633d
         const ::com::sun::star::uno::Reference <
f0633d
diff --git a/xmloff/source/text/txtflde.cxx b/xmloff/source/text/txtflde.cxx
f0633d
index 7e013db..0ad8fcb 100644
f0633d
--- a/xmloff/source/text/txtflde.cxx
f0633d
+++ b/xmloff/source/text/txtflde.cxx
f0633d
@@ -1261,17 +1261,17 @@ void XMLTextFieldExport::ExportFieldHelper(
f0633d
         if (xPropSetInfo->hasPropertyByName(sPropertyDateTimeValue))
f0633d
         {
f0633d
             // no value -> current time
f0633d
-            ProcessDateTime(XML_TIME_VALUE,
f0633d
+            ProcessTimeOrDateTime(XML_TIME_VALUE,
f0633d
                             GetDateTimeProperty(sPropertyDateTimeValue,
f0633d
                                                 rPropSet),
f0633d
-                            sal_False );
f0633d
+                            XML_NAMESPACE_TEXT);
f0633d
         }
f0633d
         if (xPropSetInfo->hasPropertyByName(sPropertyDateTime))
f0633d
         {
f0633d
             // no value -> current time
f0633d
-            ProcessDateTime(XML_TIME_VALUE,
f0633d
+            ProcessTimeOrDateTime(XML_TIME_VALUE,
f0633d
                             GetDateTimeProperty(sPropertyDateTime,rPropSet),
f0633d
-                            sal_False );
f0633d
+                            XML_NAMESPACE_TEXT);
f0633d
         }
f0633d
         if (xPropSetInfo->hasPropertyByName(sPropertyIsFixed))
f0633d
         {
f0633d
@@ -2674,6 +2674,22 @@ void XMLTextFieldExport::ProcessDateTime(enum XMLTokenEnum eName,
f0633d
     }
f0633d
 }
f0633d
 
f0633d
+/// export a time or dateTime
f0633d
+void XMLTextFieldExport::ProcessTimeOrDateTime(enum XMLTokenEnum eName,
f0633d
+                                         const util::DateTime& rTime,
f0633d
+                                         sal_uInt16 nPrefix)
f0633d
+{
f0633d
+    OUStringBuffer aBuffer;
f0633d
+
f0633d
+    // date/time value
f0633d
+//    ::sax::Converter::convertTimeOrDateTime(aBuffer, rTime, 0);
f0633d
+// NOTE: for 4.1 continue writing the invalid value that old versions can read
f0633d
+    ::sax::Converter::convertDateTime(aBuffer, rTime);
f0633d
+
f0633d
+    // output attribute
f0633d
+    ProcessString(eName, aBuffer.makeStringAndClear(), sal_True, nPrefix);
f0633d
+}
f0633d
+
f0633d
 
f0633d
 SvXMLEnumMapEntry const aBibliographyDataTypeMap[] =
f0633d
 {
f0633d
diff --git a/xmloff/source/text/txtfldi.cxx b/xmloff/source/text/txtfldi.cxx
f0633d
index cf6bc10..875677c 100644
f0633d
--- a/xmloff/source/text/txtfldi.cxx
f0633d
+++ b/xmloff/source/text/txtfldi.cxx
f0633d
@@ -1106,6 +1106,7 @@ void XMLTimeFieldImportContext::ProcessAttribute(
f0633d
     {
f0633d
         case XML_TOK_TEXTFIELD_TIME_VALUE:
f0633d
         {
f0633d
+            // FIXME double appears unused?
f0633d
             double fTmp;
f0633d
             if (GetImport().GetMM100UnitConverter().
f0633d
                 convertDateTime(fTmp, sAttrValue))
f0633d
@@ -1114,7 +1115,8 @@ void XMLTimeFieldImportContext::ProcessAttribute(
f0633d
                 bTimeOK = sal_True;
f0633d
             }
f0633d
 
f0633d
-            if (::sax::Converter::convertDateTime(aDateTimeValue, sAttrValue))
f0633d
+            if (::sax::Converter::parseTimeOrDateTime(aDateTimeValue,
f0633d
+                        sAttrValue))
f0633d
             {
f0633d
                 bTimeOK = sal_True;
f0633d
             }
f0633d
-- 
f0633d
1.8.4.2
f0633d