# `PtcRunner.Temporal`
[🔗](https://github.com/andreasronge/ptc_runner/blob/main/lib/ptc_runner/temporal.ex#L1)

Normalize Elixir temporal structs (`DateTime`, `NaiveDateTime`, `Date`, `Time`)
to ISO 8601 strings before they cross any boundary into LLM-visible territory.

## Why this exists

Elixir's `Inspect` protocol renders temporal structs with sigil syntax:

    iex> inspect(~U[2026-05-03 09:14:00Z])
    "~U[2026-05-03 09:14:00Z]"

That's idiomatic Elixir, but every downstream consumer that expects a parseable
date string (the LLM, JSON Schema validators, `(java.util.Date. ...)` in
PTC-Lisp) breaks when it sees the sigil. ISO 8601 is the universal lingua
franca for temporal data.

This module is the central seam: any code that exposes Elixir data to an LLM
or to PTC-Lisp should normalize through `iso8601/1` (for known scalar position)
or `walk/1` (for arbitrary nested data like tool results).

## Functions

- `iso8601/1` — convert a single value. Pass-through for non-temporal terms.
- `walk/1` — recursively normalize temporal structs inside maps and lists,
  leaving everything else alone.

## Examples

    iex> PtcRunner.Temporal.iso8601(~U[2026-05-03 09:14:00Z])
    "2026-05-03T09:14:00Z"

    iex> PtcRunner.Temporal.iso8601(~D[2026-05-03])
    "2026-05-03"

    iex> PtcRunner.Temporal.iso8601(~N[2026-05-03 09:14:00])
    "2026-05-03T09:14:00"

    iex> PtcRunner.Temporal.iso8601(~T[09:14:00])
    "09:14:00"

    iex> PtcRunner.Temporal.iso8601("hello")
    "hello"

    iex> PtcRunner.Temporal.iso8601(nil)
    nil

    iex> PtcRunner.Temporal.walk(%{at: ~D[2026-05-03], items: [~T[09:14:00]]})
    %{at: "2026-05-03", items: ["09:14:00"]}

# `iso8601`

```elixir
@spec iso8601(term()) :: term()
```

Convert a temporal struct to its ISO 8601 string. Pass-through for everything
else (including non-temporal structs like user-defined ones).

# `walk`

```elixir
@spec walk(term()) :: term()
```

Recursively walk a value, normalizing any temporal structs found inside maps
and lists. Non-temporal structs are left untouched at struct boundaries
(we don't dive into them) since their internal shape is the user's contract.

Use this for tool results and other arbitrary data that gets JSON-encoded
or otherwise serialized for the LLM.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
