diff --git a/include/evalhyd/evald.hpp b/include/evalhyd/evald.hpp
index c3cc7697fd41e40ccb2a621ad9af23613f31d59a..44bd3fff6da8fc373db81f71f823ed141ff55e2f 100644
--- a/include/evalhyd/evald.hpp
+++ b/include/evalhyd/evald.hpp
@@ -89,6 +89,14 @@ namespace evalhyd
     ///       `Pushpalatha et al. (2012)
     ///       <https://doi.org/10.1016/j.jhydrol.2011.11.055>`_.
     ///
+    ///    t_msk: ``xt::xexpression<A>``, optional
+    ///       Mask(s) used to generate temporal subsets of the whole streamflow
+    ///       time series (where True/False is used for the time steps to
+    ///       include/discard in a given subset). Masks must feature the same
+    ///       number of dimensions as observations and predictions, and it must
+    ///       broadcastable with both of them.
+    ///       shape: ({... ,} time)
+    ///
     /// :Returns:
     ///
     ///    ``std::vector<xt::xarray<double>>``
@@ -127,29 +135,47 @@ namespace evalhyd
             const xt::xexpression<A>& q_obs,
             const xt::xexpression<A>& q_prd,
             const std::vector<std::string>& metrics,
-            const std::string& transform="none",
-            const double exponent=1,
-            double epsilon=-9
+            const std::string& transform = "none",
+            const double exponent = 1,
+            double epsilon = -9,
+            const xt::xexpression<A>& t_msk = A()
     )
     {
         auto q_obs_ = q_obs.derived_cast();
         auto q_prd_ = q_prd.derived_cast();
+        auto t_msk_ = t_msk.derived_cast();
+
+        // initialise a mask if none provided
+        // (corresponding to no temporal subset)
+        A msk;
+        if (t_msk_.size() < 1)
+            msk = xt::ones<bool>(q_obs_.shape());
+        else
+            msk = std::move(t_msk.derived_cast());
 
-        // check that observations and predictions dimensions are compatible
-        if (q_prd_.dimension() != q_obs_.dimension())
+        // check that observations, predictions, and masks dimensions are compatible
+        if (q_obs_.dimension() != q_prd_.dimension())
         {
             throw std::runtime_error(
                     "observations and predictions feature different numbers "
                     "of dimensions"
             );
         }
+        if (q_obs_.dimension() != msk.dimension())
+        {
+            throw std::runtime_error(
+                    "observations and masks feature different numbers "
+                    "of dimensions"
+            );
+        }
 
         auto obs_shp = q_obs_.shape();
         auto prd_shp = q_prd_.shape();
+        auto msk_shp = msk.shape();
 
         if (obs_shp != prd_shp)
         {
-            // check observations and predictions are broadcastable
+            // check observations, predictions, and masks are broadcastable
             for (int a = 0; a < obs_shp.size(); a++)
             {
                 if (obs_shp[a] != prd_shp[a])
@@ -158,6 +184,16 @@ namespace evalhyd
                                 "observations and predictions are not "
                                 "broadcastable"
                         );
+                if (obs_shp[a] != msk_shp[a])
+                    if ((obs_shp[a] != 1) and (msk_shp[a] != 1))
+                        throw std::runtime_error(
+                                "observations and masks are not broadcastable"
+                        );
+                if (prd_shp[a] != msk_shp[a])
+                    if ((prd_shp[a] != 1) and (msk_shp[a] != 1))
+                        throw std::runtime_error(
+                                "predictions and masks are not broadcastable"
+                        );
             }
         }
 
@@ -224,7 +260,7 @@ namespace evalhyd
         );
 
         // instantiate determinist evaluator
-        eh::determinist::Evaluator<A> evaluator(obs, prd);
+        eh::determinist::Evaluator<A> evaluator(obs, prd, msk);
 
         // declare maps for memoisation purposes
         std::unordered_map<std::string, std::vector<std::string>> elt;
diff --git a/src/determinist/evaluator.hpp b/src/determinist/evaluator.hpp
index c83591d8e1c4cda9692a71bb5d8df7708a75298b..2baaa812eb11ae9f9b02c39fb248f55da8bdf062 100644
--- a/src/determinist/evaluator.hpp
+++ b/src/determinist/evaluator.hpp
@@ -28,10 +28,22 @@ namespace evalhyd
         {
         // define type for function to be used to "mask" (i.e. yield NaNs) the
         // time steps for which the observations/predictions are missing data
-        typedef xt::xfunction<xt::detail::conditional_ternary,
-                              xt::xfunction<xt::math::isnan_fun, const A&>,
-                              xt::xscalar<float>,
-                              const A&> nan_function;
+        typedef xt::xfunction<
+            xt::detail::conditional_ternary,
+            xt::xfunction<
+                xt::detail::bitwise_or,
+                xt::xfunction<
+                    xt::math::isnan_fun,
+                    const A&
+                >,
+                xt::xfunction<
+                    xt::detail::logical_not,
+                    const A&
+                >
+            >,
+            xt::xscalar<float>,
+            const A&
+        > nan_function;
 
         private:
             // members for input data
@@ -52,17 +64,26 @@ namespace evalhyd
         public:
             // constructor method
             Evaluator(const xt::xexpression<A>& obs,
-                      const xt::xexpression<A>& prd) :
+                      const xt::xexpression<A>& prd,
+                      const xt::xexpression<A>& msk) :
                     _q_obs{obs.derived_cast()},
                     _q_prd{prd.derived_cast()},
                     // "mask" (i.e. use NaN) observations where predictions are
                     // missing (i.e. NaN)
-                    q_obs{xt::where(xt::isnan(prd.derived_cast()),
-                                    NAN, obs.derived_cast())},
+                    q_obs{
+                        xt::where(
+                            xt::isnan(prd.derived_cast()) | !msk.derived_cast(),
+                            NAN, obs.derived_cast()
+                        )
+                    },
                     // "mask" (i.e. use NaN) predictions where observations are
                     // missing (i.e. NaN)
-                    q_prd{xt::where(xt::isnan(obs.derived_cast()),
-                                    NAN, prd.derived_cast())}
+                    q_prd{
+                        xt::where(
+                            xt::isnan(obs.derived_cast()) | !msk.derived_cast(),
+                            NAN, prd.derived_cast()
+                        )
+                    }
             {};
 
             // members for evaluation metrics
diff --git a/tests/test_determinist.cpp b/tests/test_determinist.cpp
index 1980c4d648e38ad23b9dae7dc9f63e32dcc13f5f..f392f9ad5885619219288e8fddfec4dda9754dbf 100644
--- a/tests/test_determinist.cpp
+++ b/tests/test_determinist.cpp
@@ -170,6 +170,45 @@ TEST(DeterministTests, TestTransform)
 
 }
 
+TEST(DeterministTests, TestMasks)
+{
+    // read in data
+    std::ifstream ifs;
+    ifs.open("./data/q_obs.csv");
+    xt::xtensor<double, 2> observed = xt::load_csv<int>(ifs);
+    ifs.close();
+
+    ifs.open("./data/q_prd.csv");
+    xt::xtensor<double, 2> predicted = xt::load_csv<double>(ifs);
+    ifs.close();
+
+    // generate temporal subset by dropping 20 first time steps
+    xt::xtensor<double, 2> masks =
+            xt::ones<bool>({std::size_t {1}, std::size_t {observed.size()}});
+    xt::view(masks, 0, xt::range(0, 20)) = 0;
+
+    // compute scores using masks to subset whole record
+    std::vector<std::string> metrics =
+            {"RMSE", "NSE", "KGE", "KGEPRIME"};
+
+    std::vector<xt::xarray<double>> metrics_masked =
+            evalhyd::evald(observed, predicted, metrics, "none", 1, -9, masks);
+
+    // compute scores on pre-computed subset of whole record
+    xt::xtensor<double, 2> obs = xt::view(observed, xt::all(), xt::range(20, _));
+    xt::xtensor<double, 2> prd = xt::view(predicted, xt::all(), xt::range(20, _));
+
+    std::vector<xt::xarray<double>> metrics_subset =
+            evalhyd::evald(obs, prd, metrics);
+
+    // check results are identical
+    for (int m = 0; m < metrics.size(); m++)
+    {
+        EXPECT_TRUE(xt::allclose(metrics_masked[m], metrics_subset[m]))
+        << "Failure for (" << metrics[m] << ")";
+    }
+}
+
 TEST(DeterministTests, TestMissingData)
 {
     std::vector<std::string> metrics =