Repository: libuv/libuv (C)

Issue:
FR: add real-time clock version of uv_hrtime()
`uv_hrtime()` always uses a monotonic clock but that is not sufficient for all applications.

Example: `new Date()` in node.js currently needs platform-specific code because the spec mandates the use of a real-time clock (i.e., human time.)

My proposal:
```c
struct uv_timespec {
  int64_t sec;  /* Seconds since UNIX epoch.  Negative for dates < 1970. */
  uint32_t nsec;
};

enum uv_clock_id {
  UV_CLOCK_MONOTONIC,
  UV_CLOCK_MONOTONIC_COARSE,  // XXX define as "at least 1 ms precision"?
  UV_CLOCK_REALTIME,
  UV_CLOCK_REALTIME_COARSE
 };

int uv_clock_gettime(enum uv_clock_id id, struct uv_timespec* ts);
```
[Look familiar](http://man7.org/linux/man-pages/man2/clock_gettime.2.html)?  `uv_hrtime()` can be reimplemented in terms of `uv_clock_gettime()`.

Other interesting clocks can be added later if we error with UV_EINVAL for invalid clock ids so that downstream users can detect their (lack of) support at run-time.

The Windows implementation would probably use ` GetSystemTimePreciseAsFileTime()` or `GetSystemTimeAsFileTime()` for the real-time clock.

Like `gettimeofday()` and `clock_gettime(CLOCK_REALTIME)`, they return the time in UTC, but in "100-nanosecond intervals since January 1, 1601 (UTC)" according to MSDN.


Discussion: This design seems reasonable.
SGTM.

We could add `UV_CLOCK_BOOTTIME` as `CLOCK_BOOTTIME` is already in use in the [code](https://github.com/libuv/libuv/blob/v1.x/src/unix/linux-core.c#L544).
> We could add UV_CLOCK_BOOTTIME as CLOCK_BOOTTIME is already in use in the code.

Subsuming `uv_uptime()` is part of the master plan but I figured I'd start with the basics.
This looks good. I'd like to take a shot at it.
SGTM
I'll never pass on an opportunity to foist work onto others.  Go for it, Colin!

The only thing I wasn't sure about is `UV_CLOCK_*_COARSE`.  I was thinking that maybe we should reserve one or two additional values so that `UV_CLOCK_*_COARSE+1` can be used in the future for e.g. microsecond granularity and `UV_CLOCK_*_COARSE+2` for, say, hectonanoseconds (decimicroseconds?  100 ns intervals.)

On the other hand, we can always append enums at the end later.  I'm probably overthinking things.
Just noting that I don't think macOS will be able to move to pure `clock_gettime()` yet. There was an attempt in #1073 and a subsequent revert in #1202. Even on more up to date versions, it looks like the coarse clock definitions aren't there.

EDIT: After a little reading, I see that the coarse clocks are Linux specific.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.




Fix (patch):
diff --git a/docs/src/fs.rst b/docs/src/fs.rst
index e7522d59927..891ee74c19d 100644
--- a/docs/src/fs.rst
+++ b/docs/src/fs.rst
@@ -30,7 +30,8 @@ Data types
 
 .. c:type:: uv_timespec_t
 
-    Portable equivalent of ``struct timespec``.
+    Y2K38-unsafe data type for storing times with nanosecond resolution.
+    Will be replaced with :c:type:`uv_timespec64_t` in libuv v2.0.
 
     ::
 
diff --git a/docs/src/misc.rst b/docs/src/misc.rst
index e8b9a4c838d..c2ad5804a6f 100644
--- a/docs/src/misc.rst
+++ b/docs/src/misc.rst
@@ -73,7 +73,8 @@ Data types
 
 .. c:type:: uv_timeval_t
 
-    Data type for storing times.
+    Y2K38-unsafe data type for storing times with microsecond resolution.
+    Will be replaced with :c:type:`uv_timeval64_t` in libuv v2.0.
 
     ::
 
@@ -84,7 +85,7 @@ Data types
 
 .. c:type:: uv_timeval64_t
 
-    Alternative data type for storing times.
+    Y2K38-safe data type for storing times with microsecond resolution.
 
     ::
 
@@ -93,6 +94,28 @@ Data types
             int32_t tv_usec;
         } uv_timeval64_t;
 
+.. c:type:: uv_timespec64_t
+
+    Y2K38-safe data type for storing times with nanosecond resolution.
+
+    ::
+
+        typedef struct {
+            int64_t tv_sec;
+            int32_t tv_nsec;
+        } uv_timespec64_t;
+
+.. c:enum:: uv_clock_id
+
+    Clock source for :c:func:`uv_clock_gettime`.
+
+    ::
+
+        typedef enum {
+          UV_CLOCK_MONOTONIC,
+          UV_CLOCK_REALTIME
+        } uv_clock_id;
+
 .. c:type:: uv_rusage_t
 
     Data type for resource usage results.
@@ -588,7 +611,7 @@ API
 
 .. c:function:: uint64_t uv_hrtime(void)
 
-    Returns the current high-resolution real time. This is expressed in
+    Returns the current high-resolution timestamp. This is expressed in
     nanoseconds. It is relative to an arbitrary time in the past. It is not
     related to the time of day and therefore not subject to clock drift. The
     primary use is for measuring performance between intervals.
@@ -597,6 +620,19 @@ API
         Not every platform can support nanosecond resolution; however, this value will always
         be in nanoseconds.
 
+.. c:function:: int uv_clock_gettime(uv_clock_id clock_id, uv_timespec64_t* ts)
+
+    Obtain the current system time from a high-resolution real-time or monotonic
+    clock source.
+
+    The real-time clock counts from the UNIX epoch (1970-01-01) and is subject
+    to time adjustments; it can jump back in time.
+
+    The monotonic clock counts from an arbitrary point in the past and never
+    jumps back in time.
+
+    .. versionadded:: 1.45.0
+
 .. c:function:: void uv_print_all_handles(uv_loop_t* loop, FILE* stream)
 
     Prints all handles associated with the given `loop` to the given `stream`.
diff --git a/include/uv.h b/include/uv.h
index e6724deffdf..f3d70231ffa 100644
--- a/include/uv.h
+++ b/include/uv.h
@@ -345,11 +345,32 @@ typedef void (*uv_random_cb)(uv_random_t* req,
                              void* buf,
                              size_t buflen);
 
+typedef enum {
+  UV_CLOCK_MONOTONIC,
+  UV_CLOCK_REALTIME
+} uv_clock_id;
+
+/* XXX(bnoordhuis) not 2038-proof, https://github.com/libuv/libuv/issues/3864 */
 typedef struct {
   long tv_sec;
   long tv_nsec;
 } uv_timespec_t;
 
+typedef struct {
+  int64_t tv_sec;
+  int32_t tv_nsec;
+} uv_timespec64_t;
+
+/* XXX(bnoordhuis) not 2038-proof, https://github.com/libuv/libuv/issues/3864 */
+typedef struct {
+  long tv_sec;
+  long tv_usec;
+} uv_timeval_t;
+
+typedef struct {
+  int64_t tv_sec;
+  int32_t tv_usec;
+} uv_timeval64_t;
 
 typedef struct {
   uint64_t st_dev;
@@ -1191,16 +1212,6 @@ UV_EXTERN int uv_uptime(double* uptime);
 UV_EXTERN uv_os_fd_t uv_get_osfhandle(int fd);
 UV_EXTERN int uv_open_osfhandle(uv_os_fd_t os_fd);
 
-typedef struct {
-  long tv_sec;
-  long tv_usec;
-} uv_timeval_t;
-
-typedef struct {
-  int64_t tv_sec;
-  int32_t tv_usec;
-} uv_timeval64_t;
-
 typedef struct {
    uv_timeval_t ru_utime; /* user CPU time used */
    uv_timeval_t ru_stime; /* system CPU time used */
@@ -1732,6 +1743,7 @@ UV_EXTERN uint64_t uv_get_total_memory(void);
 UV_EXTERN uint64_t uv_get_constrained_memory(void);
 UV_EXTERN uint64_t uv_get_available_memory(void);
 
+UV_EXTERN int uv_clock_gettime(uv_clock_id clock_id, uv_timespec64_t* ts);
 UV_EXTERN uint64_t uv_hrtime(void);
 UV_EXTERN void uv_sleep(unsigned int msec);
 
diff --git a/src/unix/core.c b/src/unix/core.c
index 3dd7a94ca4d..9c0b3f99b80 100644
--- a/src/unix/core.c
+++ b/src/unix/core.c
@@ -44,6 +44,7 @@
 #include <grp.h>
 #include <sys/utsname.h>
 #include <sys/time.h>
+#include <time.h> /* clock_gettime */
 
 #ifdef __sun
 # include <sys/filio.h>
@@ -108,6 +109,35 @@ STATIC_ASSERT(offsetof(uv_buf_t, base) == offsetof(struct iovec, iov_base));
 STATIC_ASSERT(offsetof(uv_buf_t, len) == offsetof(struct iovec, iov_len));
 
 
+/* https://github.com/libuv/libuv/issues/1674 */
+int uv_clock_gettime(uv_clock_id clock_id, uv_timespec64_t* ts) {
+  struct timespec t;
+  int r;
+
+  if (ts == NULL)
+    return UV_EFAULT;
+
+  switch (clock_id) {
+    default:
+      return UV_EINVAL;
+    case UV_CLOCK_MONOTONIC:
+      r = clock_gettime(CLOCK_MONOTONIC, &t);
+      break;
+    case UV_CLOCK_REALTIME:
+      r = clock_gettime(CLOCK_REALTIME, &t);
+      break;
+  }
+
+  if (r)
+    return UV__ERR(errno);
+
+  ts->tv_sec = t.tv_sec;
+  ts->tv_nsec = t.tv_nsec;
+
+  return 0;
+}
+
+
 uint64_t uv_hrtime(void) {
   return uv__hrtime(UV_CLOCK_PRECISE);
 }
diff --git a/src/win/util.c b/src/win/util.c
index 6232ce1505a..f6ec79cd57b 100644
--- a/src/win/util.c
+++ b/src/win/util.c
@@ -31,6 +31,7 @@
 #include "internal.h"
 
 /* clang-format off */
+#include <sysinfoapi.h>
 #include <winsock2.h>
 #include <winperf.h>
 #include <iphlpapi.h>
@@ -504,11 +505,43 @@ int uv_get_process_title(char* buffer, size_t size) {
 }
 
 
+/* https://github.com/libuv/libuv/issues/1674 */
+int uv_clock_gettime(uv_clock_id clock_id, uv_timespec64_t* ts) {
+  FILETIME ft;
+  int64_t t;
+
+  if (ts == NULL)
+    return UV_EFAULT;
+
+  switch (clock_id) {
+    case UV_CLOCK_MONOTONIC:
+      uv__once_init();
+      t = uv__hrtime(UV__NANOSEC);
+      ts->tv_sec = t / 1000000000;
+      ts->tv_nsec = t % 1000000000;
+      return 0;
+    case UV_CLOCK_REALTIME:
+      GetSystemTimePreciseAsFileTime(&ft);
+      /* In 100-nanosecond increments from 1601-01-01 UTC because why not? */
+      t = (int64_t) ft.dwHighDateTime << 32 | ft.dwLowDateTime;
+      /* Convert to UNIX epoch, 1970-01-01. Still in 100 ns increments. */
+      t -= 116444736000000000ll;
+      /* Now convert to seconds and nanoseconds. */
+      ts->tv_sec = t / 10000000;
+      ts->tv_nsec = t % 10000000 * 100;
+      return 0;
+  }
+
+  return UV_EINVAL;
+}
+
+
 uint64_t uv_hrtime(void) {
   uv__once_init();
   return uv__hrtime(UV__NANOSEC);
 }
 
+
 uint64_t uv__hrtime(unsigned int scale) {
   LARGE_INTEGER counter;
   double scaled_freq;