summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEike Rathke <erack@redhat.com>2018-08-27 15:05:54 +0200
committerEike Rathke <erack@redhat.com>2018-08-28 00:14:00 +0200
commit273b3e10eab70ebc084cb62568bd699fddfb376e (patch)
tree47d4f8779ee4047fe7c70b27ecbac105f1d3c479
parenta3358bb4a75ce36e1dd4c26a3be7bf0d8dbbfa98 (diff)
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 <erack@redhat.com> Tested-by: Jenkins
-rw-r--r--sc/source/core/tool/interpr2.cxx59
1 files changed, 47 insertions, 12 deletions
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()