Dates, numbers and encoding pitfalls across formats
Why dates should be strings or epochs but never both, how big integers and floats lose precision in JSON, and the null, bytes and Unicode differences between formats.
Dates: pick one representation
| Representation | Example | Problem |
|---|---|---|
| ISO 8601 string | 2026-09-18T10:00:00Z | None for interchange; sorting is lexicographic and correct |
| Epoch seconds | 1789123456 | Ambiguous unit; range limited on 32-bit systems |
| Epoch millis | 1789123456789 | Needs 53 bits for precision in JavaScript |
| Local string | 18/09/2026 10:00 | Ambiguous order and no zone — never interchange this |
| Date only | 2026-09-18 | Is not an instant; do not convert it to a timestamp |
{
"createdAt": "2026-09-18T10:00:00Z",
"createdAtEpoch": 1789123200,
"birthday": "1990-04-01"
}Use the string form for anything crossing a boundary, and store a timestamp in the database. Keeping both an ISO string and an epoch in the same document is a guaranteed source of disagreement.
Numbers that do not survive the trip
JSON.parse('{"id": 9223372036854775807}')
// -> { id: 9223372036854775808 } wrong, precision lost
JSON.stringify({ x: 0.1 + 0.2 })
// -> '{"x":0.3}', but the value is 0.30000000000000004
// money as a float accumulates error
0.1 + 0.2 === 0.3 // false- JSON numbers are IEEE 754 doubles in most parsers, so integers above 2^53 lose precision.
- Send 64-bit IDs as strings; the receiver can parse them with a big-integer library.
- Represent money as a minor-unit integer (
1999) or a string decimal ("19.99"), never a float. - YAML and TOML preserve integer types when the reader supports them; CSV does not.
Null, bytes and text across formats
| Concept | JSON | MessagePack/CBOR | CSV |
|---|---|---|---|
| Missing value | Omitted key | Omitted key | Short row or empty field |
| Explicit null | null | nil / null | Empty field — indistinguishable from empty string |
| Binary data | Base64 string (convention) | Native byte string | Base64, and usually broken by line endings |
| Non-finite numbers | Invalid JSON (Infinity, NaN) | May be supported | Plain text token |
| Unicode | UTF-8, \uXXXX escapes | UTF-8, possibly invalid | Depends on the file encoding |
The CSV row is the dangerous one: an empty field, an empty string and a null are all the same bytes. If nullability matters, encode it explicitly — for example an empty field means null and "" means the empty string.
⚠️
Do not put non-finite numbers in JSON.
Infinity, -Infinity and NaN are not valid JSON tokens, and a strict parser will reject the document while a lenient one quietly accepts it — which means the bug appears only in production.FAQ
Should I send dates as epoch integers?
Only for internal, high-volume payloads where both ends agree on units and range. For anything long-lived or cross-team, ISO 8601 strings in UTC are self-describing and sort correctly as text.
How do I send a 64-bit ID through JSON?
As a string. Parsers in JavaScript and many dynamic languages lose precision above 2^53, and the corruption is silent because no exception is raised.
Related
Converting between formats with jq, yq and csvkit Choosing a format: size, speed, readability and tooling
Last refreshed 2026-09-18.