Consumer agents run for months and years with the same user. A companion, a coach, a journaling app: each one collects thousands of small facts over that time, and most of those facts are only true for a while. The race is next Sunday. The trial ends in January. The trip was last week. So six months later, the memory might still be there, but the time it was tied to would have passed.
That is the part most memory setups skip. They store what was said and when it was stored, and nothing about when it was true. A memory without a clock reads fine on the day it is written and turns wrong afterwards, and the agent has no way to tell.
This article walks through every place we have seen time break memory, and how Mem0 handles each of those cases. For consistency, we've used a sample usecase: a fitness coach app - Stride, and a user called Sam.
Every memory has two clocks
A memory has two times attached to it.
Event time: when the thing happened. Sam ran the race on Sunday.
Write time: when your app stored the memory. Maybe Sunday night, maybe three weeks later during a data import.
This process of analysing events or system states across two independent timelines - when the event actually happened in the real world, and when the system learned about it - is called Bitemporal reasoning.
Most memory setups only keep write time. That works until the two clocks disagree, and they disagree more often than you would think. Every case below is one of those places.
1. Loading a user’s history
Stride, our fitness coach app, has been live for two years. It adds memory today and wants to load every old chat and journal entry for every user.
The first trap: Sam wrote “I ran my first 10K last weekend” in June 2025. If you store it today, “last weekend” is read against today. The memory becomes “Sam ran a 10K this week.” That is wrong by over a year, and it stays wrong forever, because the original conversation date is gone after the import.
The rule: when you load old data, pass the original conversation time with each add. The memory system should read “last weekend” against that time, not against the server clock.
In Mem0, that is the timestamp parameter. It takes a Unix epoch for when the conversation happened.
We ran exactly this. The stored memory came back as:
User ran a 10K on the weekend of 2025-06-14 to 2025-06-15 and finished in 58 minutes.
The phrase was read against June 2025, and the memory now carries real dates instead of “last weekend.”
Two more things the timestamp does:
The created_at field on that memory reads 2025-06-16, not today.
Date filters use it. A filter for June 2025 finds this memory. A filter for 2026 does not.
2. “Yesterday” in a daily check-in
Imports are the big version of the problem. The small version happens every day.
Let's say Sam checks in today: “I started the new knee meds yesterday.” Six months later, “yesterday” means nothing. The coach needs the date.
Three things to get right:
Resolve at write time. The conversation date is only known now. Store the resolved date, not the phrase.
Keep the precision honest. “Yesterday” is a day. “Last month” is a month. “Back in college” is vague. Do not invent an exact day where the user gave you a month.
Know the user’s timezone. “This morning” for Sam in Mumbai is a different calendar day than “this morning” in SF. Consumer apps have users everywhere.
Mem0 does the first two on every add, using the timestamp you pass as the anchor. “Last weekend” resolves to a two-day range above. “Last night” with a timestamp of 2025-08-10 resolves to “at 11PM on 2025-08-09.” A vague phrase stays vague.
For the third, pass the exact instant Sam sent the message as the timestamp, not a rounded date. Mem0 reads the phrases against that instant in UTC, so a late-night message from a user far from UTC is the one to keep an eye on: “this morning” sent at 2AM in Mumbai is still August 9 in UTC when Sam means August 10.
3. Facts that evolve with time
Sam is vegetarian. In March, Sam says “I started eating fish again; my doctor wants more protein”. Sam lives in Lisbon. In spring, Sam moves to Berlin.
Both versions were true, at different times. A coach that mixes them suggests tofu recipes in a city Sam left.
Here's what most people get wrong: they delete the old fact. But you need two reads from the same memory:
What is true now. For the next reply. Berlin, fish.
What was true then. For “what was I eating during my last training block?” and for the support ticket in case 8 below.
Mem0 keeps both. When a new memory replaces an older one, the older one stays, marked as superseded. A default read still returns both, so the history is there when you need it. When you want only the current truth, ask for it:
Recall result from an actual production call with latest_only=true:
User moved to Berlin last month (February 2026) and now lives in Neukölln; no longer lives in Lisbon and runs now around Tempelhofer Feld.
The same flag works on get_all. If your app knows exactly when a fact stopped being true, you can also close the old memory yourself with an end date, which is the next case.
4. Facts with an end date
Some memories have an expiry.
“Training for the Berlin half on Nov 15” should stop driving advice after Nov 15.
“Doing Dry January” ends on Jan 31.
Two rules. First, hiding/expiration in this case does not mean deleting. The record may still matter for history or for a later “actually, keep that.” Second, the end date is usually known when you write the memory, so set it then and let the system do the rest.
In Mem0, that is expiration_date on a memory.
How it behaves in production:
The format is a plain YYYY-MM-DD, evaluated in UTC. The memory stays visible through that day and is hidden from search and get_all the day after.
A January date is set on Sam’s Dry January memory in October, so it gets dropped out of get_all and search for “is Sam drinking alcohol?”
show_expired=True on either call will bring it back.
Fetching by ID always returns the memory, expired or not.
5. The coach asks time-aware questions
Everything so far was about writing. Now about reading. Before each reply, it asks memory things like:
“What did Sam struggle with last week?”
“Everything Sam said about the knee between July and September.”
“What did Sam eat last night?”
Two tools exist for this, and they are not the same.
a. Filters. Hard windows. Use it when the question is deterministic.
Remember case 1: this window runs on the timestamp you passed at write time, not on the import date. That is what makes a filter on “June 2025” find a memory you imported this week.
b. Boost. Reorders by how well a memory’s date fits the question, and never drops anything. Use it for natural language (non-deterministic) questions.
On the Mem0 Platform, the boost is automatic: a time phrase in the question lifts memories whose dates fit it. We stored three runs for Sam: a tempo run “yesterday,” a long run “last week,” and a marathon “last year.” Then we asked: "Where did Sam run last week?"
The long run came first. The tempo run from yesterday came second. The marathon from last year came fourth. Same kind of memory three times, different dates, and the date decided the order.
One important thing to design for your agent: “suggest a workout for next week” is not a time question. The time word is part of the request. Do not let your retrieval layer turn it into a filter on memory.
6. Replaying time
“Now” should be an input, not a global. This bites a consumer app in three places:
A nightly check-in job runs at 02:00 UTC for every user. For Sam in Mumbai, that is morning. For a user in San Francisco, that is yesterday evening.
An eval asks “what would the coach have known on 2026-03-21?” and the answer should not change as the calendar moves.
A demo seeded with old data: “last week” means something different every time you open it.
In Mem0, reference_date makes a moment “now” for one search. We gave Sam two dinners: a salad on 2026-10-09 and a pasta dinner on 2025-08-09. Then we asked the same question twice.
Same memories, same question, one parameter. It accepts a Unix epoch, a YYYY-MM-DD date, or an ISO datetime. Pass the user’s local time for the nightly job, a fixed date for the eval, and the seed date for the demo.
7. Old facts crowding out new ones
After 18 months, Sam has a few hundred memories. A 2025 knee injury matches the word “knee” exactly as well as last week’s niggle. So the coach keeps bringing up an injury that healed a year ago.
Age alone is a weak signal. Whether a memory keeps getting used is a better one. And any freshness rule should bias, never filter. The rare old fact that matters, a surgery, an allergy, has to stay reachable.
In Mem0, that is Memory Decay. It is off by default, and you turn it on per project.
Decay on means memories that keep coming back in searches rank higher, and memories that haven't been retrieved in a long time rank lower. It only reorders. Nothing is hidden, so the allergy from two years ago is still there when the question is about allergies.
So the healed injury drifts down. If Sam mentions the knee again, it comes right back up.
8. When a user says “you forgot”
What if Sam says: "I told you about my injury and you still pushed me to run hills."
The question that follows is: what did the coach know on the day it gave that advice? Two reads to answer this.
The first is the created_at window from case 5, with no latest_only. It returns every memory written up to that day, including facts that have since been replaced, because replaced memories stay in a default read.
If the injury memory is in that list with a created_at before the hill session, the coach had it. If it is not, Sam told the coach later than they remember. Either way the answer comes from dates.
The second is the history of a single memory:
It returns the memory's own changes in order. When we ran it on a memory we had added and then updated twice, it came back with three entries: the add, carrying the timestamp we passed, and the two updates, each with the old text, the new text, and when it changed. So if your app ever edits a memory, say to set an end date in case 4, the before and after are kept.
Back to the race
"My race is next Sunday" now reads "half marathon on 2026-10-25." It carries a date. It stops driving advice the day after. It ranks first when the coach asks what happened last week, and lower a year later when something newer matches. None of that needed app logic beyond passing the time.
The two clocks are the whole idea. Your app knows when things happened. Tell the memory layer, and it will take care of the rest.
All the knobs in one place
Problem | What to Pass |
|---|---|
Old data, original dates |
|
Facts that changed |
|
Facts with an end date |
|
Hard time windows |
|
Time words in a query | automatic boost |
Fixed “now” |
|
Stale memories rank lower |
|
What was known by a date |
|
Changes to one memory |
|
Here is Stride's whole setup, in the order a request flows:
TypeScript: timestamp, expirationDate, showExpired, referenceDate, latestOnly.
If you are building a B2C app (ex: a companion, coach, or journaling app) and have hit a time problem that we did not cover, we want to hear about it.
Mem0 App: https://app.mem0.ai
Mem0 Docs: https://docs.mem0.ai
GET TLDR from:
Summarize
Website/Footer
Summarize
Website/Footer
Summarize
Website/Footer
Summarize
Website/Footer











