diff --git a/source/RobotAPI/interface/armem/query.ice b/source/RobotAPI/interface/armem/query.ice
index 3833b4444736bc1f78fdf9c34d1c5d6ea4acfc8c..bfe0764272dbee12270de46cfe7cf0b90f851bb0 100644
--- a/source/RobotAPI/interface/armem/query.ice
+++ b/source/RobotAPI/interface/armem/query.ice
@@ -49,6 +49,57 @@ module armarx
                         long first = 0;  ///< First index to get.
                         long last = -1;  ///< Last index to get (inclusive).
                     };
+
+
+                    /**
+                     * @brief Get snapshot(s) around timestamp.
+                     *
+                     * If there is a snapshot which exactly matches the query timestamp,
+                     * the behavior is as for the @see Single query. 
+                     * 
+                     * However, if there is no matching timestamp, the snapshots before 
+                     * and after the query timestamp will be returned.
+                     * 
+                     * If #eps is positive, the snapshots must be within the time
+                     * range [timestamp - eps, timestamp + eps]. 
+                     * Consequently, the query result can be empty.
+                     *
+                     * Hint: 
+                     *   This query can be quite useful when interpolating snapshots
+                     *   is possible.
+                     */
+                    class TimeApprox extends EntityQuery
+                    {
+                        long timestamp = -1;
+                        long eps = -1;
+                    };
+
+                    /**
+                     * @brief Get the last snapshot before or at the timestamp.
+                     * 
+                     * This query is kind of similar to latest() but with a reference timestamp.
+                     * 
+                     */
+                    class BeforeOrAtTime extends EntityQuery
+                    {
+                        long timestamp;
+                    }
+
+                    /**
+                     * @brief Get the last snapshot before the timestamp or a sequence of 
+                     *           n last elements before the timestamp depending on #maxEntries
+                     * 
+                     * Depending on #maxEntries, the behavior is as follows:
+                     *  - #maxEntries == 1 => last element before timestamp
+                     *  - #maxEntries > 1  => n last elements before timestamp
+                     *  - #maxEntries < 0  => all elements before timestamp
+                     */
+                    class BeforeTime extends EntityQuery
+                    {
+                        long timestamp;
+                        long maxEntries = 1;
+                    }
+
                 }
 
 
diff --git a/source/RobotAPI/libraries/armem/client/query/query_fns.h b/source/RobotAPI/libraries/armem/client/query/query_fns.h
index 8dcb88991450b95583c7228b2349fdfdf05cba95..40927ace4f6af3602b426689e82439c09507b666 100644
--- a/source/RobotAPI/libraries/armem/client/query/query_fns.h
+++ b/source/RobotAPI/libraries/armem/client/query/query_fns.h
@@ -134,4 +134,14 @@ namespace armarx::armem::client::query_fns
         };
     }
 
+    inline
+    std::function<void(query::SnapshotSelector&)>
+    atTimeApprox(Time time, Duration eps)
+    {
+        return [ = ](query::SnapshotSelector & selector)
+        {
+            selector.atTimeApprox(time, eps);
+        };
+    }
+
 }
diff --git a/source/RobotAPI/libraries/armem/client/query/selectors.cpp b/source/RobotAPI/libraries/armem/client/query/selectors.cpp
index f2bae712818b566ef51444aafb84af6031d338d0..8b2aa11dea44ad42cad732b76c65c87eb1cc7ad2 100644
--- a/source/RobotAPI/libraries/armem/client/query/selectors.cpp
+++ b/source/RobotAPI/libraries/armem/client/query/selectors.cpp
@@ -42,6 +42,15 @@ namespace armarx::armem::client::query
         return *this;
     }
 
+    SnapshotSelector& SnapshotSelector::atTimeApprox(Time timestamp, Duration eps)
+    {
+        auto& q = _addQuery<dq::entity::TimeApprox>();
+        toIce(q.timestamp, timestamp);
+        toIce(q.eps, eps);
+        return *this;
+    }
+
+
     SnapshotSelector& SnapshotSelector::indexRange(long first, long last)
     {
         auto& q = _addQuery<dq::entity::IndexRange>();
@@ -51,6 +60,13 @@ namespace armarx::armem::client::query
     }
 
 
+    SnapshotSelector& SnapshotSelector::beforeTimestamp(Time timestamp)
+    {
+        auto& q = _addQuery<dq::entity::BeforeTime>();
+        toIce(q.timestamp, timestamp);
+        return *this;
+    }
+
 
     SnapshotSelector& EntitySelector::snapshots()
     {
diff --git a/source/RobotAPI/libraries/armem/client/query/selectors.h b/source/RobotAPI/libraries/armem/client/query/selectors.h
index 8bc2850b390b394a5d2b5fc07753bb846f598416..69fda84b54cbfc3c0c0395e23d33ebfebd7d9d1b 100644
--- a/source/RobotAPI/libraries/armem/client/query/selectors.h
+++ b/source/RobotAPI/libraries/armem/client/query/selectors.h
@@ -26,6 +26,9 @@ namespace armarx::armem::client::query
 
         SnapshotSelector& latest();
         SnapshotSelector& atTime(Time timestamp);
+        SnapshotSelector& atTimeApprox(Time timestamp, Duration eps);
+        SnapshotSelector& beforeTimestamp(Time timestamp);
+
 
         SnapshotSelector& timeRange(Time min, Time max);
         SnapshotSelector& indexRange(long first, long last);
diff --git a/source/RobotAPI/libraries/armem/core/Time.h b/source/RobotAPI/libraries/armem/core/Time.h
index 328d1f59d793cbcc9a7d0febc0951775d200013a..f76f7657dda6e14a9c35bb4ad5edf58471680144 100644
--- a/source/RobotAPI/libraries/armem/core/Time.h
+++ b/source/RobotAPI/libraries/armem/core/Time.h
@@ -7,8 +7,8 @@
 
 namespace armarx::armem
 {
-
     using Time = IceUtil::Time;
+    using Duration = IceUtil::Time;
 
     /**
      * @brief Returns `time` as e.g. "123456789.012 ms".
diff --git a/source/RobotAPI/libraries/armem/server/query_proc/base/EntityQueryProcessorBase.h b/source/RobotAPI/libraries/armem/server/query_proc/base/EntityQueryProcessorBase.h
index 447bd259a2c24a3ca80c91c59a518daed56c18f1..9b85847791e21aee0031e40b551b2fceff1e9547 100644
--- a/source/RobotAPI/libraries/armem/server/query_proc/base/EntityQueryProcessorBase.h
+++ b/source/RobotAPI/libraries/armem/server/query_proc/base/EntityQueryProcessorBase.h
@@ -1,15 +1,19 @@
 #pragma once
 
-#include <RobotAPI/interface/armem/query.h>
+#include <cstdint>
+#include <iterator>
 
-#include "BaseQueryProcessorBase.h"
+#include <RobotAPI/interface/armem/query.h>
 
+#include <ArmarXCore/core/exceptions/LocalException.h>
 #include <ArmarXCore/core/logging/Logging.h>
 #include <ArmarXCore/core/exceptions/local/ExpressionException.h>
 
 #include <RobotAPI/libraries/armem/core/error.h>
 #include <RobotAPI/libraries/armem/core/ice_conversions.h>
 
+#include "BaseQueryProcessorBase.h"
+
 
 namespace armarx::armem::base::query_proc
 {
@@ -48,6 +52,18 @@ namespace armarx::armem::base::query_proc
             {
                 process(result, *q, entity);
             }
+            else if (auto q = dynamic_cast<const armem::query::data::entity::TimeApprox*>(&query))
+            {
+                process(result, *q, entity);
+            }
+            else if (auto q = dynamic_cast<const armem::query::data::entity::BeforeOrAtTime*>(&query))
+            {
+                process(result, *q, entity);
+            }
+            else if (auto q = dynamic_cast<const armem::query::data::entity::BeforeTime*>(&query))
+            {
+                process(result, *q, entity);
+            }
             else
             {
                 throw armem::error::UnknownQueryType("entity snapshot", query);
@@ -152,6 +168,115 @@ namespace armarx::armem::base::query_proc
             }
         }
 
+        void process(_EntityT& result,
+                     const armem::query::data::entity::BeforeOrAtTime& query,
+                     const _EntityT& entity) const
+        {
+            const auto referenceTimestamp = fromIce<Time>(query.timestamp);
+            ARMARX_CHECK(referenceTimestamp.toMicroSeconds() >= 0) << "Reference timestamp is negative!";
+
+            const auto it = entity.history().lower_bound(referenceTimestamp);
+
+            if (it != entity.history().end())
+            {
+                addResultSnapshot(result, it);
+            }
+        }
+
+        void process(_EntityT& result,
+                     const armem::query::data::entity::BeforeTime& query,
+                     const _EntityT& entity) const
+        {
+            const auto referenceTimestamp = fromIce<Time>(query.timestamp);
+            ARMARX_CHECK(referenceTimestamp.toMicroSeconds() >= 0) << "Reference timestamp is negative!";
+
+            const auto maxEntries = fromIce<std::int64_t>(query.maxEntries);
+
+            const auto refIt = entity.history().lower_bound(referenceTimestamp);
+
+            if (refIt == entity.history().end())
+            {
+                ARMARX_WARNING << "No valid entities found.";
+                return;
+            }
+
+            const auto nEntriesBefore = std::distance(entity.history().begin(), refIt);
+
+            const int nEntries = [&]()
+            {
+                // see query.ice
+                if (maxEntries > 0)
+                {
+                    return std::min(nEntriesBefore, maxEntries);
+                }
+
+                return nEntriesBefore; // all elements before timestamp
+            }
+            ();
+
+            auto it = refIt;
+            for (std::int64_t i = 0; i < nEntries; i++, --it)
+            {
+                addResultSnapshot(result, it);
+            }
+        }
+
+        void process(_EntityT& result,
+                     const armem::query::data::entity::TimeApprox& query,
+                     const _EntityT& entity) const
+        {
+            const auto referenceTimestamp = fromIce<Time>(query.timestamp);
+            ARMARX_CHECK(referenceTimestamp.toMicroSeconds() >= 0) << "Reference timestamp is negative!";
+
+            const auto referenceTimestampMicroSeconds = referenceTimestamp.toMicroSeconds();
+            const auto epsDuration = fromIce<Time>(query.eps).toMicroSeconds();
+
+            // elements have to be in range [t_ref - eps, t_ref + eps] if eps is positive
+            const auto isInRange = [&](const Time & t) -> bool
+            {
+                if (epsDuration <= 0)
+                {
+                    return true;
+                }
+
+                return std::abs(t.toMicroSeconds() - referenceTimestampMicroSeconds) <= epsDuration;
+            };
+
+
+            const auto beforeOrAt = entity.history().lower_bound(referenceTimestamp);
+            const auto after = entity.history().upper_bound(referenceTimestamp);
+
+            const bool isBeforeOrAtValid = beforeOrAt != entity.history().end();
+            const bool isAfterValid = isBeforeOrAtValid && after != entity.history().end();
+
+            // if 'before' is already invalid, there is nothing to be gained here.
+            if (not isBeforeOrAtValid)
+            {
+                return;
+            }
+
+            // only 'before' valid? or is 'before' perfect match?
+            if ((not isAfterValid) or (beforeOrAt->first == referenceTimestamp))
+            {
+                if (isInRange(beforeOrAt->first))
+                {
+                    addResultSnapshot(result, beforeOrAt);
+                }
+                return;
+            }
+            // -> now both are valid
+
+            // return both => user can interpolate
+            if (isInRange(beforeOrAt->first))
+            {
+                addResultSnapshot(result, beforeOrAt);
+            }
+            if (isInRange(after->first))
+            {
+                addResultSnapshot(result, after);
+            }
+        }
+
 
         static size_t negativeIndexSemantics(long index, size_t size)
         {