diff --git a/examples/client.cc b/examples/client.cc
index fc0fc002de72c2ca4095f1f55b8aab7cc6db04e9..9dbd1f73c760bea5685a6d65c3bac422839f811f 100644
--- a/examples/client.cc
+++ b/examples/client.cc
@@ -295,7 +295,7 @@ int Client::tls_handshake() {
 int Client::feed_data(const uint8_t *data, size_t datalen) {
   int rv;
 
-  rv = ngtcp2_conn_recv(conn_, data, datalen);
+  rv = ngtcp2_conn_recv(conn_, data, datalen, util::timestamp());
   if (rv != 0) {
     std::cerr << "ngtcp2_conn_recv: " << rv << std::endl;
     return -1;
@@ -324,7 +324,7 @@ int Client::on_read() {
 
 int Client::on_write() {
   std::array<uint8_t, 1280> buf;
-  auto n = ngtcp2_conn_send(conn_, buf.data(), buf.size());
+  auto n = ngtcp2_conn_send(conn_, buf.data(), buf.size(), util::timestamp());
   if (n < 0) {
     return -1;
   }
diff --git a/examples/server.cc b/examples/server.cc
index dcde19d96644bf92d1ef8091b1ecbb613f0b6a1f..8e68d134a6609c26a0720b8d5e4d35b8a4c1d0c9 100644
--- a/examples/server.cc
+++ b/examples/server.cc
@@ -319,7 +319,7 @@ void Handler::write_client_handshake(const uint8_t *data, size_t datalen) {
 int Handler::feed_data(const uint8_t *data, size_t datalen) {
   int rv;
 
-  rv = ngtcp2_conn_recv(conn_, data, datalen);
+  rv = ngtcp2_conn_recv(conn_, data, datalen, util::timestamp());
   if (rv != 0) {
     std::cerr << "ngtcp2_conn_recv: " << rv << std::endl;
     return -1;
@@ -351,7 +351,7 @@ int Handler::on_write() {
   std::array<uint8_t, 1280> buf;
 
   for (;;) {
-    auto n = ngtcp2_conn_send(conn_, buf.data(), buf.size());
+    auto n = ngtcp2_conn_send(conn_, buf.data(), buf.size(), util::timestamp());
     if (n < 0) {
       return -1;
     }
diff --git a/examples/util.cc b/examples/util.cc
index 417470723af5249a70503bc5e821f0afcd46da83..4bc7dcd159fc8f2d5d5a905e671caba0530d2096 100644
--- a/examples/util.cc
+++ b/examples/util.cc
@@ -24,6 +24,8 @@
  */
 #include "util.h"
 
+#include <chrono>
+
 namespace ngtcp2 {
 
 namespace util {
@@ -60,6 +62,12 @@ std::mt19937 make_mt19937() {
   return std::mt19937(rd());
 }
 
+ngtcp2_tstamp timestamp() {
+  return std::chrono::duration_cast<std::chrono::microseconds>(
+             std::chrono::steady_clock::now().time_since_epoch())
+      .count();
+}
+
 } // namespace util
 
 } // namespace ngtcp2
diff --git a/examples/util.h b/examples/util.h
index 945bc943b24c5c2b0f6aa95ab9b84766415e77ee..4b2c6daa754c56a72c51337515b5d63d03edfdf9 100644
--- a/examples/util.h
+++ b/examples/util.h
@@ -44,6 +44,8 @@ std::string format_hex(const uint8_t *s, size_t len);
 
 std::mt19937 make_mt19937();
 
+ngtcp2_tstamp timestamp();
+
 } // namespace util
 
 } // namespace ngtcp2;
diff --git a/lib/includes/ngtcp2/ngtcp2.h b/lib/includes/ngtcp2/ngtcp2.h
index 4cd7760d502c8c982f7c8e352d8ade4bfcd6a2f1..31cca1f47158f2c5fcf357d0a0db2d06ea7d4b3c 100644
--- a/lib/includes/ngtcp2/ngtcp2.h
+++ b/lib/includes/ngtcp2/ngtcp2.h
@@ -207,6 +207,9 @@ typedef enum {
   NGTCP2_FRAME_STREAM = 0xc0
 } ngtcp2_frame_type;
 
+/*
+ * ngtcp2_tstamp is a timestamp with microsecond resolution.
+ */
 typedef uint64_t ngtcp2_tstamp;
 
 typedef struct {
@@ -526,10 +529,10 @@ NGTCP2_EXTERN int ngtcp2_conn_server_new(ngtcp2_conn **pconn, uint64_t conn_id,
 NGTCP2_EXTERN void ngtcp2_conn_del(ngtcp2_conn *conn);
 
 NGTCP2_EXTERN int ngtcp2_conn_recv(ngtcp2_conn *conn, const uint8_t *pkt,
-                                   size_t pktlen);
+                                   size_t pktlen, ngtcp2_tstamp ts);
 
 NGTCP2_EXTERN ssize_t ngtcp2_conn_send(ngtcp2_conn *conn, uint8_t *dest,
-                                       size_t destlen);
+                                       size_t destlen, ngtcp2_tstamp ts);
 
 NGTCP2_EXTERN int ngtcp2_conn_handshake_completed(ngtcp2_conn *conn);
 
diff --git a/lib/ngtcp2_conn.c b/lib/ngtcp2_conn.c
index 7cb86661dd02b4bc91a3b7c93f04959e4f2bce43..9f679fb74d1d0387a3cab965b15668dc86827b03 100644
--- a/lib/ngtcp2_conn.c
+++ b/lib/ngtcp2_conn.c
@@ -399,8 +399,8 @@ static ssize_t ngtcp2_conn_send_client_initial(ngtcp2_conn *conn, uint8_t *dest,
 }
 
 static ssize_t ngtcp2_conn_send_client_cleartext(ngtcp2_conn *conn,
-                                                 uint8_t *dest,
-                                                 size_t destlen) {
+                                                 uint8_t *dest, size_t destlen,
+                                                 ngtcp2_tstamp ts) {
   const uint8_t *payload;
   ssize_t payloadlen;
   ngtcp2_frame ackfr;
@@ -408,7 +408,7 @@ static ssize_t ngtcp2_conn_send_client_cleartext(ngtcp2_conn *conn,
   int rv;
 
   ackfr.type = 0;
-  rv = conn_create_ack_frame(conn, &ackfr.ack, 0 /* timestamp */);
+  rv = conn_create_ack_frame(conn, &ackfr.ack, ts);
   if (rv != 0) {
     return rv;
   }
@@ -436,7 +436,8 @@ static ssize_t ngtcp2_conn_send_client_cleartext(ngtcp2_conn *conn,
 
 static ssize_t ngtcp2_conn_send_server_cleartext(ngtcp2_conn *conn,
                                                  uint8_t *dest, size_t destlen,
-                                                 int initial) {
+                                                 int initial,
+                                                 ngtcp2_tstamp ts) {
   uint64_t pkt_num = 0;
   const uint8_t *payload;
   ssize_t payloadlen;
@@ -445,7 +446,7 @@ static ssize_t ngtcp2_conn_send_server_cleartext(ngtcp2_conn *conn,
   int rv;
 
   ackfr.type = 0;
-  rv = conn_create_ack_frame(conn, &ackfr.ack, 0 /* timestamp */);
+  rv = conn_create_ack_frame(conn, &ackfr.ack, ts);
   if (rv != 0) {
     return rv;
   }
@@ -481,7 +482,8 @@ static ssize_t ngtcp2_conn_send_server_cleartext(ngtcp2_conn *conn,
                                    ackfr.type == 0 ? NULL : &ackfr, tx_buf);
 }
 
-ssize_t ngtcp2_conn_send(ngtcp2_conn *conn, uint8_t *dest, size_t destlen) {
+ssize_t ngtcp2_conn_send(ngtcp2_conn *conn, uint8_t *dest, size_t destlen,
+                         ngtcp2_tstamp ts) {
   ssize_t nwrite = 0;
   int rv;
 
@@ -494,7 +496,7 @@ ssize_t ngtcp2_conn_send(ngtcp2_conn *conn, uint8_t *dest, size_t destlen) {
     conn->state = NGTCP2_CS_CLIENT_CI_SENT;
     break;
   case NGTCP2_CS_CLIENT_SC_RECVED:
-    nwrite = ngtcp2_conn_send_client_cleartext(conn, dest, destlen);
+    nwrite = ngtcp2_conn_send_client_cleartext(conn, dest, destlen, ts);
     if (nwrite < 0) {
       break;
     }
@@ -507,14 +509,14 @@ ssize_t ngtcp2_conn_send(ngtcp2_conn *conn, uint8_t *dest, size_t destlen) {
     }
     break;
   case NGTCP2_CS_SERVER_CI_RECVED:
-    nwrite = ngtcp2_conn_send_server_cleartext(conn, dest, destlen, 1);
+    nwrite = ngtcp2_conn_send_server_cleartext(conn, dest, destlen, 1, ts);
     if (nwrite < 0) {
       break;
     }
     conn->state = NGTCP2_CS_SERVER_SC_SENT;
     break;
   case NGTCP2_CS_SERVER_SC_SENT:
-    nwrite = ngtcp2_conn_send_server_cleartext(conn, dest, destlen, 0);
+    nwrite = ngtcp2_conn_send_server_cleartext(conn, dest, destlen, 0, ts);
     if (nwrite < 0) {
       break;
     }
@@ -526,7 +528,8 @@ ssize_t ngtcp2_conn_send(ngtcp2_conn *conn, uint8_t *dest, size_t destlen) {
 
 static int ngtcp2_conn_recv_cleartext(ngtcp2_conn *conn, uint8_t exptype,
                                       const uint8_t *pkt, size_t pktlen,
-                                      int server, int initial) {
+                                      int server, int initial,
+                                      ngtcp2_tstamp ts) {
   ssize_t nread;
   ngtcp2_pkt_hd hd;
   ngtcp2_frame fr;
@@ -607,7 +610,7 @@ static int ngtcp2_conn_recv_cleartext(ngtcp2_conn *conn, uint8_t exptype,
   }
 
   if (require_ack) {
-    rv = ngtcp2_conn_sched_ack(conn, hd.pkt_num, 0 /* timestamp */);
+    rv = ngtcp2_conn_sched_ack(conn, hd.pkt_num, ts);
     if (rv != 0) {
       return rv;
     }
@@ -616,7 +619,8 @@ static int ngtcp2_conn_recv_cleartext(ngtcp2_conn *conn, uint8_t exptype,
   return 0;
 }
 
-int ngtcp2_conn_recv(ngtcp2_conn *conn, const uint8_t *pkt, size_t pktlen) {
+int ngtcp2_conn_recv(ngtcp2_conn *conn, const uint8_t *pkt, size_t pktlen,
+                     ngtcp2_tstamp ts) {
   int rv = 0;
 
   if (pktlen == 0) {
@@ -634,7 +638,7 @@ int ngtcp2_conn_recv(ngtcp2_conn *conn, const uint8_t *pkt, size_t pktlen) {
   case NGTCP2_CS_CLIENT_CI_SENT:
     /* TODO Handle Version Negotiation */
     rv = ngtcp2_conn_recv_cleartext(conn, NGTCP2_PKT_SERVER_CLEARTEXT, pkt,
-                                    pktlen, 0, 1);
+                                    pktlen, 0, 1, ts);
     if (rv < 0) {
       break;
     }
@@ -642,14 +646,14 @@ int ngtcp2_conn_recv(ngtcp2_conn *conn, const uint8_t *pkt, size_t pktlen) {
     break;
   case NGTCP2_CS_CLIENT_SC_RECVED:
     rv = ngtcp2_conn_recv_cleartext(conn, NGTCP2_PKT_SERVER_CLEARTEXT, pkt,
-                                    pktlen, 0, 0);
+                                    pktlen, 0, 0, ts);
     if (rv < 0) {
       break;
     }
     break;
   case NGTCP2_CS_SERVER_INITIAL:
     rv = ngtcp2_conn_recv_cleartext(conn, NGTCP2_PKT_CLIENT_INITIAL, pkt,
-                                    pktlen, 1, 1);
+                                    pktlen, 1, 1, ts);
     if (rv < 0) {
       break;
     }
@@ -657,7 +661,7 @@ int ngtcp2_conn_recv(ngtcp2_conn *conn, const uint8_t *pkt, size_t pktlen) {
     break;
   case NGTCP2_CS_SERVER_SC_SENT:
     rv = ngtcp2_conn_recv_cleartext(conn, NGTCP2_PKT_CLIENT_CLEARTEXT, pkt,
-                                    pktlen, 1, 0);
+                                    pktlen, 1, 0, ts);
     if (rv < 0) {
       break;
     }