diff options
author | Eike Rathke <erack@redhat.com> | 2018-08-31 11:21:03 +0200 |
---|---|---|
committer | Eike Rathke <erack@redhat.com> | 2018-08-31 12:50:52 +0200 |
commit | e2e47898180e547cad7ccde1e5890385d573e551 (patch) | |
tree | c91b4e1e053c69bbf0906953fe1bba276977ef2c | |
parent | 370d4c9ba34913076f7a73a2912eb2e0e48ca73c (diff) |
Use tools::Time::GetClock() in number formatter for wall clock time
Also handle rounding/scaling better in ImpGetTimeOutput() for the
[] duration formats, of which [HH]:MM:SS(.0000000) is used to edit
time values.
The wall clock change made it necessary to adapt some test cases in
Test::testUserDefinedNumberFormats() where M_PI formatted to
date+time actually is 1900-01-02 03:23:53.60527 with second 53
instead of the previously rounded 54.
Change-Id: I242a6c753a24281e041d3f73af019bdd77c65b37
Reviewed-on: https://gerrit.libreoffice.org/59857
Reviewed-by: Eike Rathke <erack@redhat.com>
Tested-by: Jenkins
-rw-r--r-- | include/svl/zformat.hxx | 9 | ||||
-rw-r--r-- | svl/qa/unit/svl.cxx | 22 | ||||
-rw-r--r-- | svl/source/numbers/zformat.cxx | 227 |
3 files changed, 146 insertions, 112 deletions
diff --git a/include/svl/zformat.hxx b/include/svl/zformat.hxx index 88b015f4adce..4f5dcb243ec5 100644 --- a/include/svl/zformat.hxx +++ b/include/svl/zformat.hxx @@ -700,6 +700,15 @@ private: return OUString::number(nVal); } + // Obtain the string of the fraction of second, without leading "0.", + // rounded to nFractionDecimals (or nFractionDecimals+1 if + // bAddOneRoundingDecimal==true but then truncated at nFractionDecimals, + // for use with the result of tools::Time::GetClock()) with the length of + // nFractionDecimals, unless nMinimumInputLineDecimals>0 is given for input + // line string where extra trailing "0" are discarded. + SVL_DLLPRIVATE sal_uInt16 ImpGetFractionOfSecondString( OUStringBuffer& rBuf, double fFractionOfSecond, + int nFractionDecimals, bool bAddOneRoundingDecimal, sal_uInt16 nIx, sal_uInt16 nMinimumInputLineDecimals ); + // transliterate according to NativeNumber SVL_DLLPRIVATE OUString impTransliterateImpl(const OUString& rStr, const SvNumberNatNum& rNum) const; SVL_DLLPRIVATE void impTransliterateImpl(OUStringBuffer& rStr, const SvNumberNatNum& rNum) const; diff --git a/svl/qa/unit/svl.cxx b/svl/qa/unit/svl.cxx index 68c4f15444a8..6824d15026fe 100644 --- a/svl/qa/unit/svl.cxx +++ b/svl/qa/unit/svl.cxx @@ -1285,12 +1285,12 @@ void Test::testUserDefinedNumberFormats() } { // tdf#95339: detect SSMM as second minute sCode = "SS:MM:HH DD/MM/YY"; // Month not detected by Excel, but we do not follow that. - sExpected = "54:23:03 02/01/00"; + sExpected = "53:23:03 02/01/00"; checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected); } { // tdf#101147: detect SSMM as second month sCode = "HH:MM:SS MM/DD"; - sExpected = "03:23:54 01/02"; + sExpected = "03:23:53 01/02"; checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected); } { // tdf#101096: different detection of month/minute with Excel @@ -1301,7 +1301,7 @@ void Test::testUserDefinedNumberFormats() sExpected = "03:23 03 02/01"; checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected); sCode = "SS:DD-MM-YY SS:MM"; // 1st is month, because of previous DD; 2nd is minute as SS has not minute - sExpected = "54:02-01-00 54:23"; + sExpected = "53:02-01-00 53:23"; checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected); } { // tdf#99996: better algorithm for fraction representation @@ -1475,49 +1475,49 @@ void Test::testUserDefinedNumberFormats() } { // tdf#33689 use English NfKeywords in non-English language eLang = LANGUAGE_DUTCH; - sExpected = "Dutch: 1900/01/02 03:23:54"; + sExpected = "Dutch: 1900/01/02 03:23:53"; sCode = "\"Dutch:\" JJJJ/MM/DD UU:MM:SS"; checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected); sCode = "\"Dutch: \"YYYY/MM/DD HH:MM:SS"; checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected); eLang = LANGUAGE_GERMAN; - sExpected = "German: 1900/01/02 03:23:54"; + sExpected = "German: 1900/01/02 03:23:53"; sCode = "\"German: \"JJJJ/MM/TT HH:MM:SS"; checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected); sCode = "\"German: \"YYYY/MM/DD HH:MM:SS"; checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected); eLang = LANGUAGE_FRENCH; - sExpected = "French: 1900/01/02 03:23:54"; + sExpected = "French: 1900/01/02 03:23:53"; sCode = "\"French: \"AAAA/MM/JJ HH:MM:SS"; checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected); sCode = "\"French: \"YYYY/MM/DD HH:MM:SS"; checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected); eLang = LANGUAGE_ITALIAN; - sExpected = "Italian: 1900/01/02 03:23:54"; + sExpected = "Italian: 1900/01/02 03:23:53"; sCode = "\"Italian: \"AAAA/MM/GG HH:MM:SS"; checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected); sCode = "\"Italian: \"YYYY/MM/DD HH:MM:SS"; checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected); eLang = LANGUAGE_PORTUGUESE; - sExpected = "Portuguese: 1900/01/02 03:23:54"; + sExpected = "Portuguese: 1900/01/02 03:23:53"; sCode = "\"Portuguese: \"AAAA/MM/DD HH:MM:SS"; checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected); sCode = "\"Portuguese: \"YYYY/MM/DD HH:MM:SS"; checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected); eLang = LANGUAGE_SPANISH_MODERN; - sExpected = "Spanish: 1900/01/02 03:23:54"; + sExpected = "Spanish: 1900/01/02 03:23:53"; sCode = "\"Spanish: \"AAAA/MM/DD HH:MM:SS"; checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected); sCode = "\"Spanish: \"YYYY/MM/DD HH:MM:SS"; checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected); eLang = LANGUAGE_DANISH; - sExpected = "Danish: 1900/01/02 03:23:54"; + sExpected = "Danish: 1900/01/02 03:23:53"; sCode = "\"Danish: \"YYYY/MM/DD TT:MM:SS"; checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected); sCode = "\"Danish: \"YYYY/MM/DD HH:MM:SS"; checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected); eLang = LANGUAGE_FINNISH; - sExpected = "Finnish: 1900/01/02 03:23:54"; + sExpected = "Finnish: 1900/01/02 03:23:53"; sCode = "\"Finnish: \"VVVV/KK/PP TT:MM:SS"; checkPreviewString(aFormatter, sCode, M_PI, eLang, sExpected); sCode = "\"Finnish: \"YYYY/MM/DD HH:MM:SS"; diff --git a/svl/source/numbers/zformat.cxx b/svl/source/numbers/zformat.cxx index cffa86176781..27d9b1322d5b 100644 --- a/svl/source/numbers/zformat.cxx +++ b/svl/source/numbers/zformat.cxx @@ -2940,6 +2940,37 @@ bool SvNumberformat::ImpGetFractionOutput(double fNumber, return bRes; } +sal_uInt16 SvNumberformat::ImpGetFractionOfSecondString( OUStringBuffer& rBuf, double fFractionOfSecond, + int nFractionDecimals, bool bAddOneRoundingDecimal, sal_uInt16 nIx, sal_uInt16 nMinimumInputLineDecimals ) +{ + if (!nFractionDecimals) + return 0; + + // nFractionDecimals+1 to not round up what Time::GetClock() carefully + // truncated. + rBuf.append( rtl::math::doubleToUString( fFractionOfSecond, rtl_math_StringFormat_F, + (bAddOneRoundingDecimal ? nFractionDecimals + 1 : nFractionDecimals), '.')); + rBuf.stripStart('0'); + rBuf.stripStart('.'); + if (bAddOneRoundingDecimal && rBuf.getLength() > nFractionDecimals) + rBuf.truncate( nFractionDecimals); // the digit appended because of nFractionDecimals+1 + if (nMinimumInputLineDecimals) + { + rBuf.stripEnd('0'); + for (sal_Int32 index = rBuf.getLength(); index < nMinimumInputLineDecimals; ++index) + { + rBuf.append('0'); + } + impTransliterate(rBuf, NumFor[nIx].GetNatNum()); + nFractionDecimals = rBuf.getLength(); + } + else + { + impTransliterate(rBuf, NumFor[nIx].GetNatNum()); + } + return static_cast<sal_uInt16>(nFractionDecimals); +} + bool SvNumberformat::ImpGetTimeOutput(double fNumber, sal_uInt16 nIx, OUStringBuffer& sBuff) @@ -2987,70 +3018,70 @@ bool SvNumberformat::ImpGetTimeOutput(double fNumber, { fNumber = 1.0 - fNumber; // "Inverse" } - double fTime = fNumber * 86400.0; - fTime = ::rtl::math::round( fTime, int(nCntPost) ); - if (bSign && fTime == 0.0) - { - bSign = false; // Not -00:00:00 - } - if( floor( fTime ) > D_MAX_U_INT32 ) - { - sBuff = ImpSvNumberformatScan::GetErrorString(); - return false; - } - sal_uInt32 nSeconds = static_cast<sal_uInt32>(floor( fTime )); - - OUStringBuffer sSecStr( ::rtl::math::doubleToUString( fTime-nSeconds, - rtl_math_StringFormat_F, int(nCntPost), '.')); - sSecStr.stripStart('0'); - sSecStr.stripStart('.'); - if ( bInputLine ) - { - sSecStr.stripEnd('0'); - for(sal_Int32 index = sSecStr.getLength(); index < rInfo.nCntPost; ++index) - { - sSecStr.append('0'); - } - impTransliterate(sSecStr, NumFor[nIx].GetNatNum()); - nCntPost = sSecStr.getLength(); - } - else - { - impTransliterate(sSecStr, NumFor[nIx].GetNatNum()); - } + OUStringBuffer sSecStr; sal_Int32 nSecPos = 0; // For figure by figure processing sal_uInt32 nHour, nMin, nSec; if (!rInfo.bThousand) // No [] format { - nHour = (nSeconds/3600) % 24; - nMin = (nSeconds%3600) / 60; - nSec = nSeconds%60; - } - else if (rInfo.nThousand == 3) // [ss] - { - nHour = 0; - nMin = 0; - nSec = nSeconds; - } - else if (rInfo.nThousand == 2) // [mm]:ss - { - nHour = 0; - nMin = nSeconds / 60; - nSec = nSeconds % 60; - } - else if (rInfo.nThousand == 1) // [hh]:mm:ss - { - nHour = nSeconds / 3600; - nMin = (nSeconds%3600) / 60; - nSec = nSeconds%60; + sal_uInt16 nCHour, nCMinute, nCSecond; + double fFractionOfSecond; + tools::Time::GetClock( fNumber, nCHour, nCMinute, nCSecond, fFractionOfSecond, nCntPost); + nHour = nCHour; + nMin = nCMinute; + nSec = nCSecond; + nCntPost = ImpGetFractionOfSecondString( sSecStr, fFractionOfSecond, nCntPost, true, nIx, + (bInputLine ? rInfo.nCntPost : 0)); } else { - // TODO What should these be set to? - nHour = 0; - nMin = 0; - nSec = 0; + double fTime = fNumber * 86400.0; + const double fOrigTime = fTime; + const double fFullSeconds = std::trunc(fTime); + fTime = rtl::math::round( fTime, int(nCntPost)); + // Do not round up into the next magnitude, truncate instead. + if (fTime >= fFullSeconds + 1.0 || (fTime == 0.0 && fOrigTime != 0.0)) + fTime = rtl::math::pow10Exp( std::trunc( rtl::math::pow10Exp( fOrigTime, nCntPost)), -nCntPost); + + if (bSign && fTime == 0.0) + { + bSign = false; // Not -00:00:00 + } + if (fTime > D_MAX_U_INT32) + { + sBuff = ImpSvNumberformatScan::GetErrorString(); + return false; + } + sal_uInt32 nSeconds = static_cast<sal_uInt32>(fTime); + + nCntPost = ImpGetFractionOfSecondString( sSecStr, fTime - nSeconds, nCntPost, false, nIx, + (bInputLine ? rInfo.nCntPost : 0)); + + if (rInfo.nThousand == 3) // [ss] + { + nHour = 0; + nMin = 0; + nSec = nSeconds; + } + else if (rInfo.nThousand == 2) // [mm]:ss + { + nHour = 0; + nMin = nSeconds / 60; + nSec = nSeconds % 60; + } + else if (rInfo.nThousand == 1) // [hh]:mm:ss + { + nHour = nSeconds / 3600; + nMin = (nSeconds%3600) / 60; + nSec = nSeconds%60; + } + else + { + // TODO What should these be set to? + nHour = 0; + nMin = 0; + nSec = 0; + } } sal_Unicode cAmPm = ' '; // a or p @@ -3873,57 +3904,51 @@ bool SvNumberformat::ImpGetDateTimeOutput(double fNumber, } sal_Int16 nNatNum = NumFor[nIx].GetNatNum().GetNatNum(); - sal_uInt32 nSeconds = static_cast<sal_uInt32>(floor( fTime )); - OUStringBuffer sSecStr( ::rtl::math::doubleToUString( fTime-nSeconds, - rtl_math_StringFormat_F, int(nCntPost), '.')); - sSecStr.stripStart('0'); - sSecStr.stripStart('.'); - if ( bInputLine ) - { - sSecStr.stripEnd('0'); - for(sal_Int32 index = sSecStr.getLength(); index < rInfo.nCntPost; ++index) - { - sSecStr.append('0'); - } - impTransliterate(sSecStr, NumFor[nIx].GetNatNum()); - nCntPost = sSecStr.getLength(); - } - else - { - impTransliterate(sSecStr, NumFor[nIx].GetNatNum()); - } - + OUStringBuffer sSecStr; sal_Int32 nSecPos = 0; // For figure by figure processing sal_uInt32 nHour, nMin, nSec; - if (!rInfo.bThousand) // [] format - { - nHour = (nSeconds/3600) % 24; - nMin = (nSeconds%3600) / 60; - nSec = nSeconds%60; - } - else if (rInfo.nThousand == 3) // [ss] - { - nHour = 0; - nMin = 0; - nSec = nSeconds; - } - else if (rInfo.nThousand == 2) // [mm]:ss - { - nHour = 0; - nMin = nSeconds / 60; - nSec = nSeconds % 60; - } - else if (rInfo.nThousand == 1) // [hh]:mm:ss + if (!rInfo.bThousand) // No [] format { - nHour = nSeconds / 3600; - nMin = (nSeconds%3600) / 60; - nSec = nSeconds%60; + sal_uInt16 nCHour, nCMinute, nCSecond; + double fFractionOfSecond; + tools::Time::GetClock( fNumber, nCHour, nCMinute, nCSecond, fFractionOfSecond, nCntPost); + nHour = nCHour; + nMin = nCMinute; + nSec = nCSecond; + nCntPost = ImpGetFractionOfSecondString( sSecStr, fFractionOfSecond, nCntPost, true, nIx, + (bInputLine ? rInfo.nCntPost : 0)); } else { - nHour = 0; // TODO What should these values be? - nMin = 0; - nSec = 0; + sal_uInt32 nSeconds = static_cast<sal_uInt32>(floor( fTime )); + + nCntPost = ImpGetFractionOfSecondString( sSecStr, fTime - nSeconds, nCntPost, false, nIx, + (bInputLine ? rInfo.nCntPost : 0)); + + if (rInfo.nThousand == 3) // [ss] + { + nHour = 0; + nMin = 0; + nSec = nSeconds; + } + else if (rInfo.nThousand == 2) // [mm]:ss + { + nHour = 0; + nMin = nSeconds / 60; + nSec = nSeconds % 60; + } + else if (rInfo.nThousand == 1) // [hh]:mm:ss + { + nHour = nSeconds / 3600; + nMin = (nSeconds%3600) / 60; + nSec = nSeconds%60; + } + else + { + nHour = 0; // TODO What should these values be? + nMin = 0; + nSec = 0; + } } sal_Unicode cAmPm = ' '; // a or p if (rInfo.nCntExp) // AM/PM |