summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEike Rathke <erack@redhat.com>2018-08-31 11:21:03 +0200
committerEike Rathke <erack@redhat.com>2018-08-31 12:50:52 +0200
commite2e47898180e547cad7ccde1e5890385d573e551 (patch)
treec91b4e1e053c69bbf0906953fe1bba276977ef2c
parent370d4c9ba34913076f7a73a2912eb2e0e48ca73c (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.hxx9
-rw-r--r--svl/qa/unit/svl.cxx22
-rw-r--r--svl/source/numbers/zformat.cxx227
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