diff options
author | Michael Stahl <mstahl@redhat.com> | 2013-12-10 15:14:00 +0100 |
---|---|---|
committer | Michael Stahl <mstahl@redhat.com> | 2013-12-10 15:28:53 +0100 |
commit | cc407e50e8a1a74f9d1ed29d444dce9bd2e9167a (patch) | |
tree | c7ef90848446a9c961f694e0a2f772a17198319f /sax/source | |
parent | 1235e694c21f6e3e000e24a176123c386ea91df1 (diff) |
sax, xmloff: fix ODF import/export of text:time/text:time-value
The value written for an Impress time field is something like
text:time-value="0000-00-00T23:28:07" (in LO 3.5+) or
text:time-value="0-00-00T23:28:07" (in OOo 3.3) which contains an
invalid all-zero date. Such values are actually rejected by the
ODF import since commit ae3e2f170045a1525f67e9f3e9b7e03d94f2b56b.
Actually there was no real support to read the RelaxNG type
timeOrDateTime before.
So fix that by:
- adding convertTimeOrDateTime/parseTimeOrDateTime functions to
sax::Converter
- recognizing and ignoring the 2 invalid all-zero values written by
LO 3.5 and historic OOo respectively
- writing a bare "time" in text:time-value if the DateTime struct
contains zero Date members
(Older OOo versions and AOO cannot actually read that, but everything
they _can_ read is invalid ODF...)
Change-Id: I754076caee74a5163ed3f972af0f23796aa14f9f
Diffstat (limited to 'sax/source')
-rw-r--r-- | sax/source/tools/converter.cxx | 209 |
1 files changed, 155 insertions, 54 deletions
diff --git a/sax/source/tools/converter.cxx b/sax/source/tools/converter.cxx index c77e02aa402b..b5c9934636f4 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 */ |