Specification scope: Core with the Units and Semantic Annotations companion specifications.
Keep separate timestamps for separate events. A weather observation can carry three:
phenomenonTime says when the value applied to the world, resultTime says
when the result became available, and ingestionTime says when a declared
receiving system accepted the record. The values may be close or even equal,
but a generic timestamp leaves consumers guessing which event it records.
A weather observation with three timestamps
This station samples air temperature, computes a quality-controlled result, and then sends the record through a telemetry gateway:
{
"$schema": "https://json-structure.org/meta/semantic-annotations/v0/#",
"$id": "https://schemas.example.com/weather/temperature-observation/v1",
"$uses": ["JSONStructureSemanticAnnotations"],
"name": "TemperatureObservationDocument",
"$root": "#/definitions/TemperatureObservation",
"definitions": {
"TemperatureObservation": {
"name": "TemperatureObservation",
"type": "object",
"observedProperty": {
"reference": "http://qudt.org/vocab/quantitykind/Temperature",
"kind": "qudt-quantity-kind"
},
"properties": {
"stationId": {
"type": "string",
"semanticRole": "featureOfInterest"
},
"observedAt": {
"type": "datetime",
"semanticRole": "phenomenonTime"
},
"resultAvailableAt": {
"type": "datetime",
"semanticRole": "resultTime"
},
"gatewayAcceptedAt": {
"type": "datetime",
"semanticRole": "ingestionTime"
},
"airTemperature": {
"type": "double",
"unit": "°C",
"semanticRole": "observationValue"
}
},
"required": [
"stationId",
"observedAt",
"resultAvailableAt",
"gatewayAcceptedAt",
"airTemperature"
],
"additionalProperties": false
}
}
}
The corresponding record makes the timing visible:
{
"$schema": "https://schemas.example.com/weather/temperature-observation/v1",
"stationId": "WX-BER-042",
"observedAt": "2026-11-20T06:15:00Z",
"resultAvailableAt": "2026-11-20T06:15:04Z",
"gatewayAcceptedAt": "2026-11-20T06:15:19Z",
"airTemperature": 7.4
}
In this example, the temperature applies at 06:15:00. Sensor processing and quality checks make the result available 4 seconds later, at 06:15:04. Network transit and gateway work add 15 seconds before acceptance at 06:15:19.
Use phenomenon time for event-time analysis
phenomenonTime is the time during which the result applies to the observed
property. For an instantaneous reading, it annotates a temporal position. It
may also annotate a named object or tuple representing a period; flattened
periods use the separate phenomenonTimeStart and phenomenonTimeEnd roles.
Windowing observations by gatewayAcceptedAt would move delayed records into
the wrong weather interval. Joining temperature with wind by result production
time would align processing schedules, not atmospheric conditions. Event-time
analytics therefore needs the phenomenon clock.
Compare result and phenomenon time for production delay
resultTime is the temporal position at which the result became available.
The difference between result and phenomenon time describes observation
production delay. That delay may come from sensor integration, laboratory
analysis, model execution, quality control, or publication.
The role does not say that result time must follow phenomenon time in every domain. Forecasts are observations whose result time precedes their phenomenon time. The roles remain useful because they do not require result time to follow phenomenon time.
Ingestion time depends on a receiving system
ingestionTime is an operational event-time role: the temporal position when a
declared system accepted the record. In this schema that system is the telemetry
gateway named by the property. A data lake could add its own acceptance time in
a downstream record because ingestion is relative to an operational boundary.
The difference between ingestion and result time measures transport and pipeline delay to that boundary. On replay, an old observation can acquire a new ingestion time without acquiring a new phenomenon or result time.
One timestamp cannot answer three questions
A generic timestamp forces every consumer to guess whether the field is safe
for event-time windows, measures sensor latency, or can drive a pipeline
freshness alert. One value cannot answer all three unless the system guarantees
the events coincide, and even then the roles describe different events.
semanticRole carries the distinction without changing the instance encoding.
All three fields remain ordinary datetime values. The annotation tells a
processor which temporal concern each value serves; the Core type and any
temporal reference-system binding tell it how the position is represented.
Cadence, statistics, derivation, and temporal reference systems can add more
context later. They cannot recover which event an undifferentiated timestamp
was meant to record.