How to design time-related API fields correctly — format choices, timezone handling, relative times, and avoiding the most painful datetime bugs.
Never return raw Unix timestamps as the primary format — they're not human-readable.
{ "created_at": "2025-02-22T00:00:00Z" }
{
"created_at": "2025-01-01T10:00:00Z",
"updated_at": "2025-02-01T15:30:00Z",
"published_at": "2025-01-15T08:00:00Z",
"deleted_at": null
}
{ "retry_after": 3600 }
{ "trial_period": "P14D", "session_timeout": "PT30M" }
Don't return pre-rendered relative strings from your API ('2 hours ago'). Return the absolute timestamp and let the client format it:
const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
const seconds = (new Date(post.created_at) - Date.now()) / 1000;
rtf.format(Math.round(seconds / 3600), "hour"); // "2 hours ago"
{
"access_token": "eyJ...",
"expires_at": "2025-02-22T15:00:00Z",
"expires_in": 900
}
expires_in (seconds from now) is useful for clients that don't want to parse dates; expires_at is precise and timezone-unambiguous.