# Patterns for Temporal Queries

This guide explores patterns for performing time-based queries in event-sourced systems. Temporal queries are essential for understanding how the system has evolved, analyzing trends, answering retrospective questions, and reconstructing domain state at specific points in time.

Unlike traditional databases that store only the current state, event-sourced systems retain the entire history of what happened. This makes them ideal for time-based analysis, but also introduces unique challenges. Querying across time means working with sequences of events, and often requires reinterpreting domain logic in historical context.

## Understanding Temporal Questions

Temporal queries take many forms. Some questions ask for a snapshot at a specific moment:

- **What did the data look like on January 1st?**
- **Which users were active at 9:00 AM today?**

Others require analyzing changes over time:

- **How many books were acquired each week?**
- **When did the price of a product last change?**

Still others involve periods and durations:

- **How long was a machine in error state?**
- **Which subscriptions were active between two dates?**

The key distinction in event-sourced systems is that answers are not stored – they are **derived**. To answer time-based questions, you must **replay events** and **interpret them relative to the time window or historical condition** you care about.

## Projecting State at a Specific Point in Time

A common temporal query is reconstructing the state of a domain entity as it existed at a specific moment. This typically involves **reading all events for a given subject in chronological order, stopping at the desired timestamp or event ID**.

Clients can set an **upper bound** when reading from a stream to limit the result to only the events that occurred before or at a certain point. The application logic then **replays those events to compute the historical state**.

> **Try it with EventSourcingDB**
>
> In EventSourcingDB, the **`upperBound`** option limits a read to the events up to a given event ID – see **[Fine-Tuning Reads](/learn/tutorials/first-steps-with-eventsourcingdb/reading-events#fine-tuning-reads)**.

This pattern is especially useful for **audits, debugging, or retrospective analysis**. For example, to determine what a customer's account looked like before a dispute was filed, you can replay the stream up to the relevant event.

## Building Read Models Over Time Windows

Temporal reporting often involves **grouping events by time intervals**. For instance, counting how many events of a certain type occurred per day, week, or month.

This can be done by **grouping events** by date parts such as the **year**, **month**, **day**, or **weekday**. The result is a **projection of time-bucketed aggregates** that reveal trends over time.

> **Try it with EventSourcingDB**
>
> EventQL, the query language of EventSourcingDB, supports this with its **`GROUP BY`** clause and date functions such as **`YEAR`**, **`MONTH`**, **`DAY`**, and **`WEEKDAY`** – see **[EventQL](/docs/eventsourcingdb/eventql)**.

To improve performance and reusability, such results are often **persisted as read models**. Instead of recalculating everything from scratch, the read model can be **incrementally updated with new events**, while **historical results remain unchanged**.

## Detecting Transitions and Durations

Some queries are concerned with **transitions** – for example, when a device changed from one state to another – or with **durations**, such as how long a user session lasted.

These cases require **detecting specific event patterns** and **computing the time difference between them**. This logic typically lives outside the **event store**, in the **application layer**. The event store provides the raw events; **interpretation is up to the consumer**.

To detect such transitions, read the relevant stream and identify the **pairs of events** that represent **start and end conditions**. For duration, **subtract the timestamps** of the corresponding events. This allows tracking metrics such as **uptime**, **usage duration**, or **time spent in a workflow stage**.

## Using Event Types and Subjects for Efficient Filtering

Efficient temporal queries rely on **proper modeling**. Events should have **meaningful types that reflect domain changes**, and subjects should be structured in a way that **supports scoping and filtering**.

If all state changes of interest are represented explicitly as events – such as **`subscription-activated`** or **`machine-entered-error-state`** – then querying becomes straightforward. If instead only low-level signals are recorded, the **interpretation becomes more complex and less reliable**.

Use subjects to **isolate event streams**, so that **replaying or querying one domain concept does not require scanning unrelated events**. This reduces both **processing time and cognitive complexity**.

## Replaying with Time Constraints

In addition to filtering by event ID, clients can **read events in chronological order** and combine this with their own logic to **stop processing once a given timestamp is exceeded**. This avoids unnecessary work and enables **time-bounded projections**.

In some scenarios, it may be useful to perform **multiple partial replays**, each covering a defined time window. This helps **parallelize processing** and makes the results more **explainable** – for example, by computing **monthly aggregates independently**.

## Practical Considerations

Time-based queries can be **computationally expensive** if they require processing large numbers of events. To manage this, consider **building and maintaining incremental read models** for frequently used queries. Use **checkpoints** to **avoid replaying the same events multiple times**. Consider also **storing derived data** if it is needed often or by multiple consumers.

Another consideration is **time zone handling**. Event timestamps are stored in **UTC**, and all temporal comparisons should be performed in **UTC unless explicitly converted**. Make sure your application logic respects this, especially when dealing with **daily or weekly aggregations**.

Finally, remember that **events describe what happened – but not necessarily what was known at the time**. If you need to analyze what was visible to a user or system at a given moment, make sure to apply the same **rules, filters, and access logic** that would have applied historically.

## Designing for Temporal Queries

The power of event sourcing lies in its ability to **answer questions that were never anticipated**. Temporal queries are a prime example: they allow teams to **explore the past, detect patterns, and derive insights without relying on precomputed state**.

To make the most of this capability, **design your events with time-based use in mind**. Choose **event types that clearly express domain transitions**. Use **subject hierarchies to scope queries**. **Record timestamps consistently**. And **build application logic that can safely replay and interpret history**.

A well-modeled event stream is not just a log – it is a **time machine**. Use it to **understand not only what changed, but when, how, and why**.
