/**
  * Return a Duration representing the DST difference (if any) between two dates. i.e. if one date
  * is before the DST changeover, and the other date is after, the resulting duration will
  * represent the DST offset.
  *
  * @param self a Date
  * @param other another Date
  * @return a Duration
  */
 public static Duration getRelativeDaylightSavingsOffset(Date self, Date other) {
   Duration d1 = getDaylightSavingsOffset(self);
   Duration d2 = getDaylightSavingsOffset(other);
   return new TimeDuration(0, 0, 0, (int) (d2.toMilliseconds() - d1.toMilliseconds()));
 }
 /** returns how much time is left (negative if {@link #isExpired()}) */
 public Duration getDurationRemaining() {
   return Duration.millis(limit.toMilliseconds() - stopwatch.elapsed(TimeUnit.MILLISECONDS));
 }
 /** true iff the timer has run for more than the duration specified at creation time */
 public boolean isExpired() {
   return stopwatch.elapsed(TimeUnit.MILLISECONDS) > limit.toMilliseconds();
 }
 /**
  * block on the given argument until the timer is completed or the object receives a notified;
  * callers must be synchronized on the waitTarget
  *
  * @return true if the object is notified (or receives a spurious wake), false if the duration is
  *     expired
  * @throws InterruptedException
  */
 public boolean waitOnForExpiry(Object waitTarget) throws InterruptedException {
   Duration remainder = getDurationRemaining();
   if (remainder.toMilliseconds() <= 0) return false;
   waitTarget.wait(remainder.toMilliseconds());
   return true;
 }