summaryrefslogtreecommitdiff
path: root/sax
diff options
context:
space:
mode:
Diffstat (limited to 'sax')
-rw-r--r--sax/qa/cppunit/test_converter.cxx138
-rw-r--r--sax/source/tools/converter.cxx209
2 files changed, 292 insertions, 55 deletions
diff --git a/sax/qa/cppunit/test_converter.cxx b/sax/qa/cppunit/test_converter.cxx
index 28eefd9fc2ec..7145e5e6b05c 100644
--- a/sax/qa/cppunit/test_converter.cxx
+++ b/sax/qa/cppunit/test_converter.cxx
@@ -53,6 +53,7 @@ public:
void testDuration();
void testDateTime();
+ void testTime();
void testDouble();
void testMeasure();
void testBool();
@@ -64,6 +65,7 @@ public:
CPPUNIT_TEST_SUITE(ConverterTest);
CPPUNIT_TEST(testDuration);
CPPUNIT_TEST(testDateTime);
+ CPPUNIT_TEST(testTime);
CPPUNIT_TEST(testDouble);
CPPUNIT_TEST(testMeasure);
CPPUNIT_TEST(testBool);
@@ -240,7 +242,7 @@ void ConverterTest::testDateTime()
doTestDateTimeF( "0001-13-01T00:00:00" ); // invalid: M > 12
doTestDateTimeF( "0001-01-32T00:00:00" ); // invalid: D > 31
doTestDateTimeF( "0001-01-01T25:00:00" ); // invalid: H > 24
- doTestDateTimeF( "0001-01-01T00:60:00" ); // invalid: H > 59
+ doTestDateTimeF( "0001-01-01T00:60:00" ); // invalid: M > 59
doTestDateTimeF( "0001-01-01T00:00:60" ); // invalid: S > 59
doTestDateTimeF( "0001-01-01T24:01:00" ); // invalid: H=24, but M != 0
doTestDateTimeF( "0001-01-01T24:00:01" ); // invalid: H=24, but S != 0
@@ -251,9 +253,143 @@ void ConverterTest::testDateTime()
doTestDateTimeF( "0001-01-02T00:00:00-14:01" ); // invalid: TZ < -14:00
doTestDateTimeF( "2100-02-29T00:00:00-00:00" ); // invalid: no leap year
doTestDateTimeF( "1900-02-29T00:00:00-00:00" ); // invalid: no leap year
+ doTestDateTimeF( "00:00:00" ); // invalid: no date
+ doTestDateTimeF( "T00:00:00" ); // invalid: no date
SAL_INFO("sax.cppunit","\nSAX CONVERTER TEST END");
}
+static void doTestTime(util::DateTime const & rdt, char const*const pis,
+ char const*const i_pos = 0)
+{
+ char const*const pos((i_pos) ? i_pos : pis);
+ OUString is(OUString::createFromAscii(pis));
+ util::DateTime odt;
+ SAL_INFO("sax.cppunit","about to convert '" << is << "'");
+ bool bSuccess( Converter::parseTimeOrDateTime(odt, 0, is) );
+ 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);
+ CPPUNIT_ASSERT(bSuccess);
+ CPPUNIT_ASSERT(eqDateTime(rdt, odt));
+ OUStringBuffer buf;
+ Converter::convertTimeOrDateTime(buf, odt, 0);
+ SAL_INFO("sax.cppunit","" << buf.toString());
+ CPPUNIT_ASSERT_EQUAL(OUString::createFromAscii(pos),
+ buf.makeStringAndClear());
+}
+
+static void doTestTimeF(char const*const pis)
+{
+ util::DateTime odt;
+ bool bSuccess = Converter::parseTimeOrDateTime(odt, 0,
+ OUString::createFromAscii(pis));
+ 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);
+ CPPUNIT_ASSERT(!bSuccess);
+}
+
+void ConverterTest::testTime() // time or dateTime + horrible backcompat mess
+{
+ doTestTime( util::DateTime(0, 0, 0, 0, 1, 1, 1, false),
+ "0001-01-01T00:00:00" );
+ doTestTime( util::DateTime(0, 0, 0, 0, 1, 1, 1, false),
+ "0001-01-01T00:00:00" );
+ doTestTime( util::DateTime(0, 0, 0, 0, 1, 1, 1, true),
+ "0001-01-01T00:00:00Z" );
+ doTestTime( util::DateTime(0, 0, 0, 0, 1, 1, -1, false),
+ "-0001-01-01T00:00:00");
+ doTestTime( util::DateTime(0, 0, 0, 0, 1, 1, -1, true),
+ "-0001-01-01T01:00:00+01:00", "-0001-01-01T00:00:00Z");
+ doTestTime( util::DateTime(0, 0, 0, 0, 1, 1, -324, false),
+ "-0324-01-01T00:00:00" );
+ doTestTime( util::DateTime(0, 0, 0, 0, 1, 1, 1, true),
+ "0001-01-01T00:00:00-00:00", "0001-01-01T00:00:00Z" );
+ doTestTime( util::DateTime(0, 0, 0, 0, 1, 1, 1, true),
+ "0001-01-01T00:00:00+00:00", "0001-01-01T00:00:00Z" );
+ doTestTime( util::DateTime(0, 0, 0, 12, 2, 1, 1, true),
+ "0001-01-02T00:00:00-12:00", "0001-01-02T12:00:00Z" );
+ doTestTime( util::DateTime(0, 0, 0, 12, 1, 1, 1, true),
+ "0001-01-02T00:00:00+12:00", "0001-01-01T12:00:00Z" );
+ doTestTime( util::DateTime(990000000, 59, 59, 23, 31, 12, 9999, false),
+ "9999-12-31T23:59:59.99", "9999-12-31T23:59:59.990000000" );
+ doTestTime( util::DateTime(990000000, 59, 59, 23, 31, 12, 9999, true),
+ "9999-12-31T23:59:59.99Z", "9999-12-31T23:59:59.990000000Z" );
+ doTestTime( util::DateTime(999999999, 59, 59, 23, 31, 12, 9999, false),
+ "9999-12-31T23:59:59.9999999999999999999999999999999999999",
+ "9999-12-31T23:59:59.999999999" );
+ doTestTime( util::DateTime(999999999, 59, 59, 23, 31, 12, 9999, true),
+ "9999-12-31T23:59:59.9999999999999999999999999999999999999Z",
+ "9999-12-31T23:59:59.999999999Z" );
+ doTestTime( util::DateTime(0, 0, 0, 0, 29, 2, 2000, true), // leap year
+ "2000-02-29T00:00:00-00:00", "2000-02-29T00:00:00Z" );
+ doTestTime( util::DateTime(0, 0, 0, 0, 29, 2, 1600, true), // leap year
+ "1600-02-29T00:00:00-00:00", "1600-02-29T00:00:00Z" );
+ doTestTime( util::DateTime(0, 0, 0, 24, 1, 1, 333, false)
+ /*(0, 0, 0, 0, 2, 1, 333)*/,
+ "0333-01-01T24:00:00"/*, "0333-01-02T00:00:00"*/ );
+ // While W3C XMLSchema specifies a minimum of 4 year digits we are lenient
+ // in what we accept.
+ doTestTime( util::DateTime(0, 0, 0, 0, 1, 1, 1, false),
+ "1-01-01T00:00:00", "0001-01-01T00:00:00" );
+
+ doTestTime( util::DateTime(0, 0, 0, 0, 0, 0, 0, false), "00:00:00" );
+ doTestTime( util::DateTime(0, 0, 0, 24, 0, 0, 0, false), "24:00:00" );
+ doTestTime( util::DateTime(0, 0, 59, 0, 0, 0, 0, false), "00:59:00" );
+ doTestTime( util::DateTime(0, 1, 2, 4, 0, 0, 0, true), "04:02:01Z" );
+ doTestTime( util::DateTime(0, 1, 2, 4, 0, 0, 0, true),
+ "05:02:01+01:00", "04:02:01Z" );
+ doTestTime( util::DateTime(0, 11, 12, 9, 0, 0, 0, true),
+ "05:12:11-04:00", "09:12:11Z" );
+ doTestTime( util::DateTime(990000000, 59, 59, 23, 0, 0, 0, false),
+ "23:59:59.99", "23:59:59.990000000" );
+ doTestTime( util::DateTime(990000000, 59, 59, 23, 0, 0, 0, true),
+ "23:59:59.99Z", "23:59:59.990000000Z" );
+ // backwards compatible: recognize invalid 0000-00-00 date (LO 3.5)
+ doTestTime( util::DateTime(0, 1, 0, 0, 0, 0, 0, false),
+ "0000-00-00T00:00:01", "00:00:01" );
+ // backwards compatible: recognize invalid 0-00-00 date (OOo)
+ doTestTime( util::DateTime(0, 0, 1, 0, 0, 0, 0, false),
+ "0-00-00T00:01:00", "00:01:00" );
+
+ doTestTimeF( "+0001-01-01T00:00:00" ); // invalid: ^+
+ doTestTimeF( "0001-1-01T00:00:00" ); // invalid: < 2 M
+ doTestTimeF( "0001-01-1T00:00:00" ); // invalid: < 2 D
+ doTestTimeF( "0001-01-01T0:00:00" ); // invalid: < 2 H
+ doTestTimeF( "0001-01-01T00:0:00" ); // invalid: < 2 M
+ doTestTimeF( "0001-01-01T00:00:0" ); // invalid: < 2 S
+ doTestTimeF( "0001-01-01T00:00:00." ); // invalid: .$
+ doTestTimeF( "0001-01-01T00:00:00+1:00" ); // invalid: < 2 TZ H
+ doTestTimeF( "0001-01-01T00:00:00+00:1" ); // invalid: < 2 TZ M
+ doTestTimeF( "0001-13-01T00:00:00" ); // invalid: M > 12
+ doTestTimeF( "0001-01-32T00:00:00" ); // invalid: D > 31
+ doTestTimeF( "0001-01-01T25:00:00" ); // invalid: H > 24
+ doTestTimeF( "0001-01-01T00:60:00" ); // invalid: M > 59
+ doTestTimeF( "0001-01-01T00:00:60" ); // invalid: S > 59
+ doTestTimeF( "0001-01-01T24:01:00" ); // invalid: H=24, but M != 0
+ doTestTimeF( "0001-01-01T24:00:01" ); // invalid: H=24, but S != 0
+ doTestTimeF( "0001-01-01T24:00:00.1" ); // invalid: H=24, but H != 0
+ doTestTimeF( "0001-01-02T00:00:00+15:00" ); // invalid: TZ > +14:00
+ doTestTimeF( "0001-01-02T00:00:00+14:01" ); // invalid: TZ > +14:00
+ doTestTimeF( "0001-01-02T00:00:00-15:00" ); // invalid: TZ < -14:00
+ doTestTimeF( "0001-01-02T00:00:00-14:01" ); // invalid: TZ < -14:00
+ doTestTimeF( "2100-02-29T00:00:00-00:00" ); // invalid: no leap year
+ doTestTimeF( "1900-02-29T00:00:00-00:00" ); // invalid: no leap year
+ doTestTimeF( "T00:00:00" ); // invalid: T
+ doTestTimeF( "0:00:00" ); // invalid: < 2 H
+ doTestTimeF( "00:0:00" ); // invalid: < 2 M
+ doTestTimeF( "00:00:0" ); // invalid: < 2 S
+ doTestTimeF( "00:00:00." ); // invalid: .$
+ doTestTimeF( "00:00:00+1:00" ); // invalid: < 2 TZ H
+ doTestTimeF( "00:00:00+00:1" ); // invalid: < 2 TZ M
+ doTestTimeF( "25:00:00" ); // invalid: H > 24
+ doTestTimeF( "00:60:00" ); // invalid: M > 59
+ doTestTimeF( "00:00:60" ); // invalid: S > 59
+ doTestTimeF( "24:01:00" ); // invalid: H=24, but M != 0
+ doTestTimeF( "24:00:01" ); // invalid: H=24, but S != 0
+ doTestTimeF( "24:00:00.1" ); // invalid: H=24, but H != 0
+ doTestTimeF( "00:00:00+15:00" ); // invalid: TZ > +14:00
+ doTestTimeF( "00:00:00+14:01" ); // invalid: TZ > +14:00
+ doTestTimeF( "00:00:00-15:00" ); // invalid: TZ < -14:00
+ doTestTimeF( "00:00:00-14:01" ); // invalid: TZ < -14:00
+}
+
void doTestDouble(char const*const pis, double const rd,
sal_Int16 const nSourceUnit, sal_Int16 const nTargetUnit)
{
diff --git a/sax/source/tools/converter.cxx b/sax/source/tools/converter.cxx
index a2b3dd0af9fd..8823965bf75a 100644
--- a/sax/source/tools/converter.cxx
+++ b/sax/source/tools/converter.cxx
@@ -1234,6 +1234,69 @@ void Converter::convertDate(
convertDateTime(i_rBuffer, dt, pTimeZoneOffset, false);
}
+static void convertTime(
+ OUStringBuffer& i_rBuffer,
+ const com::sun::star::util::DateTime& i_rDateTime)
+{
+ if (i_rDateTime.Hours < 10) {
+ i_rBuffer.append(sal_Unicode('0'));
+ }
+ i_rBuffer.append( static_cast<sal_Int32>(i_rDateTime.Hours) )
+ .append(sal_Unicode(':'));
+ if (i_rDateTime.Minutes < 10) {
+ i_rBuffer.append(sal_Unicode('0'));
+ }
+ i_rBuffer.append( static_cast<sal_Int32>(i_rDateTime.Minutes) )
+ .append(sal_Unicode(':'));
+ if (i_rDateTime.Seconds < 10) {
+ i_rBuffer.append(sal_Unicode('0'));
+ }
+ i_rBuffer.append( static_cast<sal_Int32>(i_rDateTime.Seconds) );
+ if (i_rDateTime.NanoSeconds > 0) {
+ OSL_ENSURE(i_rDateTime.NanoSeconds < 1000000000,"NanoSeconds cannot be more than 999 999 999");
+ i_rBuffer.append(sal_Unicode('.'));
+ std::ostringstream ostr;
+ ostr.fill('0');
+ ostr.width(9);
+ ostr << i_rDateTime.NanoSeconds;
+ i_rBuffer.append(OUString::createFromAscii(ostr.str().c_str()));
+ }
+}
+
+static void convertTimeZone(
+ OUStringBuffer& i_rBuffer,
+ const com::sun::star::util::DateTime& i_rDateTime,
+ sal_Int16 const* pTimeZoneOffset)
+{
+ if (pTimeZoneOffset)
+ {
+ lcl_AppendTimezone(i_rBuffer, *pTimeZoneOffset);
+ }
+ else if (i_rDateTime.IsUTC)
+ {
+ lcl_AppendTimezone(i_rBuffer, 0);
+ }
+}
+
+/** convert util::DateTime to ISO "time" or "dateTime" string */
+void Converter::convertTimeOrDateTime(
+ OUStringBuffer& i_rBuffer,
+ const com::sun::star::util::DateTime& i_rDateTime,
+ sal_Int16 const* pTimeZoneOffset)
+{
+ if (i_rDateTime.Year == 0 ||
+ i_rDateTime.Month < 1 || i_rDateTime.Month > 12 ||
+ i_rDateTime.Day < 1 || i_rDateTime.Day > 31)
+ {
+ convertTime(i_rBuffer, i_rDateTime);
+ convertTimeZone(i_rBuffer, i_rDateTime, pTimeZoneOffset);
+ }
+ else
+ {
+ convertDateTime(i_rBuffer, i_rDateTime, pTimeZoneOffset, true);
+ }
+}
+
/** convert util::DateTime to ISO "date" or "dateTime" string */
void Converter::convertDateTime(
OUStringBuffer& i_rBuffer,
@@ -1242,10 +1305,7 @@ void Converter::convertDateTime(
bool i_bAddTimeIf0AM )
{
const sal_Unicode dash('-');
- const sal_Unicode col (':');
- const sal_Unicode dot ('.');
const sal_Unicode zero('0');
- const sal_Unicode tee ('T');
sal_Int32 const nYear(abs(i_rDateTime.Year));
if (i_rDateTime.Year < 0) {
@@ -1275,41 +1335,11 @@ void Converter::convertDateTime(
i_rDateTime.Hours != 0 ||
i_bAddTimeIf0AM )
{
- i_rBuffer.append(tee);
- if( i_rDateTime.Hours < 10 ) {
- i_rBuffer.append(zero);
- }
- i_rBuffer.append( static_cast<sal_Int32>(i_rDateTime.Hours) )
- .append(col);
- if( i_rDateTime.Minutes < 10 ) {
- i_rBuffer.append(zero);
- }
- i_rBuffer.append( static_cast<sal_Int32>(i_rDateTime.Minutes) )
- .append(col);
- if( i_rDateTime.Seconds < 10 ) {
- i_rBuffer.append(zero);
- }
- i_rBuffer.append( static_cast<sal_Int32>(i_rDateTime.Seconds) );
- if( i_rDateTime.NanoSeconds > 0 ) {
- OSL_ENSURE(i_rDateTime.NanoSeconds < 1000000000,"NanoSeconds cannot be more than 999 999 999");
- i_rBuffer.append(dot);
- std::ostringstream ostr;
- ostr.fill('0');
- ostr.width(9);
- ostr << i_rDateTime.NanoSeconds;
- i_rBuffer.append(OUString::createFromAscii(ostr.str().c_str()));
- }
+ i_rBuffer.append(sal_Unicode('T'));
+ convertTime(i_rBuffer, i_rDateTime);
}
- if (pTimeZoneOffset)
- {
- lcl_AppendTimezone(i_rBuffer, *pTimeZoneOffset);
- }
- else if (i_rDateTime.IsUTC)
- {
- // append local time
- lcl_AppendTimezone(i_rBuffer, 0);
- }
+ convertTimeZone(i_rBuffer, i_rDateTime, pTimeZoneOffset);
}
/** convert ISO "date" or "dateTime" string to util::DateTime */
@@ -1362,11 +1392,17 @@ static void lcl_ConvertToUTC(
{
return;
}
+ sal_Int16 nDayAdd(0);
while (24 <= o_rHours)
{
o_rHours -= 24;
- ++o_rDay;
+ ++nDayAdd;
+ }
+ if (o_rDay == 0)
+ {
+ return; // handle time without date - don't adjust what isn't there
}
+ o_rDay += nDayAdd;
sal_Int16 const nDaysInMonth(lcl_MaxDaysPerMonth(o_rMonth, o_rYear));
if (o_rDay <= nDaysInMonth)
{
@@ -1397,6 +1433,10 @@ static void lcl_ConvertToUTC(
++nDaySubtract;
}
o_rHours -= nOffsetHours;
+ if (o_rDay == 0)
+ {
+ return; // handle time without date - don't adjust what isn't there
+ }
if (nDaySubtract < o_rDay)
{
o_rDay -= nDaySubtract;
@@ -1436,20 +1476,17 @@ readDateTimeComponent(const OUString & rString,
return true;
}
-
-
/** convert ISO "date" or "dateTime" string to util::DateTime or util::Date */
-bool Converter::parseDateOrDateTime(
- util::Date *const pDate, util::DateTime & rDateTime,
- bool & rbDateTime,
- boost::optional<sal_Int16> *const pTimeZoneOffset,
- const OUString & rString )
+static bool lcl_parseDate(
+ bool & isNegative,
+ sal_Int32 & nYear, sal_Int32 & nMonth, sal_Int32 & nDay,
+ bool & bHaveTime,
+ sal_Int32 & nPos,
+ const OUString & string,
+ bool const bIgnoreInvalidOrMissingDate)
{
bool bSuccess = true;
- bool isNegative(false);
- const OUString string = rString.trim().toAsciiUpperCase();
- sal_Int32 nPos(0);
if (string.getLength() > nPos)
{
if ('-' == string[nPos])
@@ -1459,13 +1496,15 @@ bool Converter::parseDateOrDateTime(
}
}
- sal_Int32 nYear(0);
{
// While W3C XMLSchema specifies years with a minimum of 4 digits, be
// leninent in what we accept for years < 1000. One digit is acceptable
// if the remainders match.
bSuccess = readDateTimeComponent(string, nPos, nYear, 1, false);
- bSuccess &= (0 < nYear);
+ if (!bIgnoreInvalidOrMissingDate)
+ {
+ bSuccess &= (0 < nYear);
+ }
bSuccess &= (nPos < string.getLength()); // not last token
}
if (bSuccess && ('-' != string[nPos])) // separator
@@ -1477,11 +1516,14 @@ bool Converter::parseDateOrDateTime(
++nPos;
}
- sal_Int32 nMonth(0);
if (bSuccess)
{
bSuccess = readDateTimeComponent(string, nPos, nMonth, 2, true);
- bSuccess &= (0 < nMonth) && (nMonth <= 12);
+ if (!bIgnoreInvalidOrMissingDate)
+ {
+ bSuccess &= (0 < nMonth);
+ }
+ bSuccess &= (nMonth <= 12);
bSuccess &= (nPos < string.getLength()); // not last token
}
if (bSuccess && ('-' != string[nPos])) // separator
@@ -1493,14 +1535,16 @@ bool Converter::parseDateOrDateTime(
++nPos;
}
- sal_Int32 nDay(0);
if (bSuccess)
{
bSuccess = readDateTimeComponent(string, nPos, nDay, 2, true);
- bSuccess &= (0 < nDay) && (nDay <= lcl_MaxDaysPerMonth(nMonth, nYear));
+ if (!bIgnoreInvalidOrMissingDate)
+ {
+ bSuccess &= (0 < nDay);
+ }
+ bSuccess &= (nDay <= lcl_MaxDaysPerMonth(nMonth, nYear));
}
- bool bHaveTime(false);
if (bSuccess && (nPos < string.getLength()))
{
if ('T' == string[nPos]) // time separator
@@ -1510,6 +1554,41 @@ bool Converter::parseDateOrDateTime(
}
}
+ return bSuccess;
+}
+
+/** convert ISO "date" or "dateTime" string to util::DateTime or util::Date */
+static bool lcl_parseDateTime(
+ util::Date *const pDate, util::DateTime & rDateTime,
+ bool & rbDateTime,
+ boost::optional<sal_Int16> *const pTimeZoneOffset,
+ const OUString & rString,
+ bool const bIgnoreInvalidOrMissingDate)
+{
+ bool bSuccess = true;
+
+ const OUString string = rString.trim().toAsciiUpperCase();
+
+ bool isNegative(false);
+ sal_Int32 nYear(0);
+ sal_Int32 nMonth(0);
+ sal_Int32 nDay(0);
+ sal_Int32 nPos(0);
+ bool bHaveTime(false);
+
+ if ( !bIgnoreInvalidOrMissingDate
+ || string.indexOf(':') == -1 // no time?
+ || (string.indexOf('-') != -1
+ && string.indexOf('-') < string.indexOf(':')))
+ {
+ bSuccess &= lcl_parseDate(isNegative, nYear, nMonth, nDay,
+ bHaveTime, nPos, string, bIgnoreInvalidOrMissingDate);
+ }
+ else
+ {
+ bHaveTime = true;
+ }
+
sal_Int32 nHours(0);
sal_Int32 nMinutes(0);
sal_Int32 nSeconds(0);
@@ -1708,6 +1787,28 @@ bool Converter::parseDateOrDateTime(
return bSuccess;
}
+/** convert ISO "time" or "dateTime" string to util::DateTime */
+bool Converter::parseTimeOrDateTime(
+ util::DateTime & rDateTime,
+ boost::optional<sal_Int16> * pTimeZoneOffset,
+ const OUString & rString)
+{
+ bool dummy;
+ return lcl_parseDateTime(
+ 0, rDateTime, dummy, pTimeZoneOffset, rString, true);
+}
+
+/** convert ISO "date" or "dateTime" string to util::DateTime or util::Date */
+bool Converter::parseDateOrDateTime(
+ util::Date *const pDate, util::DateTime & rDateTime,
+ bool & rbDateTime,
+ boost::optional<sal_Int16> *const pTimeZoneOffset,
+ const OUString & rString )
+{
+ return lcl_parseDateTime(
+ pDate, rDateTime, rbDateTime, pTimeZoneOffset, rString, false);
+}
+
/** gets the position of the first comma after npos in the string
rStr. Commas inside '"' pairs are not matched */