This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-io.git


The following commit(s) were added to refs/heads/master by this push:
     new b8e788a08 ThreadUtils.sleep(Duration) should handle the underlying OS 
time changing
     new c722fe80b Merge branch 'master' of 
https://gitbox.apache.org/repos/asf/commons-io.git
b8e788a08 is described below

commit b8e788a0875789303a957b077bef86e98cb83af3
Author: Gary Gregory <garydgreg...@gmail.com>
AuthorDate: Tue Apr 8 12:21:40 2025 -0400

    ThreadUtils.sleep(Duration) should handle the underlying OS time
    changing
---
 src/changes/changes.xml                            |  1 +
 .../java/org/apache/commons/io/ThreadUtils.java    | 28 ++++++++++++++++------
 2 files changed, 22 insertions(+), 7 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 1684f86a7..fdfdf0a0c 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -74,6 +74,7 @@ The <action> type attribute can be add,update,fix,remove.
       <action dev="ggregory" type="fix"                due-to="Gary 
Gregory">General Javadoc improvements.</action>
       <action dev="ggregory" type="fix"                due-to="Gary 
Gregory">Calling QueueInputStream.QueueInputStream(null) maps to the same kind 
of default blocking queue as 
QueueInputStream.Builder.setBlockingQueue(null).</action>
       <action dev="ggregory" type="fix"                due-to="Gary 
Gregory">CopyDirectoryVisitor creates incorrect file names when copying between 
different file systems that use different file system separators ("/" versus 
"\"); fixes PathUtils.copyDirectory(Path, Path, CopyOption...).</action>
+      <action dev="ggregory" type="fix"                due-to="zhouchongwen, 
Gary Gregory">ThreadUtils.sleep(Duration) should handle the underlying OS time 
changing.</action>
       <!-- ADD -->
       <action dev="ggregory" type="add" issue="IO-860" due-to="Nico Strecker, 
Gary Gregory">Add ThrottledInputStream.Builder.setMaxBytes(long, 
ChronoUnit).</action>
       <action dev="ggregory" type="add"                due-to="Gary 
Gregory">Add IOIterable.</action>
diff --git a/src/main/java/org/apache/commons/io/ThreadUtils.java 
b/src/main/java/org/apache/commons/io/ThreadUtils.java
index f6b7713ca..e41af9a84 100644
--- a/src/main/java/org/apache/commons/io/ThreadUtils.java
+++ b/src/main/java/org/apache/commons/io/ThreadUtils.java
@@ -38,17 +38,31 @@ private static int getNanosOfMilli(final Duration duration) 
{
      * </p>
      *
      * @param duration the sleep duration.
-     * @throws InterruptedException if interrupted
+     * @throws InterruptedException if interrupted.
      * @see Thread#sleep(long, int)
      */
     public static void sleep(final Duration duration) throws 
InterruptedException {
         // Using this method avoids depending on the vagaries of the precision 
and accuracy of system timers and schedulers.
-        final Instant finishInstant = Instant.now().plus(duration);
-        Duration remainingDuration = duration;
-        do {
-            Thread.sleep(remainingDuration.toMillis(), 
getNanosOfMilli(remainingDuration));
-            remainingDuration = Duration.between(Instant.now(), finishInstant);
-        } while (!remainingDuration.isNegative());
+        try {
+            // Use the JVM elapsed time, avoids issues with DST changes and 
manual OS time changes.
+            final long nanoStart = System.nanoTime();
+            final long finishNanos = nanoStart + duration.toNanos(); // 
toNanos(): Possible ArithmeticException, otherwise wrap around OK.
+            Duration remainingDuration = duration;
+            long nowNano;
+            do {
+                Thread.sleep(remainingDuration.toMillis(), 
getNanosOfMilli(remainingDuration));
+                nowNano = System.nanoTime();
+                remainingDuration = Duration.ofNanos(finishNanos - nowNano);
+            } while (nowNano - finishNanos < 0); // handles wrap around, see 
Thread#sleep(long, int).
+        } catch (final ArithmeticException e) {
+            // Use the current time
+            final Instant finishInstant = Instant.now().plus(duration);
+            Duration remainingDuration = duration;
+            do {
+                Thread.sleep(remainingDuration.toMillis(), 
getNanosOfMilli(remainingDuration));
+                remainingDuration = Duration.between(Instant.now(), 
finishInstant);
+            } while (!remainingDuration.isNegative());
+        }
     }
 
     /**

Reply via email to