CSV Format for TimeSeries
Summary
This ADR defines a standardized CSV representation for TimeSeries. It specifies a row-per-record CSV format that preserves essential metadata and ensures consistent ingestion by analytics, automation, and warehousing systems.
Opinions
Opinion 1
@brysonspilman
Summary
Since the intended use of the CSV format is for retrieval only, a customized format that follows standardized csv practices is appropriate.
Key points
Topic |
Decision |
Justification |
|---|---|---|
Required columns |
Always include |
Units should exist in exactly one canonical location in all modes. Conditionally adding them as metadata comments will cause confusion over the inconsistency |
Optional columns |
Optional (off by default): |
Because headers are always included, optional columns can be toggled without breaking parsing. Clients should rely on column names, not indices. Given units are in the value header, clients will need to handle this appropriately to determine the correct column index. |
Metadata fields |
Emitted as top-of-payload comments if query parameter is set to include ( |
The following fields can be treated as metadata comments at top-of-payload: |
Units location |
Express units only in the value column header via parentheses (e.g., |
Do not include units as a separate column or in metadata comments. This avoids the anti-pattern of dual representation; units live in exactly one canonical location. Custom deserialization may be required to extract units from the header, which is preferable to duplicate representations. |
Version-date encoding |
Use |
Matches CWMS-VUE behavior. A separate CSV column per case was rejected due to lack of use-cases and schema bloat. Note this requires custom serialization handling. |
Column headers |
Always include headers |
RFC 4180 allows headers; including them keeps the format scalable if optional columns are introduced later and prevents reliance on fixed column indices. We will include a header param of |
Comments |
Treat lines beginning with |
While not part of RFC 4180, this convention is already used by CWMS endpoints (e.g., office and location-group) that return CSV, and is human-readable. |
Column naming |
Kebab-case names |
Keeps naming consistent with JSON and XML. |
Accept header for format and columns |
Use query parameters to select date format and optional columns |
Default CSV serialization uses ISO-8601 strings. Examples: |
Quality representation |
|
A bitmask (integer) compactly represents multiple boolean flags with fast native bitwise operations; a |
Nulls and missing values |
Missing values will be represented with an empty value field (null) and will have |
Keeps behavior consistent with JSON and XML. |
Encoding and delimiters |
UTF-8, comma delimiter, LF line endings |
Comma-only CSV follows RFC 4180 compliance. Tab/Pipe/semicolon delimiters will not be supported. |
Record structure |
One row per record |
A record is a single date-time and value pair; |
Single TS per payload |
Do not mix multiple time-series IDs in one payload |
Ensures a payload represents exactly one time-series. |
Example CSVs
All optionals turned off, and no metadata comments:
date-time, value (cfs) 2021-06-21T00:00:00Z, 0.0 2021-06-22T00:00:00Z, 1.0 2021-06-23T00:00:00Z, 2.0 2021-06-24T00:00:00Z, 3.0
All optionals turned off, with metadata-as-comments turned on:
# time-series-id: ALAT2.Flow-Out.Inst.1Hour.0.Rev-SWF-REGI # office-id: SWT # version-date: aggregate date-time, value (cfs) 2021-06-21T00:00:00Z, 0.0 2021-06-22T00:00:00Z, 1.0 2021-06-23T00:00:00Z, 2.0 2021-06-24T00:00:00Z, 3.0
All optionals turned on (quality and data-entry-date), with metadata-as-comments turned off:
date-time, value (cfs), data-entry-date, quality-code 2021-06-21T00:00:00Z, 0.0, 2021-06-21T00:05:00Z, 5 2021-06-22T00:00:00Z, 1.0, 2021-06-22T00:05:00Z, 5 2021-06-23T00:00:00Z, 2.0, 2021-06-23T00:05:00Z, 5 2021-06-24T00:00:00Z, 3.0, 2021-06-24T00:05:00Z, 5
Decision Status
(Status: accepted)
References
Related Types: cwms.cda.data.dto.TimeSeries, TimeSeries.Record Issue/Discussion: https://github.com/USACE/cwms-data-api/issues/1525