include/tools/duration.hxx         |   13 +++++++++++--
 tools/source/datetime/duration.cxx |   25 ++++++++++++++-----------
 2 files changed, 25 insertions(+), 13 deletions(-)

New commits:
commit 46e672db8002e7aaac881bee65b5c50c4e14c666
Author:     Eike Rathke <er...@redhat.com>
AuthorDate: Thu Aug 3 20:52:43 2023 +0200
Commit:     Eike Rathke <er...@redhat.com>
CommitDate: Thu Aug 3 21:42:53 2023 +0200

    Resolves: tdf#127334 Increase tools::Duration accuracy epsilon unsharpness
    
    ... when converting from double, i.e. to 300 nanoseconds.
    Empirically determined..
    
    Change-Id: I92c43b5f244923363af5d44bece9c155126ca343
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155324
    Reviewed-by: Eike Rathke <er...@redhat.com>
    Tested-by: Jenkins

diff --git a/include/tools/duration.hxx b/include/tools/duration.hxx
index ea33953751b8..9fae80d1d7c9 100644
--- a/include/tools/duration.hxx
+++ b/include/tools/duration.hxx
@@ -31,8 +31,17 @@ public:
         minutes and seconds values here though. */
     Duration(const Time& rStart, const Time& rEnd);
 
-    /** Difference in days, like DateTime()-DateTime(). */
-    explicit Duration(double fTimeInDays);
+    /** Difference in days, like DateTime()-DateTime().
+
+        @param  nAccuracyEpsilonNanoseconds
+                Round for example by 1 nanosecond if it's just 1 off to a
+                second,  i.e. 0999999999 or 0000000001. This can be loosened if
+                necessary. For example, if fTimeInDays is a date+time in
+                "today's" range with a significant seconds resolution, an
+                accuracy epsilon (=unsharpness) of ~300 is required. Hence 
default.
+                Must be 0 <= nAccuracyEpsilonNanoseconds <= 
Time::nanoSecPerSec - 1.
+     */
+    explicit Duration(double fTimeInDays, sal_uInt64 
nAccuracyEpsilonNanoseconds = 300);
 
     /** Time can be a limited duration as well and can have out-of-range
         values, it will be normalized. Sign of both days and Time must be equal
diff --git a/tools/source/datetime/duration.cxx 
b/tools/source/datetime/duration.cxx
index a7b2762fff49..a655f016a1bc 100644
--- a/tools/source/datetime/duration.cxx
+++ b/tools/source/datetime/duration.cxx
@@ -47,8 +47,9 @@ Duration::Duration(const Time& rStart, const Time& rEnd)
     }
 }
 
-Duration::Duration(double fTimeInDays)
+Duration::Duration(double fTimeInDays, sal_uInt64 nAccuracyEpsilonNanoseconds)
 {
+    assert(nAccuracyEpsilonNanoseconds <= Time::nanoSecPerSec - 1);
     double fInt, fFrac;
     if (fTimeInDays < 0.0)
     {
@@ -66,19 +67,21 @@ Duration::Duration(double fTimeInDays)
         fFrac *= Time::nanoSecPerDay;
         fFrac = ::rtl::math::approxFloor(fFrac);
         sal_Int64 nNS = static_cast<sal_Int64>(fFrac);
-        // Round by 1 nanosecond if it's just 1 off to a second, i.e.
-        // 0999999999 or 0000000001. This could be loosened to rounding by 2 or
-        // such if necessary.
         const sal_Int64 nN = nNS % Time::nanoSecPerSec;
-        if (std::abs(nN) == 1)
-            nNS -= (nNS < 0) ? -1 : 1;
-        else if (std::abs(nN) == Time::nanoSecPerSec - 1)
+        if (nN)
         {
-            nNS += (nNS < 0) ? -1 : 1;
-            if (std::abs(nNS) >= Time::nanoSecPerDay)
+            const sal_uInt64 nA = std::abs(nN);
+            if (nA <= nAccuracyEpsilonNanoseconds)
+                nNS -= (nNS < 0) ? -nN : nN;
+            else if (nA >= Time::nanoSecPerSec - nAccuracyEpsilonNanoseconds)
             {
-                mnDays += nNS / Time::nanoSecPerDay;
-                nNS %= Time::nanoSecPerDay;
+                const sal_Int64 nD = Time::nanoSecPerSec - nA;
+                nNS += (nNS < 0) ? -nD : nD;
+                if (std::abs(nNS) >= Time::nanoSecPerDay)
+                {
+                    mnDays += nNS / Time::nanoSecPerDay;
+                    nNS %= Time::nanoSecPerDay;
+                }
             }
         }
         maTime.MakeTimeFromNS(nNS);

Reply via email to