diff --git a/core/sched.cc b/core/sched.cc
index 7ed4e78eba335aa0ab09cbf96d0ae4e2e461fcff..31e4c242d520311b7c6d5d3a025c642c8bb92967 100644
--- a/core/sched.cc
+++ b/core/sched.cc
@@ -980,8 +980,8 @@ timer_list::callback_dispatch::callback_dispatch()
 
 void timer_list::fired()
 {
-    auto now = clock::get()->time();
-    _last = std::numeric_limits<s64>::max();
+    auto now = osv::clock::uptime::now();
+    _last = osv::clock::uptime::time_point::max();
     // don't hold iterators across list iteration, since the list can change
     while (!_list.empty() && _list.begin()->_time <= now) {
         auto j = _list.begin();
@@ -1051,9 +1051,9 @@ void timer_base::expire()
     _t.timer_fired();
 }
 
-void timer_base::set(s64 time)
+void timer_base::set(osv::clock::uptime::time_point time)
 {
-    trace_timer_set(this, time);
+    trace_timer_set(this, time.time_since_epoch().count());
     _state = state::armed;
     _time = time;
     irq_save_lock_type irq_lock;
diff --git a/include/osv/sched.hh b/include/osv/sched.hh
index 643f8ef52c31b3a3337f5181ae75b7f7a40bab86..330d76566566e12b620d99338e928486c94f6427 100644
--- a/include/osv/sched.hh
+++ b/include/osv/sched.hh
@@ -23,6 +23,7 @@
 #include <memory>
 #include <vector>
 #include <osv/rcu.hh>
+#include <osv/clock.hh>
 
 // If RUNTIME_PSEUDOFLOAT, runtime_t is a pseudofloat<>. Otherwise, a float.
 #undef RUNTIME_PSEUDOFLOAT
@@ -169,7 +170,22 @@ public:
 public:
     explicit timer_base(client& t);
     ~timer_base();
-    void set(s64 time);
+    void set(osv::clock::uptime::time_point time);
+    // Set a timer using absolute wall-clock time.
+    // CAVEAT EMPTOR: Internally timers are kept using the monotonic (uptime)
+    // clock, so the wall-time given here is converted to an uptime.
+    // This basically means that the duration until the timer's expiration is
+    // fixed on the call to set(), even if the wall clock is later adjusted.
+    // When the timer expires, the current wall-time may not be identical to
+    // the intended expiration wall-time.
+    void set(osv::clock::wall::time_point time) {
+        set(osv::clock::uptime::time_point(
+                time - osv::clock::wall::boot_time()));
+    }
+    // Temporary, will be removed in a later patch
+    void set(s64 time) {
+        set(osv::clock::wall::time_point(std::chrono::nanoseconds(time)));
+    }
     bool expired() const;
     void cancel();
     friend bool operator<(const timer_base& t1, const timer_base& t2);
@@ -181,7 +197,7 @@ protected:
         free, armed, expired
     };
     state _state = state::free;
-    s64 _time;
+    osv::clock::uptime::time_point _time;
     friend class timer_list;
 };
 
@@ -556,7 +572,8 @@ public:
     void rearm();
 private:
     friend class timer_base;
-    s64 _last = std::numeric_limits<s64>::max();
+    osv::clock::uptime::time_point _last {
+            osv::clock::uptime::time_point::max() };
     bi::set<timer_base, bi::base_hook<bi::set_base_hook<>>> _list;
     class callback_dispatch : private clock_event_callback {
     public: