2

I have multiple struct timespec values and a time_t value. I want to see which struct timespec value is the closest to the time_t value. Should I just compare the seconds and ignore the nanoseconds? Or should I convert both to total nanoseconds since the epoch? Or maybe convert both to total nanoseconds since the Epoch adding half a second to the time_t?

Background: A struct timespec has two members: .tv_sec containing the seconds since the Epoch, and .tv_nsec with additional nanoseconds. A time_t is just the seconds. In my case, the struct timespec values are from file system attributes, while the time_t value is converted from a local time string. I don't know if system calls to get the local time round nanoseconds or truncate them, so I don't know if, hypothetically, the time_t might be plus or minus a second from a struct timespec generated at the exact same time.

I could just compare the .tv_sec in each struct timespec value to the time_r value, or I could multiply each tv_sec by 1,000,000,000 and add it to its tv_nsec, and then multiply the time_t by 1,000,000,000, and then compare them? Or also add a half second to the time_r? Would converting to nanoseconds since the Epoch be worth the hassle? Also, what type should I use for that? unsigned long long?

(I don't know if I'm overthinking this or underthinking it.)

jetset
  • 388
  • 4
  • 14
  • 2
    I would build a `struct timespec` from my `time_t` value, setting `tv_nsec` to 0. Then compare the two timespecs, writing a general-purpose timespec comparator if I didn't already have one. – Steve Summit Feb 04 '21 at 00:54
  • 1
    I would just ignore the nanoseconds. When you compare values, the result is only as accurate as the least precise input. Since the `time_t` is only accurate to the second, the nanoseconds are irrelevant. – Barmar Feb 04 '21 at 00:55
  • https://xkcd.com/2295/ – Barmar Feb 04 '21 at 00:56

1 Answers1

1

Concern about local time should not apply as time_t (and .tv_sec which is also time_t) are time zone agnostic. They both refer to a time since a common epoch.

Use both members of timespec. Use a struct timespec to keep track of differences to negate concerns about adequate range and achieve the best answer.

Unchecked code, but enough to give OP an idea.

#include <time.h>

// Reasonable to assume .tv_nsec is in the normal range [0-999999999]
// and .tv_sec >= 0.
// Else add code to handle -1 .tv_sec and out of range ones.

// Absolute value of the difference.
static struct timespec abs_timespec(struct timespec t, time_t ref) {
  if (t.tv_sec >= ref) {
    t.tv_sec -= ref;
  } else {
    t.tv_sec = ref - t.tv_sec;
    if (t.tv_nsec > 0) {
      t.tv_nsec = 1000000000 - t.tv_nsec;
      t.tv_sec--;
    }
  }
  return t;
}

const struct timespec* compare(size_t n, const struct timespec *t, time_t ref) {
  if (n == 0) {
    return NULL;
  }

  const struct timespec *closest = &t[0];
  struct timespec best_diff = abs_timespec(t[0], ref);

  for (size_t i = 1; i < n; i++) {
    struct timespec diff = abs_timespec(t[i], ref);
    if (diff.tv_sec <= best_diff.tv_sec
        && (diff.tv_sec < best_diff.tv_sec || diff.tv_nsec < best_diff.tv_nsec)) {
      best_diff = diff;
      closest = &t[i];
    }
  }
  return closest;
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Thank you very much for the detailed code snipped. Why does the abs_timespec() function ignore nanoseconds if the timespec seconds is greater than the reference seconds? (I.e., why does the abs_timespec() function only pay attention to nanoseconds if the timespec seconds is strictly less than the reference seconds?) – jetset Feb 07 '21 at 19:27
  • 1
    @jetset When `t.tv_sec >= ref` is true. the difference in seconds is `t.tv_sec -= ref` and the difference in nanoseconds is `t.tv_nsec -= 0;`, so no need for the subtraction. – chux - Reinstate Monica Feb 07 '21 at 19:41