diff --git a/src/utils/Timer.hpp b/src/utils/Timer.hpp
index c26ea40aa12526b858d6e3a998102c9cd71d7ecd..e1ba542f2cda3969c1af397decc05451e7a3aaa4 100644
--- a/src/utils/Timer.hpp
+++ b/src/utils/Timer.hpp
@@ -1,8 +1,111 @@
 #ifndef TIMER_HPP
 #define TIMER_HPP
 
-#include <Kokkos_Timer.hpp>
+#include <chrono>
+#include <iostream>
 
-using Timer = Kokkos::Timer;
+class Timer
+{
+ public:
+  enum class Status
+  {
+    running,
+    paused,
+    stopped
+  };
+
+ private:
+  std::chrono::time_point<std::chrono::high_resolution_clock> m_start;
+  std::chrono::duration<double> m_elapsed_sum;
+
+  Status m_status;
+
+ public:
+  Status
+  status() const
+  {
+    return m_status;
+  }
+
+  double
+  seconds() const
+  {
+    switch (m_status) {
+    case Status::running: {
+      return (m_elapsed_sum + std::chrono::duration<double>{std::chrono::system_clock::now() - m_start}).count();
+    }
+    case Status::paused:
+    case Status::stopped: {
+      return m_elapsed_sum.count();
+    }
+      // LCOV_EXCL_START
+    default: {
+      return 0;
+    }
+      // LCOV_EXCL_STOP
+    }
+  }
+
+  friend std::ostream&
+  operator<<(std::ostream& os, const Timer& timer)
+  {
+    os << timer.seconds() << 's';
+    return os;
+  }
+
+  void
+  reset()
+  {
+    m_start       = std::chrono::high_resolution_clock::now();
+    m_elapsed_sum = std::chrono::duration<double>::zero();
+  }
+
+  void
+  stop()
+  {
+    m_start       = std::chrono::high_resolution_clock::now();
+    m_elapsed_sum = std::chrono::duration<double>::zero();
+    m_status      = Status::stopped;
+  }
+
+  void
+  pause()
+  {
+    if (m_status == Status::running) {
+      m_elapsed_sum += std::chrono::high_resolution_clock::now() - m_start;
+      m_start  = std::chrono::high_resolution_clock::now();
+      m_status = Status::paused;
+    }
+  }
+
+  void
+  start()
+  {
+    switch (m_status) {
+    case Status::running: {
+      return;
+    }
+    case Status::paused:
+    case Status::stopped: {
+      m_start  = std::chrono::high_resolution_clock::now();
+      m_status = Status::running;
+    }
+    }
+  }
+
+  Timer& operator=(const Timer&) = default;
+  Timer& operator=(Timer&&) = default;
+
+  Timer(const Timer&) = default;
+  Timer(Timer&&)      = default;
+
+  Timer()
+    : m_start{std::chrono::high_resolution_clock::now()},
+      m_elapsed_sum{std::chrono::duration<double>::zero()},
+      m_status{Status::running}
+  {}
+
+  ~Timer() = default;
+};
 
 #endif   // TIMER_HPP
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 0502c414a76e536dc6d2a0d1d49bc260a930b84d..edaa8069ad867aeaa139f68f646519a6219097d0 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -86,6 +86,7 @@ add_executable (unit_tests
   test_RevisionInfo.cpp
   test_SparseMatrixDescriptor.cpp
   test_SymbolTable.cpp
+  test_Timer.cpp
   test_TinyMatrix.cpp
   test_TinyVector.cpp
   test_TupleToVectorProcessor.cpp
diff --git a/tests/test_Timer.cpp b/tests/test_Timer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..adbbd69cbb8aaa4aaad8695b5ce5c0d1ec623f9e
--- /dev/null
+++ b/tests/test_Timer.cpp
@@ -0,0 +1,74 @@
+#include <catch2/catch.hpp>
+
+#include <utils/Timer.hpp>
+
+#include <sstream>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("Timer", "[utils]")
+{
+  SECTION("auto start")
+  {
+    Timer t;
+
+    REQUIRE(t.status() == Timer::Status::running);
+
+    double seconds = t.seconds();
+    REQUIRE(t.seconds() > seconds);
+
+    t.start();
+    seconds = t.seconds();
+    REQUIRE(t.status() == Timer::Status::running);
+
+    REQUIRE(t.seconds() > seconds);
+  }
+
+  SECTION("pause/start")
+  {
+    Timer t1;
+
+    REQUIRE(t1.status() == Timer::Status::running);
+    t1.pause();
+
+    REQUIRE(t1.status() == Timer::Status::paused);
+    const double seconds = t1.seconds();
+
+    REQUIRE(t1.seconds() == seconds);
+
+    std::stringstream os1;
+    os1 << t1;
+
+    Timer t2 = t1;
+    std::stringstream os2;
+    os2 << t2.seconds() << 's';
+
+    REQUIRE(os1.str() == os2.str());
+
+    REQUIRE(t1.seconds() == t2.seconds());
+    t1.start();
+    REQUIRE(t1.status() == Timer::Status::running);
+    REQUIRE(t1.seconds() > t2.seconds());
+
+    t2.reset();
+    REQUIRE(t2.status() == Timer::Status::paused);
+    REQUIRE(t2.seconds() == 0);
+  }
+
+  SECTION("stop/start")
+  {
+    Timer t;
+    REQUIRE(t.status() == Timer::Status::running);
+
+    const double seconds = t.seconds();
+    REQUIRE(seconds > 0);
+
+    t.stop();
+    REQUIRE(t.status() == Timer::Status::stopped);
+    REQUIRE(t.seconds() == 0);
+
+    t.start();
+    REQUIRE(t.status() == Timer::Status::running);
+    REQUIRE(t.seconds() > 0);
+  }
+}