{history:item_id:field_id:time:format}

Description

Reads a field value from an item edit history (the change log). Give it the item long id and the field id; it returns what that field held at an earlier revision. The third argument time selects which revision: empty or -1 is the last value before the current one, -2 the one before that, and so on; a positive number is a Unix timestamp and returns the value in force at that moment (falling back to the current value if no older change is recorded); any other string (for example a single bar) is used as a delimiter and joins ALL historical values of that field. The fourth argument format is a template applied to each value, with the placeholders _#val, _#date, _#user and _#timestamp; it defaults to _#val (the bare value). A field with no recorded change history - and likewise a missing or unmatched item id or field id - returns an empty string (it does not error).

Parameters

item_id required default (none - required)

Long id (32 hex chars) of the item whose history you want to read. Required - an empty or missing item_id raises a fatal error, so always pass a real long id (often 3161cf1bc90504c8688af6551535f0d1 for the current item).

field_id required default (none - required)

Field id (slice column selector, e.g. headline........ or status_code.....) to read from the item history. Required - passing the item id alone, with no field_id, raises a fatal error. A field that has no recorded change returns an empty string.

time optional default -1 (last value before the current one)

Selects which revision. A negative integer is a step back: -1 (the default) is the last value before the current one, -2 the value before that, and so on; stepping past the oldest record returns empty. A positive integer is a Unix timestamp - it returns the value in force at that time, or the current value if no change is recorded at/after it. Any non-integer string (for example a single bar) is treated as a delimiter and joins ALL historical values of the field into one string.

format optional default _#val (the bare value)

Template applied to each returned value, using the placeholders _#val (the value), _#date (change time as Y-m-d H:i:s), _#user (numeric user id who made the change) and _#timestamp (Unix seconds). With a delimiter time, the format is applied to every value before they are joined. Only _#val is deterministic across installs; _#date/_#user/_#timestamp depend on when and by whom the edit was made.

Examples

test[{history:5726c2c6b035d7aab450d1794e9e90d7:status_code.....}]
Expected[3]
Actual[3]
The default time is -1, the last value status_code held before its current value. This frozen fixture item was last moved out of holding, so its previous status code was 3 (pending).
test[{history:5726c2c6b035d7aab450d1794e9e90d7:headline........}]
Expected[]
Actual[]
The headline of this fixture item was never edited after creation, so there is no change record and history returns an empty string.
test[{history:5726c2c6b035d7aab450d1794e9e90d7:status_code.....:-2}]
Expected[1]
Actual[1]
A time of -2 is the value two steps back. The status_code history of this item holds two values (3 then 1), so -2 returns 1.
test[{history:5726c2c6b035d7aab450d1794e9e90d7:status_code.....:-3}]
Expected[]
Actual[]
Only two historical status_code values exist, so asking for the third step back (-3) returns an empty string.
test[{history:5726c2c6b035d7aab450d1794e9e90d7:status_code.....:|}]
Expected[3|1]
Actual[3|1]
When time is a non-integer string it is used as a delimiter and the whole history of the field is returned. Here the two recorded status_code values are joined by a bar: newest first.
test{history:5726c2c6b035d7aab450d1794e9e90d7:status_code.....:|:[_#val]}
Expected[3]|[1]
Actual[3]|[1]
The format argument is applied to every value before they are joined. Here each value is wrapped in brackets; the _#val placeholder is the only one that is deterministic across installs.
virtual{history:5726c2c6b035d7aab450d1794e9e90d7:status_code.....:-1:_#val on _#date by user _#user}
Expected(3 on 2026-06-04 01:42:25 by user 35)
Actual3 on 2026-06-04 01:42:25 by user 35
_#date, _#user and _#timestamp report when and by whom the change was made, so the output depends on the install and is not asserted. _#date is Y-m-d H:i:s, _#user is the numeric user id, _#timestamp is Unix seconds.
test[{history:5726c2c6b035d7aab450d1794e9e90d7:status_code.....:4000000000}]
Expected[1]
Actual[1]
A positive time is read as a Unix timestamp and returns the value in force then. 4000000000 is far in the future, so no later change exists and history falls back to the current value (this item is active, status 1).
test[{history:00000000000000000000000000000000:status_code.....}]
Expected[]
Actual[]
A well-formed but non-existent item id has no change records, so history returns an empty string. Note: the item id and field id must both be syntactically present - calling history with no field id at all raises a fatal error rather than returning empty.