Skip to content

Date and Time Reasoning Bugs

5 min read · updated August 3, 2026

“Schedule it for the Friday after next” is one of the most dangerous strings you can hand a language model, because it will confidently return a date, that date will be well formatted, and there is roughly no chance anyone downstream will check it.

The model has no clock

Start with the thing that is easy to forget: a language model is a pure function of its context. It has no system clock, no timezone database access at inference time and no notion of when “now” is. If the current date is not in the context, the model does what it does with any missing variable — it infers a plausible one from the distribution, which means from the density of dates in its training data.

So a model asked for “next Tuesday” with no anchor is computing an offset from a guess, and the guess skews towards its training cutoff. Worse, models will often state the assumed date confidently, or not state it at all, which removes the one signal a reviewer could have used. This is a hallucination in the strict sense: a specific claim about the world, produced with no information behind it.

The four failures

1. Missing anchor

Everything above. The fix is one line in the system prompt and it is astonishing how often it is missing. Include the full instant, not just the date: Current time: 2026-08-03T14:05:00+02:00 (Europe/Amsterdam, Monday). Giving the weekday explicitly removes a computation, and giving the offset and the IANA zone removes two more.

2. Date arithmetic, which is just arithmetic

Counting days across month boundaries, adding 90 days, computing an age at a past date, finding the number of business days in a range. Every weakness on the numerical reasoning page applies, plus irregular bases: months of unequal length, leap years, and the leap-year rule’s century exceptions. Off-by-one errors here are systematic rather than random, which is what makes them survive casual review.

3. Timezones, offsets and DST

The richest source of silent bugs. An offset is not a timezone — +01:00 is a fact about one instant, Europe/Amsterdam is a rule that produces different offsets at different times of year. Models conflate them constantly. Add the DST transitions, where an hour occurs twice or not at all, and the near-midnight conversions where a timezone shift moves the date as well as the time, and you have a class of error that shows up as a meeting on the wrong day for exactly the users who are in a different zone from the developer.

4. Calendar conventions

ISO week numbers — which put some days of early January in week 52 of the previous year — fiscal quarters that do not align with calendar ones, business-day counting with a holiday calendar the model cannot know, and locale-dependent formats where 03/08/2026 is two different days depending on who wrote it. Ambiguous input formats are worth rejecting outright rather than guessing at.

What the temporal benchmarks show

Temporal reasoning has its own literature, which is a good sign that it is genuinely hard rather than merely annoying. TimeQA (Chen, Wang and Wang, 2021) built questions whose answers change with time, from Wikidata’s time-stamped facts. TempReason (Tan, Ng and Bing, 2023) separates time–time, time–event and event–event reasoning, which is a useful decomposition because models are much better at the first than the third. Fatemi et al.’s Test of Time (Google, 2024) took the synthetic-data route specifically to avoid measuring memorisation of real dates, and reported that model performance depends heavily on the structure of the temporal problem and the order in which facts are presented, rather than on difficulty in any human sense.

Across all of them the pattern holds: retrieving a date that appeared in training is comparatively easy, and computing a relation between dates is comparatively hard. Design accordingly.

Never let the model compute a date

The robust architecture treats the model as a parser of temporal language, which it is genuinely good at, and gives the arithmetic to a date library, which is genuinely good at that. The model emits a specification; your code evaluates it.

# What the model returns -- a structure, never a computed date:
# {"anchor": "now", "offset": 2, "unit": "week", "weekday": "friday",
#  "time": "09:00", "timezone": "Europe/Amsterdam"}

from datetime import datetime, timedelta
from zoneinfo import ZoneInfo

WEEKDAYS = ["monday","tuesday","wednesday","thursday","friday",
            "saturday","sunday"]

def resolve(spec, now: datetime):
    tz = ZoneInfo(spec["timezone"])
    base = now.astimezone(tz)
    if spec["unit"] == "week":
        base += timedelta(weeks=spec["offset"])
        if spec.get("weekday"):
            delta = (WEEKDAYS.index(spec["weekday"]) - base.weekday()) % 7
            base += timedelta(days=delta)
    elif spec["unit"] == "day":
        base += timedelta(days=spec["offset"])
    hh, mm = map(int, spec["time"].split(":"))
    resolved = base.replace(hour=hh, minute=mm, second=0, microsecond=0)

    # Validator: reject anything outside a plausible window. Catches the
    # training-cutoff guess and the off-by-a-year, which are the two errors
    # that reach production.
    if not (now - timedelta(days=1) <= resolved <= now + timedelta(days=730)):
        raise ValueError(f"implausible resolved date: {resolved.isoformat()}")
    return resolved

Four properties fall out of this and all four matter. The arithmetic is done by zoneinfo, which knows about DST and you do not have to. The specification is inspectable, so a wrong answer is debuggable — you can see whether the model misread “the Friday after next” or whether your resolver did. The validator catches the two errors that actually reach users. And ambiguity becomes representable: let the model return {"ambiguous": true, "candidates": [...]} and ask the user, rather than forcing a guess.

Testing with a frozen clock

Date bugs are the ones that pass in July and fail in November, so fixed-clock testing is not optional:

  • Freeze now in tests and include the nasty instants deliberately — 29 February, 31 December, the last day of a month, the hour a DST transition adds or removes, and 23:30 in a zone that is a day ahead of UTC.
  • Run the same prompt with several different injected “current times” and check the resolved dates move consistently. A model that ignores the anchor produces the same answer regardless, which is an immediate red flag and a common one.
  • Test with users in at least two timezones represented in the same conversation, which is where the offset-versus-zone confusion surfaces.
  • Log the model’s emitted specification alongside the resolved instant. When someone reports a meeting on the wrong day, that pair is the difference between a five-minute diagnosis and an afternoon.
Date and Time Reasoning Bugs · Multigrid