diff --git a/example/generator/odp_generator.c b/example/generator/odp_generator.c
index 2de530d..ddae7c0 100644
--- a/example/generator/odp_generator.c
+++ b/example/generator/odp_generator.c
@@ -594,7 +594,7 @@ static void print_global_stats(int num_workers)
 	while (odp_thrmask_worker(&thrd_mask) < num_workers)
 		continue;
 
-	wait = odp_time_local_from_ns(verbose_interval * ODP_TIME_SEC_IN_NS);
+	wait = odp_time_local_val(verbose_interval * ODP_TIME_SEC_IN_NS);
 	next = odp_time_sum(odp_time_local(), wait);
 
 	while (odp_thrmask_worker(&thrd_mask) == num_workers) {
diff --git a/include/odp/api/time.h b/include/odp/api/time.h
index 3228879..9a5916b 100644
--- a/include/odp/api/time.h
+++ b/include/odp/api/time.h
@@ -37,15 +37,16 @@ extern "C" {
  */
 
 /**
- * @def ODP_TIME_NULL
- * Zero time stamp
+ * @def ODP_TIME_NULL_VAL
+ * Zero time interval
  */
 
 /**
  * Current local time
  *
  * Returns current local time stamp value. The local time source provides high
- * resolution time and is monotonic.
+ * resolution time and is monotonic. The local time is wall time that means it's
+ * local for each thread.
  *
  * @return Local time stamp.
  */
@@ -57,7 +58,9 @@ odp_time_t odp_time_local(void);
  * @param t2    Second time stamp
  * @param t1    First time stamp
  *
- * @return Difference of time stamps
+ * @return Difference of time stamps ot intervals. Diff of timestamp and
+ * interval results in timestamp.
+ *
  */
 odp_time_t odp_time_diff(odp_time_t t2, odp_time_t t1);
 
@@ -68,6 +71,9 @@ odp_time_t odp_time_diff(odp_time_t t2, odp_time_t t1);
  * @param t2    Time stamp
  *
  * @return Sum of time stamps
+ *
+ * @note Sum of two timestamp shouldn't be used, only timestamp and interval or
+ * two intervals. Sum of timestamp and interval results in timestamp.
  */
 odp_time_t odp_time_sum(odp_time_t t1, odp_time_t t2);
 
@@ -90,12 +96,28 @@ uint64_t odp_time_to_ns(odp_time_t time);
 odp_time_t odp_time_local_from_ns(uint64_t ns);
 
 /**
+ * Convert nanoseconds to interval of local time
+ *
+ * @param ns	Time in nanoseconds
+ */
+odp_time_t odp_time_local_val(uint64_t ns);
+
+/**
+ * Convert time interval to nanoseconds
+ *
+ * @param val	Time interval
+ */
+uint64_t odp_time_val_to_ns(odp_time_t val);
+
+/**
  * Compare two times
  *
  * @param t2    Second time
  * @param t1    First time
  *
  * @retval <0 if t2 < t1, >0 if t1 = t2, 1 if t2 > t1
+ *
+ * @note Compare two timestamps or two intervals
  */
 int odp_time_cmp(odp_time_t t2, odp_time_t t1);
 
diff --git a/platform/linux-generic/include/odp/plat/time_types.h b/platform/linux-generic/include/odp/plat/time_types.h
index e5765ec..ccf6580 100644
--- a/platform/linux-generic/include/odp/plat/time_types.h
+++ b/platform/linux-generic/include/odp/plat/time_types.h
@@ -23,9 +23,9 @@ extern "C" {
 
 typedef struct timespec odp_time_t;
 
-odp_time_t odp_time_null(void);
+odp_time_t odp_time_null_val(void);
 
-#define ODP_TIME_NULL	odp_time_null()
+#define ODP_TIME_NULL_VAL	odp_time_null_val()
 
 /**
  * @}
diff --git a/platform/linux-generic/include/odp_internal.h b/platform/linux-generic/include/odp_internal.h
index 14ba159..baff135 100644
--- a/platform/linux-generic/include/odp_internal.h
+++ b/platform/linux-generic/include/odp_internal.h
@@ -79,6 +79,8 @@ int odp_schedule_term_local(void);
 int odp_timer_init_global(void);
 int odp_timer_disarm_all(void);
 
+int odp_time_local_init(void);
+
 void _odp_flush_caches(void);
 
 #ifdef __cplusplus
diff --git a/platform/linux-generic/odp_schedule.c b/platform/linux-generic/odp_schedule.c
index 3017876..6a3440c 100644
--- a/platform/linux-generic/odp_schedule.c
+++ b/platform/linux-generic/odp_schedule.c
@@ -603,7 +603,7 @@ static int schedule_loop(odp_queue_t *out_queue, uint64_t wait,
 			break;
 
 		if (first) {
-			wtime = odp_time_local_from_ns(wait);
+			wtime = odp_time_local_val(wait);
 			next = odp_time_sum(odp_time_local(), wtime);
 			first = 0;
 			continue;
diff --git a/platform/linux-generic/odp_time.c b/platform/linux-generic/odp_time.c
index 73b2dcb..c82f974 100644
--- a/platform/linux-generic/odp_time.c
+++ b/platform/linux-generic/odp_time.c
@@ -11,6 +11,30 @@
 #include <odp/hints.h>
 #include <odp_debug_internal.h>
 
+static __thread uint64_t start_local_ns;
+
+static inline
+odp_time_t _odp_time_local_val(uint64_t ns)
+{
+	struct timespec val;
+
+	val.tv_sec = ns / ODP_TIME_SEC_IN_NS;
+	val.tv_nsec = ns % ODP_TIME_SEC_IN_NS;
+
+	return val;
+}
+
+static inline
+uint64_t _odp_time_val_to_ns(odp_time_t val)
+{
+	uint64_t ns;
+
+	ns = val.tv_sec * ODP_TIME_SEC_IN_NS;
+	ns += val.tv_nsec;
+
+	return ns;
+}
+
 odp_time_t odp_time_local(void)
 {
 	int ret;
@@ -38,24 +62,30 @@ odp_time_t odp_time_diff(odp_time_t t2, odp_time_t t1)
 	return time;
 }
 
+uint64_t odp_time_val_to_ns(odp_time_t val)
+{
+	return _odp_time_val_to_ns(val);
+}
+
 uint64_t odp_time_to_ns(odp_time_t time)
 {
 	uint64_t ns;
 
-	ns = time.tv_sec * ODP_TIME_SEC_IN_NS;
-	ns += time.tv_nsec;
+	ns = _odp_time_val_to_ns(time);
+	ns -= start_local_ns;
 
 	return ns;
 }
 
 odp_time_t odp_time_local_from_ns(uint64_t ns)
 {
-	struct timespec time;
-
-	time.tv_sec = ns / ODP_TIME_SEC_IN_NS;
-	time.tv_nsec = ns % ODP_TIME_SEC_IN_NS;
+	ns += start_local_ns;
+	return _odp_time_local_val(ns);
+}
 
-	return time;
+odp_time_t odp_time_local_val(uint64_t ns)
+{
+	return _odp_time_local_val(ns);
 }
 
 int odp_time_cmp(odp_time_t t2, odp_time_t t1)
@@ -96,11 +126,21 @@ uint64_t odp_time_to_u64(odp_time_t time)
 
 	resolution = (uint64_t)tres.tv_nsec;
 
-	return odp_time_to_ns(time) / resolution;
+	return _odp_time_val_to_ns(time) / resolution;
 }
 
-odp_time_t odp_time_null(void)
+odp_time_t odp_time_null_val(void)
 {
 	return (struct timespec) {0, 0};
 }
 
+int odp_time_local_init(void)
+{
+	int ret;
+	struct timespec time;
+
+	ret = clock_gettime(CLOCK_MONOTONIC_RAW, &time);
+	start_local_ns = ret ? 0 : _odp_time_val_to_ns(time);
+
+	return ret;
+}
diff --git a/test/performance/odp_pktio_perf.c b/test/performance/odp_pktio_perf.c
index 3d37ae1..e1f65e4 100644
--- a/test/performance/odp_pktio_perf.c
+++ b/test/performance/odp_pktio_perf.c
@@ -310,7 +310,7 @@ static void *run_thread_tx(void *arg)
 	uint32_t batch_len;
 	int unsent_pkts = 0;
 	odp_event_t  tx_event[BATCH_LEN_MAX];
-	odp_time_t idle_start = ODP_TIME_NULL;
+	odp_time_t idle_start = ODP_TIME_NULL_VAL;
 
 	thread_args_t *targs = arg;
 
@@ -328,10 +328,10 @@ static void *run_thread_tx(void *arg)
 	if (outq == ODP_QUEUE_INVALID)
 		LOG_ABORT("Failed to get output queue for thread %d\n", thr_id);
 
-	burst_gap = odp_time_local_from_ns(
+	burst_gap = odp_time_local_val(
 			ODP_TIME_SEC_IN_NS / (targs->pps / targs->batch_len));
 	send_duration =
-		odp_time_local_from_ns(targs->duration * ODP_TIME_SEC_IN_NS);
+		odp_time_local_val(targs->duration * ODP_TIME_SEC_IN_NS);
 
 	odp_barrier_wait(&globals->tx_barrier);
 
@@ -343,18 +343,18 @@ static void *run_thread_tx(void *arg)
 
 		if (odp_time_cmp(burst_gap_end, cur_time) > 0) {
 			cur_time = odp_time_local();
-			if (!odp_time_cmp(idle_start, ODP_TIME_NULL))
+			if (!odp_time_cmp(idle_start, ODP_TIME_NULL_VAL))
 				idle_start = cur_time;
 			continue;
 		}
 
-		if (odp_time_cmp(idle_start, ODP_TIME_NULL) > 0) {
+		if (odp_time_cmp(idle_start, ODP_TIME_NULL_VAL) > 0) {
 			odp_time_t diff = odp_time_diff(cur_time, idle_start);
 
 			stats->s.idle_ticks =
 				odp_time_sum(diff, stats->s.idle_ticks);
 
-			idle_start = ODP_TIME_NULL;
+			idle_start = ODP_TIME_NULL_VAL;
 		}
 
 		burst_gap_end = odp_time_sum(burst_gap_end, burst_gap);
@@ -375,7 +375,7 @@ static void *run_thread_tx(void *arg)
 	       " AllocFail %-6"PRIu64" Idle %"PRIu64"ms\n",
 	       thr_id, stats->s.tx_cnt,
 	       stats->s.enq_failures, stats->s.alloc_failures,
-	       odp_time_to_ns(stats->s.idle_ticks) /
+	       odp_time_val_to_ns(stats->s.idle_ticks) /
 	       (uint64_t)ODP_TIME_MSEC_IN_NS);
 
 	return NULL;
@@ -595,13 +595,13 @@ static int setup_txrx_masks(odp_cpumask_t *thd_mask_tx,
  */
 static void busy_loop_ns(uint64_t wait_ns)
 {
-	odp_time_t diff;
-	odp_time_t start_time = odp_time_local();
-	odp_time_t wait = odp_time_local_from_ns(wait_ns);
+	odp_time_t wait = odp_time_local_val(wait_ns);
+	odp_time_t cur = odp_time_local();
+	odp_time_t end_time = odp_time_sum(cur, wait);
 
 	do {
-		diff = odp_time_diff(odp_time_local(), start_time);
-	} while (odp_time_cmp(wait, diff) > 0);
+		cur = odp_time_local();
+	} while (odp_time_cmp(end_time, cur) > 0);
 }
 
 /*
diff --git a/test/validation/pktio/pktio.c b/test/validation/pktio/pktio.c
index aee3a7d..6d7e954 100644
--- a/test/validation/pktio/pktio.c
+++ b/test/validation/pktio/pktio.c
@@ -339,7 +339,7 @@ static odp_event_t queue_deq_wait_time(odp_queue_t queue, uint64_t ns)
 	odp_time_t wait, end;
 	odp_event_t ev;
 
-	wait = odp_time_local_from_ns(ns);
+	wait = odp_time_local_val(ns);
 	end = odp_time_sum(odp_time_local(), wait);
 	do {
 		ev = odp_queue_deq(queue);
@@ -359,7 +359,7 @@ static odp_packet_t wait_for_packet(pktio_info_t *pktio_rx,
 	uint64_t wait;
 
 	wait = odp_schedule_wait_time(ns);
-	wait_time = odp_time_local_from_ns(ns);
+	wait_time = odp_time_local_val(ns);
 	end = odp_time_sum(odp_time_local(), wait_time);
 	do {
 		pkt = ODP_PACKET_INVALID;
diff --git a/test/validation/scheduler/scheduler.c b/test/validation/scheduler/scheduler.c
index a562a8a..58f93e0 100644
--- a/test/validation/scheduler/scheduler.c
+++ b/test/validation/scheduler/scheduler.c
@@ -718,7 +718,7 @@ static void *schedule_common_(void *arg)
 			CU_ASSERT(from != ODP_QUEUE_INVALID);
 			if (locked) {
 				int cnt;
-				odp_time_t time = ODP_TIME_NULL;
+				odp_time_t time = ODP_TIME_NULL_VAL;
 				/* Do some work here to keep the thread busy */
 				for (cnt = 0; cnt < 1000; cnt++)
 					time = odp_time_sum(time,
diff --git a/test/validation/time/time.c b/test/validation/time/time.c
index 8f5dfe6..4386c34 100644
--- a/test/validation/time/time.c
+++ b/test/validation/time/time.c
@@ -18,6 +18,7 @@ void time_test_odp_conversion(void)
 	odp_time_t time;
 	uint64_t upper_limit, lower_limit;
 
+	/* test timestamp conversions */
 	ns1 = 100;
 	time = odp_time_local_from_ns(ns1);
 
@@ -34,8 +35,18 @@ void time_test_odp_conversion(void)
 
 	ns2 = odp_time_to_ns(time);
 
-	/* need to check within arithmetic tolerance that the same
-	 * value in ns is returned after conversions */
+	/*
+	 * need to check within arithmetic tolerance that the same
+	 * value in ns is returned after conversions
+	 */
+	upper_limit = ns1 + TOLERANCE;
+	lower_limit = ns1 - TOLERANCE;
+	CU_ASSERT((ns2 <= upper_limit) && (ns2 >= lower_limit));
+
+	/* test time vals conversions */
+	time = odp_time_local_val(ns1);
+	ns2 = odp_time_val_to_ns(time);
+
 	upper_limit = ns1 + TOLERANCE;
 	lower_limit = ns1 - TOLERANCE;
 	CU_ASSERT((ns2 <= upper_limit) && (ns2 >= lower_limit));
@@ -75,6 +86,7 @@ void time_test_odp_cmp(void)
 	volatile int count = 0;
 	odp_time_t t1, t2, t3;
 
+	/* test compare of timestamps */
 	t1 = odp_time_local();
 
 	while (count < BUSY_LOOP_CNT) {
@@ -105,8 +117,23 @@ void time_test_odp_cmp(void)
 	CU_ASSERT(odp_time_cmp(t2, t1) > 0);
 	CU_ASSERT(odp_time_cmp(t1, t2) < 0);
 
-	t1 = odp_time_local_from_ns(0);
-	CU_ASSERT(odp_time_cmp(t1, ODP_TIME_NULL) == 0);
+	t1 = odp_time_local_val(0);
+	CU_ASSERT(odp_time_cmp(t1, ODP_TIME_NULL_VAL) == 0);
+
+	/* test compare of time intervals */
+	t1 = odp_time_local_val(15);
+	t2 = odp_time_local_val(23);
+	t3 = odp_time_local_val(51);
+
+	CU_ASSERT(odp_time_cmp(t2, t1) > 0);
+	CU_ASSERT(odp_time_cmp(t3, t2) > 0);
+	CU_ASSERT(odp_time_cmp(t3, t1) > 0);
+	CU_ASSERT(odp_time_cmp(t1, t2) < 0);
+	CU_ASSERT(odp_time_cmp(t2, t3) < 0);
+	CU_ASSERT(odp_time_cmp(t1, t3) < 0);
+	CU_ASSERT(odp_time_cmp(t1, t1) == 0);
+	CU_ASSERT(odp_time_cmp(t2, t2) == 0);
+	CU_ASSERT(odp_time_cmp(t3, t3) == 0);
 }
 
 /* check that a time difference gives a reasonable result */
@@ -129,12 +156,12 @@ void time_test_odp_diff(void)
 	CU_ASSERT(odp_time_cmp(t2, t1) > 0);
 
 	diff = odp_time_diff(t2, t1);
-	CU_ASSERT(odp_time_cmp(diff, ODP_TIME_NULL) > 0);
+	CU_ASSERT(odp_time_cmp(diff, ODP_TIME_NULL_VAL) > 0);
 
 	ns1 = odp_time_to_ns(t1);
 	ns2 = odp_time_to_ns(t2);
 	ns = ns2 - ns1;
-	nsdiff = odp_time_to_ns(diff);
+	nsdiff = odp_time_val_to_ns(diff);
 
 	upper_limit = ns + TOLERANCE;
 	lower_limit = ns - TOLERANCE;
@@ -142,11 +169,11 @@ void time_test_odp_diff(void)
 
 	/* test timestamp and interval diff */
 	ns1 = 54;
-	t1 = odp_time_local_from_ns(ns1);
+	t1 = odp_time_local_val(ns1);
 	ns = ns2 - ns1;
 
 	diff = odp_time_diff(t2, t1);
-	CU_ASSERT(odp_time_cmp(diff, ODP_TIME_NULL) > 0);
+	CU_ASSERT(odp_time_cmp(diff, ODP_TIME_NULL_VAL) > 0);
 	nsdiff = odp_time_to_ns(diff);
 
 	upper_limit = ns + TOLERANCE;
@@ -157,10 +184,10 @@ void time_test_odp_diff(void)
 	ns2 = 60 * 10 * ODP_TIME_SEC_IN_NS;
 	ns = ns2 - ns1;
 
-	t2 = odp_time_local_from_ns(ns2);
+	t2 = odp_time_local_val(ns2);
 	diff = odp_time_diff(t2, t1);
-	CU_ASSERT(odp_time_cmp(diff, ODP_TIME_NULL) > 0);
-	nsdiff = odp_time_to_ns(diff);
+	CU_ASSERT(odp_time_cmp(diff, ODP_TIME_NULL_VAL) > 0);
+	nsdiff = odp_time_val_to_ns(diff);
 
 	upper_limit = ns + TOLERANCE;
 	lower_limit = ns - TOLERANCE;
@@ -168,9 +195,9 @@ void time_test_odp_diff(void)
 
 	/* same time has to diff to 0 */
 	diff = odp_time_diff(t2, t2);
-	CU_ASSERT(odp_time_cmp(diff, ODP_TIME_NULL) == 0);
+	CU_ASSERT(odp_time_cmp(diff, ODP_TIME_NULL_VAL) == 0);
 
-	diff = odp_time_diff(t2, ODP_TIME_NULL);
+	diff = odp_time_diff(t2, ODP_TIME_NULL_VAL);
 	CU_ASSERT(odp_time_cmp(t2, diff) == 0);
 }
 
@@ -184,12 +211,12 @@ void time_test_odp_sum(void)
 	/* sum timestamp and interval */
 	t1 = odp_time_local();
 	ns2 = 103;
-	t2 = odp_time_local_from_ns(ns2);
+	t2 = odp_time_local_val(ns2);
 	ns1 = odp_time_to_ns(t1);
 	ns = ns1 + ns2;
 
 	sum = odp_time_sum(t2, t1);
-	CU_ASSERT(odp_time_cmp(sum, ODP_TIME_NULL) > 0);
+	CU_ASSERT(odp_time_cmp(sum, ODP_TIME_NULL_VAL) > 0);
 	nssum = odp_time_to_ns(sum);
 
 	upper_limit = ns + TOLERANCE;
@@ -198,19 +225,19 @@ void time_test_odp_sum(void)
 
 	/* sum intervals */
 	ns1 = 60 * 13 * ODP_TIME_SEC_IN_NS;
-	t1 = odp_time_local_from_ns(ns1);
+	t1 = odp_time_local_val(ns1);
 	ns = ns1 + ns2;
 
 	sum = odp_time_sum(t2, t1);
-	CU_ASSERT(odp_time_cmp(sum, ODP_TIME_NULL) > 0);
-	nssum = odp_time_to_ns(sum);
+	CU_ASSERT(odp_time_cmp(sum, ODP_TIME_NULL_VAL) > 0);
+	nssum = odp_time_val_to_ns(sum);
 
 	upper_limit = ns + TOLERANCE;
 	lower_limit = ns - TOLERANCE;
 	CU_ASSERT((nssum <= upper_limit) && (nssum >= lower_limit));
 
 	/* test on 0 */
-	sum = odp_time_sum(t2, ODP_TIME_NULL);
+	sum = odp_time_sum(t2, ODP_TIME_NULL_VAL);
 	CU_ASSERT(odp_time_cmp(t2, sum) == 0);
 }
 
@@ -235,7 +262,7 @@ void time_test_odp_to_u64(void)
 
 	CU_ASSERT(val2 > val1);
 
-	val1 = odp_time_to_u64(ODP_TIME_NULL);
+	val1 = odp_time_to_u64(ODP_TIME_NULL_VAL);
 	CU_ASSERT(val1 == 0);
 }
 
