diff --git a/changelogs/unreleased/changed-signedness-of-time64-functions.md b/changelogs/unreleased/changed-signedness-of-time64-functions.md
new file mode 100644
index 0000000000000000000000000000000000000000..3e500994c007925a34d905fc561dac38c1d70c91
--- /dev/null
+++ b/changelogs/unreleased/changed-signedness-of-time64-functions.md
@@ -0,0 +1,10 @@
+## bugfix/core
+
+* **[Breaking change]** Return value signedness of 64-bit time functions in
+  `clock` and `fiber` was changed from `uint64_t` to `int64_t` both in Lua
+  and C (gh-5989).
+
+----
+
+Breaking change: lua: return value type for all time64 functions changed from
+`uint64_t` to `int64_t`.
diff --git a/src/lib/core/clock.c b/src/lib/core/clock.c
index ff30584857e98ea12399d41a9b84fc69ee108bbd..ccf35858c8ad1f32137c5550f6c974c837594420 100644
--- a/src/lib/core/clock.c
+++ b/src/lib/core/clock.c
@@ -70,41 +70,43 @@ clock_thread(void)
 #endif
 }
 
-uint64_t
+int64_t
 clock_realtime64(void)
 {
 	struct timespec ts;
 	clock_gettime(CLOCK_REALTIME, &ts);
-	return ((uint64_t)ts.tv_sec) * 1000000000 + ts.tv_nsec;
+	return ((int64_t)ts.tv_sec) * 1000000000 + ts.tv_nsec;
 
 }
-uint64_t
+
+int64_t
 clock_monotonic64(void)
 {
 	struct timespec ts;
 	clock_gettime(CLOCK_MONOTONIC, &ts);
-	return ((uint64_t)ts.tv_sec) * 1000000000 + ts.tv_nsec;
+	return ((int64_t)ts.tv_sec) * 1000000000 + ts.tv_nsec;
 }
-uint64_t
+
+int64_t
 clock_process64(void)
 {
 #if defined(CLOCK_PROCESS_CPUTIME_ID)
 	struct timespec ts;
 	clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
-	return ((uint64_t)ts.tv_sec) * 1000000000 + ts.tv_nsec;
+	return ((int64_t)ts.tv_sec) * 1000000000 + ts.tv_nsec;
 #else
-	return (uint64_t) clock() * 1000000000 / CLOCKS_PER_SEC;
+	return (int64_t)clock() * 1000000000 / CLOCKS_PER_SEC;
 #endif
 }
 
-uint64_t
+int64_t
 clock_thread64(void)
 {
 #if defined(CLOCK_THREAD_CPUTIME_ID)
 	struct timespec ts;
 	clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
-	return ((uint64_t)ts.tv_sec) * 1000000000 + ts.tv_nsec;
+	return ((int64_t)ts.tv_sec) * 1000000000 + ts.tv_nsec;
 #else
-	return (uint64_t) clock() * 1000000000 / CLOCKS_PER_SEC;
+	return (int64_t)clock() * 1000000000 / CLOCKS_PER_SEC;
 #endif
 }
diff --git a/src/lib/core/clock.h b/src/lib/core/clock.h
index e31b8599adfdbb85396fafea47a8e02168667b23..75376b56141f75ba6b3ea604ecfb00664c84d25d 100644
--- a/src/lib/core/clock.h
+++ b/src/lib/core/clock.h
@@ -40,15 +40,67 @@ extern "C" {
 
 /** \cond public */
 
-double clock_realtime(void);
-double clock_monotonic(void);
-double clock_process(void);
-double clock_thread(void);
-
-uint64_t clock_realtime64(void);
-uint64_t clock_monotonic64(void);
-uint64_t clock_process64(void);
-uint64_t clock_thread64(void);
+/**
+ * A settable system-wide clock that measures real (i.e.,
+ * wall-clock) time.
+ *
+ * See clock_gettime(2), CLOCK_REALTIME.
+ */
+double
+clock_realtime(void);
+
+/**
+ * A nonsettable system-wide clock that represents monotonic time.
+ *
+ * See clock_gettime(2), CLOCK_MONOTONIC.
+ */
+double
+clock_monotonic(void);
+
+/**
+ * A clock that measures CPU time consumed by this process (by all
+ * threads in the process).
+ *
+ * See clock_gettime(2), CLOCK_PROCESS_CPUTIME_ID.
+ */
+double
+clock_process(void);
+
+/**
+ * A clock that measures CPU time consumed by this thread.
+ *
+ * See clock_gettime(2), CLOCK_THREAD_CPUTIME_ID.
+ */
+double
+clock_thread(void);
+
+/**
+ * Same as clock_realtime(), but returns the time as 64 bit
+ * signed integer.
+ */
+int64_t
+clock_realtime64(void);
+
+/**
+ * Same as clock_monotonic(), but returns the time as 64 bit
+ * signed integer.
+ */
+int64_t
+clock_monotonic64(void);
+
+/**
+ * Same as clock_process(), but returns the time as 64 bit
+ * signed integer.
+ */
+int64_t
+clock_process64(void);
+
+/**
+ * Same as clock_thread(), but returns the time as 64 bit
+ * signed integer.
+ */
+int64_t
+clock_thread64(void);
 
 /** \endcond public */
 
diff --git a/src/lib/core/fiber.c b/src/lib/core/fiber.c
index 147ff49b49bf0af3bd639e108baae1c82e62c7cb..19e73d703645e523d23bbf1d780eb86666bcdff3 100644
--- a/src/lib/core/fiber.c
+++ b/src/lib/core/fiber.c
@@ -592,10 +592,10 @@ fiber_time(void)
 	return ev_now(loop());
 }
 
-uint64_t
+int64_t
 fiber_time64(void)
 {
-	return (uint64_t) ( ev_now(loop()) * 1000000 + 0.5 );
+	return (int64_t)(ev_now(loop()) * 1000000 + 0.5);
 }
 
 double
@@ -604,10 +604,10 @@ fiber_clock(void)
 	return ev_monotonic_now(loop());
 }
 
-uint64_t
+int64_t
 fiber_clock64(void)
 {
-	return (uint64_t) ( ev_monotonic_now(loop()) * 1000000 + 0.5 );
+	return (int64_t)(ev_monotonic_now(loop()) * 1000000 + 0.5);
 }
 
 /**
diff --git a/src/lib/core/fiber.h b/src/lib/core/fiber.h
index 5d9fd6d6dafba6f3fbac826a18a07233adf1dd84..4ed5c1d81ed1c3166e95ef46598d8cc260daabd1 100644
--- a/src/lib/core/fiber.h
+++ b/src/lib/core/fiber.h
@@ -391,7 +391,7 @@ fiber_time(void);
  * Report loop begin time as 64-bit int.
  * Uses real time clock.
  */
-API_EXPORT uint64_t
+API_EXPORT int64_t
 fiber_time64(void);
 
 /**
@@ -405,7 +405,7 @@ fiber_clock(void);
  * Report loop begin time as 64-bit int.
  * Uses monotonic clock.
  */
-API_EXPORT uint64_t
+API_EXPORT int64_t
 fiber_clock64(void);
 
 /**
diff --git a/src/lua/clock.lua b/src/lua/clock.lua
index fee43ccde767f8afa3c2a5252cc7bd33da8279d4..738f3844fec49961195cf521f9b34ee0bcf240b7 100644
--- a/src/lua/clock.lua
+++ b/src/lua/clock.lua
@@ -7,10 +7,10 @@ ffi.cdef[[
     double clock_monotonic(void);
     double clock_process(void);
     double clock_thread(void);
-    uint64_t clock_realtime64(void);
-    uint64_t clock_monotonic64(void);
-    uint64_t clock_process64(void);
-    uint64_t clock_thread64(void);
+    int64_t clock_realtime64(void);
+    int64_t clock_monotonic64(void);
+    int64_t clock_process64(void);
+    int64_t clock_thread64(void);
 ]]
 
 local C = ffi.C
diff --git a/src/lua/fiber.lua b/src/lua/fiber.lua
index b14117714989bd9fb284f3378f29b6204309fe0b..a788aa37d37240369f99d4985b4e900686271e4a 100644
--- a/src/lua/fiber.lua
+++ b/src/lua/fiber.lua
@@ -5,11 +5,11 @@ local ffi = require('ffi')
 ffi.cdef[[
 double
 fiber_time(void);
-uint64_t
+int64_t
 fiber_time64(void);
 double
 fiber_clock(void);
-uint64_t
+int64_t
 fiber_clock64(void);
 ]]
 local C = ffi.C
diff --git a/test/app-tap/clock.test.lua b/test/app-tap/clock.test.lua
index d1ea4f0a8e1dd2fc9f32d4d7bdfe1835e5289a76..5f25d77bbc96e9fa377ada3ef765cb37df66ab65 100755
--- a/test/app-tap/clock.test.lua
+++ b/test/app-tap/clock.test.lua
@@ -1,16 +1,72 @@
 #!/usr/bin/env tarantool
 
 local clock = require("clock")
-local test = require("tap").test("csv")
-test:plan(10)
+local fiber = require("fiber")
+local test = require("tap").test("clock")
+
+test:plan(36)
+
+test:ok(clock.time() > 0, "time")
 test:ok(clock.realtime() > 0, "realtime")
 test:ok(clock.thread() > 0, "thread")
 test:ok(clock.monotonic() > 0, "monotonic")
 test:ok(clock.proc() > 0, "proc")
+test:ok(fiber.time() > 0, "fiber.time")
+test:ok(fiber.clock() > 0, "fiber.clock")
+test:ok(clock.time64() > 0, "time64")
 test:ok(clock.realtime64() > 0, "realtime64")
 test:ok(clock.thread64() > 0, "thread64")
 test:ok(clock.monotonic64() > 0, "monotonic64")
 test:ok(clock.proc64() > 0, "proc64")
+test:ok(fiber.time64() > 0, "fiber.time64")
+test:ok(fiber.clock64() > 0, "fiber.clock64")
 
 test:ok(clock.monotonic() <= clock.monotonic(), "time is monotonic")
+test:ok(clock.monotonic64() <= clock.monotonic64(), "time is monotonic")
 test:ok(math.abs(clock.realtime() - os.time()) < 2, "clock.realtime ~ os.time")
+
+test:ok(fiber.time() == fiber.time(), "fiber.time is cached")
+test:ok(fiber.time64() == fiber.time64(), "fiber.time64 is cached")
+
+test:ok(fiber.clock() == fiber.clock(), "fiber.clock is cached")
+test:ok(fiber.clock64() == fiber.clock64(), "fiber.clock64 is cached")
+test:ok(fiber.clock() < (fiber.yield() or 0) + fiber.clock(),
+        "fiber.clock is growing after yield")
+test:ok(fiber.clock64() < (fiber.yield() or 0) + fiber.clock64(),
+        "fiber.clock64 is growing after yield")
+
+test:ok(math.abs(fiber.time() - tonumber(fiber.time64())/1e6) < 1,
+        "fiber.time64 is in microseconds")
+test:ok(math.abs(fiber.clock() - tonumber(fiber.clock64())/1e6) < 1,
+        "fiber.clock64 is in microseconds")
+
+test:ok(math.abs(clock.time() - tonumber(clock.time64())/1e9) < 1,
+        "clock.time64 is in nanoseconds")
+test:ok(math.abs(clock.realtime() - tonumber(clock.realtime64())/1e9) < 1,
+        "clock.realtime64 is in nanoseconds")
+test:ok(math.abs(clock.thread() - tonumber(clock.thread64())/1e9) < 1,
+        "clock.thread64 is in nanoseconds")
+test:ok(math.abs(clock.proc() - tonumber(clock.proc64())/1e9) < 1,
+        "clock.proc64 is in nanoseconds")
+
+local function subtract_future(func)
+    local ts1 = func()
+    fiber.sleep(0.001)
+    return ts1 - func()
+end
+test:ok(subtract_future(clock.time64) < 0,
+        "clock.time64() can be subtracted")
+test:ok(subtract_future(clock.realtime64) < 0,
+        "clock.realtime64() can be subtracted")
+test:ok(subtract_future(clock.thread64) < 0,
+        "clock.thread64() can be subtracted")
+test:ok(subtract_future(clock.monotonic64) < 0,
+        "clock.monotonic64() can be subtracted")
+test:ok(subtract_future(clock.proc64) < 0,
+        "clock.proc64() can be subtracted")
+test:ok(subtract_future(fiber.time64) < 0,
+        "fiber.time64 can be subtracted")
+test:ok(subtract_future(fiber.clock64) < 0,
+        "fiber.clock64 can be subtracted")
+
+os.exit(test:check() and 0 or 1)