diff --git a/dumux/common/timeloop.hh b/dumux/common/timeloop.hh
index 3f2e02dad9c74e1c8d7d30cb3dbc1ef7e02e4c0c..3144c45db1a3f62268806cee3be32ddfc39e6971 100644
--- a/dumux/common/timeloop.hh
+++ b/dumux/common/timeloop.hh
@@ -31,6 +31,7 @@
 #include <dune/common/timer.hh>
 #include <dune/common/parallel/collectivecommunication.hh>
 #include <dune/common/parallel/mpihelper.hh>
+#include <dune/common/exceptions.hh>
 
 #include <dumux/common/parameters.hh>
 
@@ -88,18 +89,7 @@ public:
     TimeLoop(Scalar startTime, Scalar dt, Scalar tEnd, bool verbose = true)
     : timer_(false)
     {
-        verbose_ =
-            verbose &&
-            Dune::MPIHelper::getCollectiveCommunication().rank() == 0;
-
-        time_ = startTime;
-        endTime_ = tEnd;
-
-        timeStepSize_ = dt;
-        previousTimeStepSize_ = timeStepSize_;
-        maxTimeStepSize_ = std::numeric_limits<Scalar>::max();
-        timeStepIdx_ = 0;
-        finished_ = false;
+        reset(startTime, dt, tEnd, verbose);
     }
 
     /*!
@@ -113,7 +103,48 @@ public:
     void start()
     {
         timer_.start();
-        cpuTime_ = 0.0;
+    }
+
+    /*!
+     * \brief Tells the time loop to stop tracking the time.
+     * \return the wall clock time (CPU time) spent until now
+     */
+    double stop()
+    {
+        return timer_.stop();
+    }
+
+    /*!
+     * \brief Reset the timer
+     */
+    void resetTimer()
+    {
+        timer_.reset();
+    }
+
+    /*!
+     * \brief Reset the time loop
+     */
+    void reset(Scalar startTime, Scalar dt, Scalar tEnd, bool verbose = true)
+    {
+        verbose_ =
+            verbose &&
+            Dune::MPIHelper::getCollectiveCommunication().rank() == 0;
+
+        time_ = startTime;
+        endTime_ = tEnd;
+
+        timeStepSize_ = dt;
+        lastTimeStepSize_ = 0.0;
+        maxTimeStepSize_ = std::numeric_limits<Scalar>::max();
+        userSetMaxTimeStepSize_ = maxTimeStepSize_;
+        timeStepIdx_ = 0;
+        finished_ = false;
+        timeAfterLastTimeStep_ = 0.0;
+        timeStepWallClockTime_ = 0.0;
+
+        timer_.stop();
+        timer_.reset();
     }
 
     /*!
@@ -123,6 +154,12 @@ public:
     {
         timeStepIdx_++;
         time_ += timeStepSize_;
+        lastTimeStepSize_ = timeStepSize_;
+
+        // compute how long the last time step took
+        const auto cpuTime = wallClockTime();
+        timeStepWallClockTime_ = cpuTime - timeAfterLastTimeStep_;
+        timeAfterLastTimeStep_ = cpuTime;
     }
 
     /*!
@@ -163,13 +200,17 @@ public:
      * \param t The time \f$\mathrm{[s]}\f$ at which the simulation is finished
      */
     void setEndTime(Scalar t)
-    { endTime_ = t; }
+    {
+        endTime_ = t;
+        if (verbose_)
+            std::cout << "Set new end time to t = " << t << " seconds." << std::endl;
+    }
 
     /*!
-     * \brief Returns the current wall time (cpu time).
+     * \brief Returns the current wall clock time (cpu time) spend in this time loop
      */
-    double wallTime() const
-    {  return cpuTime_; }
+    double wallClockTime() const
+    { return timer_.elapsed(); }
 
     /*!
      * \brief Set the current time step size to a given value.
@@ -178,7 +219,7 @@ public:
      * episode, the timeStep() method will take care that the step
      * size won't exceed the episode or the end of the simulation,
      * though.
-     * \note Always call this after TimeLoop::advanceTimeStep()
+     * \note Always call this after TimeLoop::advanceTimeStep() and TimeLoop::reportTimeStep()
      *
      * \param dt The new value for the time step size \f$\mathrm{[s]}\f$
      */
@@ -193,9 +234,15 @@ public:
      * \brief Set the maximum time step size to a given value.
      *
      * \param maxDt The new value for the maximum time step size \f$\mathrm{[s]}\f$
+     * \note This also updates the time step size
      */
     void setMaxTimeStepSize(Scalar maxDt)
-    { maxTimeStepSize_ = maxDt; }
+    {
+        using std::min;
+        userSetMaxTimeStepSize_ = maxDt;
+        computeMaxTimeStepSize_();
+        timeStepSize_ = min(timeStepSize_, maxTimeStepSize_);
+    }
 
     /*!
      * \brief Returns the suggested time step length \f$\mathrm{[s]}\f$ so that we
@@ -205,12 +252,6 @@ public:
     Scalar timeStepSize() const override
     { return timeStepSize_; }
 
-    /*!
-     * \brief Returns the size of the previous time step \f$\mathrm{[s]}\f$.
-     */
-    Scalar previousTimeStepSize() const
-    { return previousTimeStepSize_; }
-
     /*!
      * \brief Returns number of time steps which have been
      *        executed since the beginning of the simulation.
@@ -235,14 +276,20 @@ public:
      * if the end time is reached.
      */
     bool finished() const
-    { return finished_ || time_ >= endTime_; }
+    {
+        using std::abs;
+        return finished_ || abs(time_-endTime_)/endTime_ < 1e-14;
+    }
 
     /*!
      * \brief Returns true if the simulation is finished after the
      *        time level is incremented by the current time step size.
      */
     bool willBeFinished() const
-    { return finished_ || time_ + timeStepSize_ >= endTime_; }
+    {
+        using std::abs;
+        return finished_ || abs(time_+timeStepSize_-endTime_)/endTime_ < 1e-14;
+    }
 
     /*!
      * \brief The current maximum time step size
@@ -250,27 +297,28 @@ public:
      *       and other possible check points
      */
     Scalar maxTimeStepSize() const
-    { return maxTimeStepSize_; }
+    {
+        using std::min;
+        return min(userSetMaxTimeStepSize_, maxTimeStepSize_);
+    }
 
     /*!
      * \brief State info on cpu time.
+     * \note Always call this after TimeLoop::advanceTimeStep()
      */
-    void reportTimeStep()
+    void reportTimeStep() const
     {
-        auto timeStepCpuTime = timer_.elapsed();
-        cpuTime_ += timeStepCpuTime;
+        const auto cpuTime = wallClockTime();
 
         if (verbose_)
         {
             std::cout << "Time step " << timeStepIdx_ << " done in "
-                      << timeStepCpuTime << " seconds. "
-                      << "Wall time: " << cpuTime_
+                      << timeStepWallClockTime_ << " seconds. "
+                      << "Wall clock time: " << cpuTime
                       << ", time: " << time_
-                      << ", time step size: " << timeStepSize_
+                      << ", time step size: " << lastTimeStepSize_
                       << std::endl;
         }
-
-        timer_.reset();
     }
 
     /*!
@@ -279,21 +327,20 @@ public:
     template< class Communicator = Dune::CollectiveCommunication<typename Dune::MPIHelper::MPICommunicator> >
     void finalize(const Communicator& comm = Dune::MPIHelper::getCollectiveCommunication())
     {
-        timer_.stop();
-        cpuTime_ += timer_.elapsed();
+        auto cpuTime = timer_.stop();
 
         if (verbose_)
         {
-            std::cout << "Simulation took " << cpuTime_ << " seconds on "
+            std::cout << "Simulation took " << cpuTime << " seconds on "
                       << comm.size() << " processes.\n";
         }
 
         if (comm.size() > 1)
-            cpuTime_ = comm.sum(cpuTime_);
+            cpuTime = comm.sum(cpuTime);
 
         if (verbose_)
         {
-            std::cout << "The cumulative CPU time was " << cpuTime_ << " seconds.\n";
+            std::cout << "The cumulative CPU time was " << cpuTime << " seconds.\n";
         }
     }
 
@@ -307,7 +354,6 @@ public:
 
 private:
     //! Computes the maximum timestep size respecting end time
-    //! and possibly episodes (TODO)
     void computeMaxTimeStepSize_()
     {
         if (finished())
@@ -318,19 +364,17 @@ private:
 
         using std::max;
         using std::min;
-
-        // TODO check for episodes if there is an episode manager
-        maxTimeStepSize_ = min(maxTimeStepSize_, max<Scalar>(0.0, endTime_ - time_));
+        maxTimeStepSize_ = min(userSetMaxTimeStepSize_, max<Scalar>(0.0, endTime_ - time_));
     }
 
     Dune::Timer timer_;
     Scalar time_;
     Scalar endTime_;
-    double cpuTime_;
 
     Scalar timeStepSize_;
-    Scalar previousTimeStepSize_;
-    Scalar maxTimeStepSize_;
+    Scalar lastTimeStepSize_;
+    Scalar maxTimeStepSize_, userSetMaxTimeStepSize_;
+    Scalar timeAfterLastTimeStep_, timeStepWallClockTime_;
     int timeStepIdx_;
     bool finished_;
     bool verbose_;
@@ -378,6 +422,9 @@ public:
         {
             isCheckPoint_ = false;
         }
+
+        // make sure to respect future check check points
+        this->setTimeStepSize(this->timeStepSize());
     }
 
     /*!
@@ -400,21 +447,53 @@ public:
     /*!
      * \brief Set a periodic check point
      * \note You can query if we are at a time check point with isCheckPoint()
-     * \param interval Set a periodic checkout every [interal] seconds
+     * \param interval Set a periodic checkout every [interval] seconds
+     * \param offset time from which the periodic check points are supposed to start (simulation time)
+     *        the first checkpoint will be at time = offset.
+     * \note If offset is in the past the first check point will be at the next
+     *       periodic check point greater or equal than time
+     * \note This also updates the time step size and potentially reduces the time step size to meet the next check point
      */
-    void setPeriodicCheckPoint(Scalar interval)
+    void setPeriodicCheckPoint(Scalar interval, Scalar offset = 0.0)
     {
+        using std::signbit;
+        if (signbit(interval))
+            DUNE_THROW(Dune::InvalidStateException, "Interval has to be positive!");
+
         periodicCheckPoints_ = true;
         deltaPeriodicCheckPoint_ = interval;
+        lastPeriodicCheckPoint_ = offset;
+        while (lastPeriodicCheckPoint_ + interval - this->time() < 1e-14*interval)
+            lastPeriodicCheckPoint_ += interval;
+
         if (this->verbose())
-            std::cout << "Enabled periodic check points every " << interval << " seconds." << std::endl;
+            std::cout << "Enabled periodic check points every " << interval
+                      << " seconds with the next check point at " << lastPeriodicCheckPoint_ + interval << " seconds." << std::endl;
 
         // make sure we respect this check point on the next time step
         setTimeStepSize(this->timeStepSize());
+
+        // check if the current time point is a check point
+        if (Dune::FloatCmp::eq(this->time(), lastPeriodicCheckPoint_, 1e-8*interval))
+            isCheckPoint_ = true;
     }
 
-    //! Whether now is a time checkpoint
-    //! has to be called after TimeLoop::advanceTimeStep()
+    //! disable periodic check points
+    void disablePeriodicCheckPoints()
+    { periodicCheckPoints_ = false; }
+
+    //! remove all check points
+    void removeAllCheckPoints()
+    {
+        periodicCheckPoints_ = false;
+        while (!checkPoints_.empty())
+            checkPoints_.pop();
+    }
+
+    /*!
+     * \brief Whether now is a time checkpoint
+     * \note has to be called after TimeLoop::advanceTimeStep()
+     */
     bool isCheckPoint() const
     { return isCheckPoint_; }
 
@@ -422,6 +501,7 @@ public:
      * \brief add a checkpoint to the queue
      * \note checkpoints have to be provided in ascending order
      * \param t the check point (in seconds)
+     * \note This also updates the time step size and potentially reduces the time step size to meet the next check point
      */
     void setCheckPoint(Scalar t)
     {
@@ -436,6 +516,7 @@ public:
      * \brief add checkpoints to the queue from a vector of time points
      * \note checkpoints have to be provided in ascending order
      * \param checkPoints the vector of check points
+     * \note This also updates the time step size and potentially reduces the time step size to meet the next check point
      */
     void setCheckPoint(const std::vector<Scalar>& checkPoints)
     { setCheckPoint(checkPoints.begin(), checkPoints.end()); }
@@ -445,6 +526,7 @@ public:
      * \note checkpoints have to be provided in ascending order
      * \param first iterator to the first element to be inserted
      * \param last iterator to the one-after-last element to be inserted
+     * \note This also updates the time step size and potentially reduces the time step size to meet the next check point
      */
     template<class ForwardIterator>
     void setCheckPoint(ForwardIterator first, ForwardIterator last)
@@ -461,19 +543,28 @@ private:
     //! Adds a check point to the queue
     void setCheckPoint_(Scalar t)
     {
+        if (t < this->time())
+        {
+            if (this->verbose())
+                std::cerr << "Couldn't insert checkpoint at t = " << t
+                          << " because that's in the past! (current simulation time is " << this->time() << ")" << std::endl;
+            return;
+        }
+
         if (!checkPoints_.empty())
         {
             if (t < checkPoints_.back())
             {
                 if (this->verbose())
-                    std::cerr << "--- Couldn't insert checkpoint as it is earlier than the last check point in the queue.\n"
-                              << "--- Checkpoints can only be inserted in ascending order." << std::endl;
+                    std::cerr << "Couldn't insert checkpoint as it is earlier than the last check point in the queue.\n"
+                              << "Checkpoints can only be inserted in ascending order." << std::endl;
+                return;
             }
-            else
-                checkPoints_.push(t);
         }
-        else
-            checkPoints_.push(t);
+
+        checkPoints_.push(t);
+        if (this->verbose())
+            std::cout << "Set check point at t = " << t << " seconds." << std::endl;
     }
 
      /*!
diff --git a/test/common/CMakeLists.txt b/test/common/CMakeLists.txt
index 6d8f4f2ea9f7328a92665453b2fe85f4155fcdf9..ad388d297dbd7d4e98ce607e51ea5af6ae389abd 100644
--- a/test/common/CMakeLists.txt
+++ b/test/common/CMakeLists.txt
@@ -4,4 +4,5 @@ add_subdirectory(math)
 add_subdirectory(parameters)
 add_subdirectory(propertysystem)
 add_subdirectory(spline)
+add_subdirectory(timeloop)
 add_subdirectory(typetraits)
diff --git a/test/common/timeloop/CMakeLists.txt b/test/common/timeloop/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9bfd94b5b603acf63d7516f1939b87a09fd5c6cb
--- /dev/null
+++ b/test/common/timeloop/CMakeLists.txt
@@ -0,0 +1 @@
+dune_add_test(SOURCES test_timeloop.cc)
diff --git a/test/common/timeloop/test_timeloop.cc b/test/common/timeloop/test_timeloop.cc
new file mode 100644
index 0000000000000000000000000000000000000000..939fb14a89e0e8a6e81ece0a12febb96c9771c24
--- /dev/null
+++ b/test/common/timeloop/test_timeloop.cc
@@ -0,0 +1,153 @@
+// time loop test
+#include <config.h>
+
+#include <iostream>
+#include <cmath>
+#include <chrono>
+#include <thread>
+#include <limits>
+
+#include <dune/common/parallel/mpihelper.hh>
+#include <dune/common/exceptions.hh>
+
+#include <dumux/common/timeloop.hh>
+
+int main(int argc, char* argv[]) try
+{
+    const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv);
+
+    //! Standard time loop
+    double tStart = 0; double tEnd = 1; double dt = 0.1;
+
+    //! Standard time loop
+    {
+        if (mpiHelper.rank() == 0) std::cout << "------- Test time loop ----------" << std::endl;
+        Dumux::TimeLoop<double> timeLoop(tStart, dt, tEnd);
+
+        if (std::abs(timeLoop.time()-tStart) > 1e-15)
+            DUNE_THROW(Dune::InvalidStateException, "Wrong time at start!");
+        if (std::abs(timeLoop.endTime()-tEnd) > 1e-15)
+            DUNE_THROW(Dune::InvalidStateException, "Wrong end time: " << timeLoop.endTime() << " should be " << tEnd);
+        if (std::abs(timeLoop.timeStepSize()-dt) > 1e-15)
+            DUNE_THROW(Dune::InvalidStateException, "Wrong time step size!");
+
+        timeLoop.setMaxTimeStepSize(0.03333333333333333);
+        if (std::abs(timeLoop.timeStepSize()-0.03333333333333333) > 1e-15)
+            DUNE_THROW(Dune::InvalidStateException, "Wrong time step size!");
+
+        timeLoop.start(); // starts the timer
+        timeLoop.stop(); // stops the timer
+        timeLoop.resetTimer(); // resets the timer
+        timeLoop.start(); // starts the timer again
+
+        std::this_thread::sleep_for(std::chrono::milliseconds(5));
+        timeLoop.advanceTimeStep();
+        if (timeLoop.timeStepIndex() != 1)
+            DUNE_THROW(Dune::InvalidStateException, "Wrong time step index!");
+
+        timeLoop.reportTimeStep();
+
+        while (!timeLoop.finished())
+        {
+            std::this_thread::sleep_for(std::chrono::milliseconds(5));
+            timeLoop.advanceTimeStep();
+            timeLoop.reportTimeStep();
+            timeLoop.setTimeStepSize(timeLoop.timeStepSize());
+        }
+
+        timeLoop.finalize();
+
+        if (std::abs(timeLoop.time()-tEnd) > 1e-15)
+            DUNE_THROW(Dune::InvalidStateException, "Ended with wrong end time!");
+    }
+
+
+    // check point timeLoop
+    {
+        if (mpiHelper.rank() == 0) std::cout << std::endl << "------- Test check point time loop ----------" << std::endl;
+        Dumux::CheckPointTimeLoop<double> timeLoop(tStart, dt, tEnd);
+        timeLoop.setPeriodicCheckPoint(0.03, -0.03);
+        if (!timeLoop.isCheckPoint())
+            DUNE_THROW(Dune::InvalidStateException, "This is supposed to be a check point! t = " << timeLoop.time());
+
+        timeLoop.start();
+        std::this_thread::sleep_for(std::chrono::milliseconds(5));
+        timeLoop.advanceTimeStep();
+        timeLoop.reportTimeStep();
+        timeLoop.setPeriodicCheckPoint(0.03);
+
+        if (std::signbit(timeLoop.timeStepSize()))
+            DUNE_THROW(Dune::InvalidStateException, "Time step size is negative! dt = " << timeLoop.timeStepSize());
+
+        if (!timeLoop.isCheckPoint())
+            DUNE_THROW(Dune::InvalidStateException, "This is supposed to be a check point! t = " << timeLoop.time());
+
+        timeLoop.setCheckPoint(0.02); // can't be set because it's in the past
+        timeLoop.setCheckPoint(0.08);
+        std::this_thread::sleep_for(std::chrono::milliseconds(5));
+        timeLoop.advanceTimeStep();
+        timeLoop.reportTimeStep();
+
+        if (std::signbit(timeLoop.timeStepSize()))
+            DUNE_THROW(Dune::InvalidStateException, "Time step size is negative! dt = " << timeLoop.timeStepSize());
+
+        if (!timeLoop.isCheckPoint())
+            DUNE_THROW(Dune::InvalidStateException, "This is supposed to be a check point! t = " << timeLoop.time());
+
+        timeLoop.setEndTime(1e9);
+        timeLoop.setTimeStepSize(1e7);
+
+        std::this_thread::sleep_for(std::chrono::milliseconds(5));
+        timeLoop.advanceTimeStep();
+        timeLoop.reportTimeStep();
+
+        if (std::signbit(timeLoop.timeStepSize()))
+            DUNE_THROW(Dune::InvalidStateException, "Time step size is negative! dt = " << timeLoop.timeStepSize());
+
+        if (!timeLoop.isCheckPoint())
+            DUNE_THROW(Dune::InvalidStateException, "This is supposed to be a check point! t = " << timeLoop.time());
+
+        timeLoop.removeAllCheckPoints();
+        timeLoop.setTimeStepSize(0.5e-6);
+        timeLoop.setPeriodicCheckPoint(1.0e6, 1.0e6);
+
+        std::this_thread::sleep_for(std::chrono::milliseconds(5));
+        timeLoop.advanceTimeStep();
+        timeLoop.reportTimeStep();
+
+        if (std::signbit(timeLoop.timeStepSize()))
+            DUNE_THROW(Dune::InvalidStateException, "Time step size is negative! dt = " << timeLoop.timeStepSize());
+
+        while (!timeLoop.finished())
+        {
+            std::this_thread::sleep_for(std::chrono::milliseconds(5));
+            timeLoop.advanceTimeStep();
+            timeLoop.reportTimeStep();
+            timeLoop.setTimeStepSize(std::numeric_limits<double>::max());
+
+            if (std::abs(timeLoop.time() - 2.0e6) < 1e-15 && !timeLoop.isCheckPoint())
+                DUNE_THROW(Dune::InvalidStateException, "This is supposed to be a check point! t = " << timeLoop.time());
+
+            if (timeLoop.timeStepIndex() == 10)
+                timeLoop.setPeriodicCheckPoint(1.0e8);
+
+            if (std::signbit(timeLoop.timeStepSize()))
+                DUNE_THROW(Dune::InvalidStateException, "Time step size is negative! dt = " << timeLoop.timeStepSize());
+        }
+
+        timeLoop.finalize();
+
+        if (std::abs(timeLoop.time()-1e9) > 1e-15)
+            DUNE_THROW(Dune::InvalidStateException, "Ended with wrong end time!");
+
+    }
+
+    return 0;
+}
+// //////////////////////////////////
+//   Error handler
+// /////////////////////////////////
+catch (const Dune::Exception& e) {
+    std::cout << e << std::endl;
+    return 1;
+}