sc/source/core/tool/interpr2.cxx |   59 +++++++++++++++++++++++++++++++--------
 1 file changed, 47 insertions(+), 12 deletions(-)

New commits:
commit 273b3e10eab70ebc084cb62568bd699fddfb376e
Author:     Eike Rathke <er...@redhat.com>
AuthorDate: Mon Aug 27 15:05:54 2018 +0200
Commit:     Eike Rathke <er...@redhat.com>
CommitDate: Tue Aug 28 00:14:00 2018 +0200

    Resolves: tdf#119533 reintroduce time rounding but cut, tdf#118800 follow-up
    
    Regression from
    
        commit c69e7266916ac1b8917477fb4eccdb9098da5792
        CommitDate: Thu Jul 19 14:01:30 2018 +0200
    
            tdf#118800 fix rounding error in Calc function HOUR, MINUTE, SECOND.
    
    Rounding was only an error if it produced a value of a full day in
    seconds, or if it otherwise led to an inappropriately rounded-up
    individual value, but in general some rounding is necessary.
    
    Instead of omitting rounding completely, basically round to
    nanoseconds and then do not round individual hour,minute,second
    values but instead truncate to the next magnitude so 23:59:59.9999
    gives 23h59m59s instead of 24h0m0s
    
    Change-Id: I93df1aa54212c1b8816237c9467f270ed28a3f1f
    Reviewed-on: https://gerrit.libreoffice.org/59677
    Reviewed-by: Eike Rathke <er...@redhat.com>
    Tested-by: Jenkins

diff --git a/sc/source/core/tool/interpr2.cxx b/sc/source/core/tool/interpr2.cxx
index c185ecfe9223..a50f4ea9aa9c 100644
--- a/sc/source/core/tool/interpr2.cxx
+++ b/sc/source/core/tool/interpr2.cxx
@@ -142,28 +142,63 @@ void ScInterpreter::ScGetDay()
     PushDouble(static_cast<double>(aDate.GetDay()));
 }
 
+/* TODO: move this to tools::Time so also SvNumberFormatter and everything else
+ * can use it and all display the same values. */
+static void lcl_getHourMinuteSecond( double fTimeInDays, sal_Int32& nHour, 
sal_Int32& nMinute, sal_Int32& nSecond )
+{
+    const double fTime = fTimeInDays - rtl::math::approxFloor(fTimeInDays); // 
date part absent
+
+    // If 0 then full day (or no day), shortcut.
+    // If < 0 then approxFloor() effectively returned the ceiling (note this
+    // also holds for negative fTimeInDays values) because of a near identical
+    // value, shortcut this to a full day as well.
+    if (fTime <= 0.0)
+    {
+        nHour = nMinute = nSecond = 0;
+        return;
+    }
+
+    // In seconds, including milli and nano.
+    const double fRawSeconds = fTime * DATE_TIME_FACTOR;
+
+    // Round to nanoseconds, which is the highest resolution this could be
+    // influenced by.
+    double fSeconds = rtl::math::round( fRawSeconds, 9);
+
+    // If this ended up as a full day the original value was very very close
+    // but not quite. Take that.
+    if (fSeconds >= tools::Time::secondPerDay)
+        fSeconds = fRawSeconds;
+
+    // Now do not round values (specifically not up), but truncate to the next
+    // magnitude, so 23:59:59.99 is still 23:59:59 and not 24:00:00 (or even
+    // 00:00:00 which Excel does).
+    nHour = fSeconds / tools::Time::secondPerHour;
+    fSeconds -= nHour * tools::Time::secondPerHour;
+    nMinute = fSeconds / tools::Time::secondPerMinute;
+    fSeconds -= nMinute * tools::Time::secondPerMinute;
+    nSecond = fSeconds;
+}
+
 void ScInterpreter::ScGetMin()
 {
-    double fTime = GetDouble();
-    fTime -= ::rtl::math::approxFloor(fTime);       // date part absent
-    long nVal = 
static_cast<long>(::rtl::math::approxFloor(fTime*DATE_TIME_FACTOR)) % 
::tools::Time::secondPerHour;
-    PushDouble( static_cast<double>(nVal / ::tools::Time::secondPerMinute) );
+    sal_Int32 nHour, nMinute, nSecond;
+    lcl_getHourMinuteSecond( GetDouble(), nHour, nMinute, nSecond);
+    PushDouble( nMinute);
 }
 
 void ScInterpreter::ScGetSec()
 {
-    double fTime = GetDouble();
-    fTime -= ::rtl::math::approxFloor(fTime);       // date part absent
-    long nVal = 
static_cast<long>(::rtl::math::approxFloor(fTime*DATE_TIME_FACTOR)) % 
::tools::Time::secondPerMinute;
-    PushDouble( static_cast<double>(nVal) );
+    sal_Int32 nHour, nMinute, nSecond;
+    lcl_getHourMinuteSecond( GetDouble(), nHour, nMinute, nSecond);
+    PushDouble( nSecond);
 }
 
 void ScInterpreter::ScGetHour()
 {
-    double fTime = GetDouble();
-    fTime -= ::rtl::math::approxFloor(fTime);       // date part absent
-    long nVal = 
static_cast<long>(::rtl::math::approxFloor(fTime*DATE_TIME_FACTOR)) / 
::tools::Time::secondPerHour;
-    PushDouble(static_cast<double>(nVal));
+    sal_Int32 nHour, nMinute, nSecond;
+    lcl_getHourMinuteSecond( GetDouble(), nHour, nMinute, nSecond);
+    PushDouble( nHour);
 }
 
 void ScInterpreter::ScGetDateValue()
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to