diff options
author | Michael Stahl <mstahl@redhat.com> | 2013-06-28 00:43:19 +0200 |
---|---|---|
committer | Eike Rathke <erack@redhat.com> | 2013-07-15 11:46:41 +0000 |
commit | 652ccbdf3111766fadc379a8cf4650b744e1e19c (patch) | |
tree | 5efdab367c4da4ac6c62ad1e228b77648cc62aed /sax | |
parent | a8e8d46020702c64ff314adbe87e6f21e73e1999 (diff) |
i#108348 API CHANGE: add IsUTC to css.util.DateTime etc.
Add IsUTC member to:
com.sun.star.util.DateTime
com.sun.star.util.DateTimeRange
com.sun.star.util.Time
Add new stucts with explicit time zones:
com.sun.star.util.DateTimeWithTimezone
com.sun.star.util.DateWithTimezone
com.sun.star.util.TimeWithTimezone
Adapt the sax::Converter to read/write timezones, and fix the unit test.
Everything else just uses default (no time zone), this commit is just
to fix the API.
STRUCT: /UCR/com/sun/star/util/DateTime
nFields1 = 7 != nFields2 = 8
Registry2 contains 1 more fields
STRUCT: /UCR/com/sun/star/util/DateTimeRange
nFields1 = 14 != nFields2 = 15
Registry2 contains 1 more fields
STRUCT: /UCR/com/sun/star/util/Time
nFields1 = 4 != nFields2 = 5
Registry2 contains 1 more fields
Conflicts:
sc/source/filter/oox/unitconverter.cxx
Change-Id: I01f7a6d082a6b090c8efe71d2de137474c495c18
Reviewed-on: https://gerrit.libreoffice.org/4833
Reviewed-by: Eike Rathke <erack@redhat.com>
Tested-by: Eike Rathke <erack@redhat.com>
Diffstat (limited to 'sax')
-rw-r--r-- | sax/qa/cppunit/test_converter.cxx | 63 | ||||
-rw-r--r-- | sax/source/tools/converter.cxx | 204 |
2 files changed, 212 insertions, 55 deletions
diff --git a/sax/qa/cppunit/test_converter.cxx b/sax/qa/cppunit/test_converter.cxx index 8a5f72826a92..cfda248adf6c 100644 --- a/sax/qa/cppunit/test_converter.cxx +++ b/sax/qa/cppunit/test_converter.cxx @@ -157,7 +157,8 @@ static bool eqDateTime(util::DateTime a, util::DateTime b) { return a.Year == b.Year && a.Month == b.Month && a.Day == b.Day && a.Hours == b.Hours && a.Minutes == b.Minutes && a.Seconds == b.Seconds - && a.NanoSeconds == b.NanoSeconds; + && a.NanoSeconds == b.NanoSeconds + && a.IsUTC == b.IsUTC; } static void doTest(util::DateTime const & rdt, char const*const pis, @@ -168,13 +169,14 @@ static void doTest(util::DateTime const & rdt, char const*const pis, util::DateTime odt; SAL_INFO("sax.cppunit","about to convert '" << is << "'"); bool bSuccess( Converter::convertDateTime(odt, 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); + 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::convertDateTime(buf, odt, true); SAL_INFO("sax.cppunit","" << buf.getStr()); - CPPUNIT_ASSERT(buf.makeStringAndClear().equalsAscii(pos)); + CPPUNIT_ASSERT_EQUAL(OUString::createFromAscii(pos), + buf.makeStringAndClear()); } static void doTestDateTimeF(char const*const pis) @@ -189,43 +191,42 @@ static void doTestDateTimeF(char const*const pis) void ConverterTest::testDateTime() { SAL_INFO("sax.cppunit","\nSAX CONVERTER TEST BEGIN"); - doTest( util::DateTime(0, 0, 0, 0, 1, 1, 1), "0001-01-01T00:00:00" ); - doTest( util::DateTime(0, 0, 0, 0, 1, 1, 1), - "0001-01-01T00:00:00Z", "0001-01-01T00:00:00" ); - doTest( util::DateTime(0, 0, 0, 0, 1, 1, -1), "-0001-01-01T00:00:00"); -// doTest( util::DateTime(0, 0, 0, 0, 1, 1, -1), "-0001-01-01T00:00:00Z"); - doTest( util::DateTime(0, 0, 0, 0, 1, 1, -324), + doTest( util::DateTime(0, 0, 0, 0, 1, 1, 1, false), "0001-01-01T00:00:00" ); + doTest( util::DateTime(0, 0, 0, 0, 1, 1, 1, true), "0001-01-01T00:00:00Z" ); + doTest( util::DateTime(0, 0, 0, 0, 1, 1, -1, false), + "-0001-01-01T00:00:00"); + doTest( util::DateTime(0, 0, 0, 0, 1, 1, -1, true), + "-0001-01-01T01:00:00+01:00", "-0001-01-01T00:00:00Z"); + doTest( util::DateTime(0, 0, 0, 0, 1, 1, -324, false), "-0324-01-01T00:00:00" ); - doTest( util::DateTime(0, 0, 0, 0, 1, 1, 1), - "0001-01-01T00:00:00-00:00", "0001-01-01T00:00:00" ); - doTest( util::DateTime(0, 0, 0, 0, 1, 1, 1), - "0001-01-01T00:00:00+00:00", "0001-01-01T00:00:00" ); - doTest( util::DateTime(0, 0, 0, 0, 2, 1, 1)/*(0, 0, 12, 0, 2, 1, 1)*/, - "0001-01-02T00:00:00-12:00", "0001-01-02T00:00:00" ); -// "0001-02-01T12:00:00" ); - doTest( util::DateTime(0, 0, 0, 0, 2, 1, 1)/*(0, 0, 12, 0, 1, 1, 1)*/, - "0001-01-02T00:00:00+12:00", "0001-01-02T00:00:00" ); -// "0001-01-01T12:00:00" ); - doTest( util::DateTime(990000000, 59, 59, 23, 31, 12, 9999), + doTest( util::DateTime(0, 0, 0, 0, 1, 1, 1, true), + "0001-01-01T00:00:00-00:00", "0001-01-01T00:00:00Z" ); + doTest( util::DateTime(0, 0, 0, 0, 1, 1, 1, true), + "0001-01-01T00:00:00+00:00", "0001-01-01T00:00:00Z" ); + doTest( util::DateTime(0, 0, 0, 12, 2, 1, 1, true), + "0001-01-02T00:00:00-12:00", "0001-01-02T12:00:00Z" ); + doTest( util::DateTime(0, 0, 0, 12, 1, 1, 1, true), + "0001-01-02T00:00:00+12:00", "0001-01-01T12:00:00Z" ); + doTest( util::DateTime(990000000, 59, 59, 23, 31, 12, 9999, false), "9999-12-31T23:59:59.99", "9999-12-31T23:59:59.990000000" ); - doTest( util::DateTime(990000000, 59, 59, 23, 31, 12, 9999), - "9999-12-31T23:59:59.99Z", "9999-12-31T23:59:59.990000000" ); - doTest( util::DateTime(999999999, 59, 59, 23, 31, 12, 9999), + doTest( util::DateTime(990000000, 59, 59, 23, 31, 12, 9999, true), + "9999-12-31T23:59:59.99Z", "9999-12-31T23:59:59.990000000Z" ); + doTest( util::DateTime(999999999, 59, 59, 23, 31, 12, 9999, false), "9999-12-31T23:59:59.9999999999999999999999999999999999999", "9999-12-31T23:59:59.999999999" ); - doTest( util::DateTime(999999999, 59, 59, 23, 31, 12, 9999), + doTest( util::DateTime(999999999, 59, 59, 23, 31, 12, 9999, true), "9999-12-31T23:59:59.9999999999999999999999999999999999999Z", - "9999-12-31T23:59:59.999999999" ); - doTest( util::DateTime(0, 0, 0, 0, 29, 2, 2000), // leap year - "2000-02-29T00:00:00-00:00", "2000-02-29T00:00:00" ); - doTest( util::DateTime(0, 0, 0, 0, 29, 2, 1600), // leap year - "1600-02-29T00:00:00-00:00", "1600-02-29T00:00:00" ); - doTest( util::DateTime(0, 0, 0, 24, 1, 1, 333) + "9999-12-31T23:59:59.999999999Z" ); + doTest( util::DateTime(0, 0, 0, 0, 29, 2, 2000, true), // leap year + "2000-02-29T00:00:00-00:00", "2000-02-29T00:00:00Z" ); + doTest( util::DateTime(0, 0, 0, 0, 29, 2, 1600, true), // leap year + "1600-02-29T00:00:00-00:00", "1600-02-29T00:00:00Z" ); + doTest( 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. - doTest( util::DateTime(0, 0, 0, 0, 1, 1, 1), + doTest( util::DateTime(0, 0, 0, 0, 1, 1, 1, false), "1-01-01T00:00:00", "0001-01-01T00:00:00" ); doTestDateTimeF( "+0001-01-01T00:00:00" ); // invalid: ^+ doTestDateTimeF( "0001-1-01T00:00:00" ); // invalid: < 2 M diff --git a/sax/source/tools/converter.cxx b/sax/source/tools/converter.cxx index 690c3e581c51..e99690e91546 100644 --- a/sax/source/tools/converter.cxx +++ b/sax/source/tools/converter.cxx @@ -28,6 +28,7 @@ #include <rtl/ustrbuf.hxx> #include <rtl/math.hxx> +#include <osl/time.h> #include <algorithm> @@ -1185,13 +1186,48 @@ bool Converter::convertDuration(util::Duration& rDuration, } +static void +lcl_AppendTimezone(OUStringBuffer & i_rBuffer, sal_Int16 const nOffset) +{ + if (0 == nOffset) + { + i_rBuffer.append(sal_Unicode('Z')); + } + else + { + if (0 < nOffset) + { + i_rBuffer.append(sal_Unicode('+')); + } + else + { + i_rBuffer.append(sal_Unicode('-')); + } + const sal_Int32 nHours (abs(nOffset) / 60); + const sal_Int32 nMinutes(abs(nOffset) % 60); + SAL_WARN_IF(nHours > 14 || (nHours == 14 && nMinutes > 0), + "sax", "convertDateTime: timezone overflow"); + if (nHours < 10) + { + i_rBuffer.append('0'); + } + i_rBuffer.append(nHours); + i_rBuffer.append(':'); + if (nMinutes < 10) + { + i_rBuffer.append('0'); + } + i_rBuffer.append(nMinutes); + } +} + /** convert util::Date to ISO "date" string */ void Converter::convertDate( OUStringBuffer& i_rBuffer, const util::Date& i_rDate) { - const util::DateTime dt( - 0, 0, 0, 0, i_rDate.Day, i_rDate.Month, i_rDate.Year); + const util::DateTime dt(0, 0, 0, 0, + i_rDate.Day, i_rDate.Month, i_rDate.Year, false); convertDateTime(i_rBuffer, dt, false); } @@ -1260,6 +1296,17 @@ void Converter::convertDateTime( i_rBuffer.append(OUString::createFromAscii(ostr.str().c_str())); } } + + sal_uInt16 * pTimezone(0); // FIXME pass this as parameter + if (pTimezone) + { + lcl_AppendTimezone(i_rBuffer, *pTimezone); + } + else if (i_rDateTime.IsUTC) + { + // append local time + lcl_AppendTimezone(i_rBuffer, 0); + } } /** convert ISO "date" or "dateTime" string to util::DateTime */ @@ -1279,6 +1326,10 @@ bool Converter::convertDateTime( util::DateTime& rDateTime, rDateTime.Minutes = 0; rDateTime.Seconds = 0; rDateTime.NanoSeconds = 0; + // FIXME +#if 0 + rDateTime.IsUTC = date.IsUTC; +#endif } return true; } @@ -1288,6 +1339,99 @@ bool Converter::convertDateTime( util::DateTime& rDateTime, } } +static bool lcl_isLeapYear(const sal_uInt32 nYear) +{ + return ((nYear % 4) == 0) + && (((nYear % 100) != 0) || ((nYear % 400) == 0)); +} + +static sal_uInt16 +lcl_MaxDaysPerMonth(const sal_Int32 nMonth, const sal_Int32 nYear) +{ + static sal_uInt16 s_MaxDaysPerMonth[12] = + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + OSL_ASSERT(0 < nMonth && nMonth <= 12); + if ((2 == nMonth) && lcl_isLeapYear(nYear)) + { + return 29; + } + return s_MaxDaysPerMonth[nMonth - 1]; +} + +static void lcl_ConvertToUTC( + sal_Int16 & o_rYear, sal_uInt16 & o_rMonth, sal_uInt16 & o_rDay, + sal_uInt16 & o_rHours, sal_uInt16 & o_rMinutes, + sal_Int16 const nSourceOffset) +{ + sal_Int16 nOffsetHours(abs(nSourceOffset) / 60); + sal_Int16 const nOffsetMinutes(abs(nSourceOffset) % 60); + o_rMinutes += nOffsetMinutes; + if (nSourceOffset < 0) + { + o_rMinutes += nOffsetMinutes; + if (60 <= o_rMinutes) + { + o_rMinutes -= 60; + ++nOffsetHours; + } + o_rHours += nOffsetHours; + if (o_rHours < 24) + { + return; + } + while (24 <= o_rHours) + { + o_rHours -= 24; + ++o_rDay; + } + sal_Int16 const nDaysInMonth(lcl_MaxDaysPerMonth(o_rMonth, o_rYear)); + if (o_rDay <= nDaysInMonth) + { + return; + } + o_rDay -= nDaysInMonth; + ++o_rMonth; + if (o_rMonth <= 12) + { + return; + } + o_rMonth = 1; + ++o_rYear; // works for negative year too + } + else if (0 < nSourceOffset) + { + // argh everything is unsigned + if (o_rMinutes < nOffsetMinutes) + { + o_rMinutes += 60; + ++nOffsetHours; + } + o_rMinutes -= nOffsetMinutes; + sal_Int16 nDaySubtract(0); + while (o_rHours < nOffsetHours) + { + o_rHours += 24; + ++nDaySubtract; + } + o_rHours -= nOffsetHours; + if (nDaySubtract < o_rDay) + { + o_rDay -= nDaySubtract; + return; + } + sal_Int16 const nPrevMonth((o_rMonth == 1) ? 12 : o_rMonth - 1); + sal_Int16 const nDaysInMonth(lcl_MaxDaysPerMonth(nPrevMonth, o_rYear)); + o_rDay += nDaysInMonth; + --o_rMonth; + if (0 == o_rMonth) + { + o_rMonth = 12; + --o_rYear; // works for negative year too + } + o_rDay -= nDaySubtract; + } +} + static bool readDateTimeComponent(const OUString & rString, sal_Int32 & io_rnPos, sal_Int32 & o_rnTarget, @@ -1309,24 +1453,7 @@ readDateTimeComponent(const OUString & rString, return true; } -static bool lcl_isLeapYear(const sal_uInt32 nYear) -{ - return ((nYear % 4) == 0) - && (((nYear % 100) != 0) || ((nYear % 400) == 0)); -} -static sal_uInt16 -lcl_MaxDaysPerMonth(const sal_Int32 nMonth, const sal_Int32 nYear) -{ - static sal_uInt16 s_MaxDaysPerMonth[12] = - { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; - OSL_ASSERT(0 < nMonth && nMonth <= 12); - if ((2 == nMonth) && lcl_isLeapYear(nYear)) - { - return 29; - } - return s_MaxDaysPerMonth[nMonth - 1]; -} /** convert ISO "date" or "dateTime" string to util::DateTime or util::Date */ bool Converter::convertDateOrDateTime( @@ -1526,13 +1653,11 @@ bool Converter::convertDateOrDateTime( bSuccess &= (nPos == string.getLength()); // trailing junk? - if (bSuccess && bHaveTimezone) - { - // util::DateTime does not support timezones! - } - if (bSuccess) { + sal_uInt16 * pTimezone(0); // FIXME pass this as parameter + sal_Int16 const nTimezoneOffset = ((bHaveTimezoneMinus) ? (-1) : (+1)) + * ((nTimezoneHours * 60) + nTimezoneMinutes); if (bHaveTime) // time is optional { rDateTime.Year = @@ -1543,6 +1668,25 @@ bool Converter::convertDateOrDateTime( rDateTime.Minutes = static_cast<sal_uInt16>(nMinutes); rDateTime.Seconds = static_cast<sal_uInt16>(nSeconds); rDateTime.NanoSeconds = static_cast<sal_uInt32>(nNanoSeconds); + if (bHaveTimezone) + { + if (pTimezone) + { + *pTimezone = nTimezoneOffset; + rDateTime.IsUTC = (0 == nTimezoneOffset); + } + else + { + lcl_ConvertToUTC(rDateTime.Year, rDateTime.Month, + rDateTime.Day, rDateTime.Hours, rDateTime.Minutes, + nTimezoneOffset); + rDateTime.IsUTC = true; + } + } + else + { + rDateTime.IsUTC = false; + } rbDateTime = true; } else @@ -1551,6 +1695,18 @@ bool Converter::convertDateOrDateTime( ((isNegative) ? (-1) : (+1)) * static_cast<sal_Int16>(nYear); rDate.Month = static_cast<sal_uInt16>(nMonth); rDate.Day = static_cast<sal_uInt16>(nDay); + if (bHaveTimezone) + { + if (pTimezone) + { + *pTimezone = nTimezoneOffset; + } + else + { + // a Date cannot be adjusted + SAL_INFO("sax", "dropping timezone"); + } + } rbDateTime = false; } } |