Imagine a midnight alert: your user-facing service logs show an event at 1672531199, but customers across time zones insist it happened the next day. A pager duty helps, the log lines are confusing, and the incident report blames a “timestamp format”. This is a real production problem: a mismatch between epoch units, UTC storage, and local display. Understanding seconds vs milliseconds, how JavaScript and servers treat epoch values, and how daylight saving time (DST) can make logs look wrong will make debugging fast and reliable.
Why timestamps break in production logs
Most modern systems store time as an integer epoch value and convert to human-readable strings only for display. Problems arise when different systems assume different units (seconds vs milliseconds), when logs mix UTC and local timestamps, and when DST or timezone offsets are not taken into account when interpreting a timestamp. The symptoms: events appearing in the wrong hour/day, off-by-1000 errors, and inconsistent sorting in dashboards.
Key concepts: epoch, units, UTC, and local display
Start with these definitions:
- Epoch (Unix time): seconds (or milliseconds) since 1970-01-01T00:00:00Z.
- Seconds vs milliseconds: historically Unix time is in seconds. Many languages and libraries (notably JavaScript) represent epoch in milliseconds.
- UTC: Coordinated Universal Time. Storage in UTC avoids ambiguity across zones.
- Local display: UI or logs often convert UTC to a local timezone for readability, which introduces offsets and DST rules.
Common production-log scenario
Imagine a backend service writes events as JSON to a log file using a timestamp in seconds. A downstream analytics service expects milliseconds and parses the value as milliseconds. The parsed time will be interpreted as year 1970 or far in the future, depending on the conversion, producing impossible spikes.
Concrete example: seconds vs milliseconds
// Backend writes epoch seconds
{ "event": "login", "timestamp": 1700000000 }
// JavaScript Date expects milliseconds
const d = new Date(1700000000);
console.log(d.toISOString()); // 1970-01-20T... (wrong)
// Correct conversion
const dCorrect = new Date(1700000000 * 1000);
console.log(dCorrect.toISOString()); // 2023-11-14T... (expected)
How JavaScript treats epoch values
JavaScript Date takes milliseconds. If you pass a seconds value without multiplying, you get a date near the epoch. If you receive a millisecond value but your server expects seconds, you may truncate (divide by 1000) and lose subsecond precision.
Use built-in helpers and validate units when parsing:
function toIsoFromPossibleSeconds(value) {
// simple heuristic: values < 1e11 are probably seconds
const v = Number(value);
const ms = v < 1e11 ? v * 1000 : v; // 1e11 is ~ Sat Mar 03 5138 if ms
return new Date(ms).toISOString();
}
Daylight saving and timezone surprises
Storing UTC avoids wrong offset storage, but local display must respect the user’s timezone rules. DST transitions can cause local timestamps to jump forward or back an hour. That means two log entries with different UTC times could show the same local clock time (ambiguous hour during fall back), or there might be a gap (spring forward).
Example: DST ambiguity
// UTC times around an autumn DST change for America/New_York
// 2021-11-07T05:30:00Z -> 01:30:00-04:00 (before fall back)
// 2021-11-07T06:30:00Z -> 01:30:00-05:00 (after fall back)
// Both map to local clock "01:30", but different instants.
When debugging user complaints, always include both the UTC timestamp and the user’s local converted timestamp including offset and timezone name.
API contracts: make units and timezone explicit
An API contract should state:
- Which epoch unit is used (seconds or milliseconds).
- Whether timestamps are UTC or include timezone offsets/ISO 8601 strings.
- Whether subsecond precision is required and how rounding is handled.
A contract example (JSON schema style):
{
"type": "object",
"properties": {
"eventTime": { "type": "integer", "description": "Unix epoch milliseconds (UTC)" }
}
}
Reproducible investigation checklist
When you see a timestamp-related incident, follow this ordered workflow to reduce noise and find the root cause.
- Collect the raw timestamp value(s) from logs or API payloads (do not rely on UI text).
- Note the system that produced the value and its declared unit (seconds/milliseconds) and timezone policy.
- Convert the raw value to UTC ISO 8601 and to the complaining user’s local time. Record both forms.
- Check for DST boundary dates for the user’s timezone using IANA zone rules.
- Re-run the conversion using a trusted tool or library on a separate machine to rule out local machine misconfiguration.
- Search for other events with similar timestamps to determine if the issue is isolated or systemic.
- Confirm the API contract and update code or docs if mismatch is found; add automated tests that assert units and timezone handling.
Quick checklist to run during an incident
- Grab raw numeric timestamps from logs or payloads.
- Determine if the number is seconds or milliseconds.
- Convert to ISO 8601 UTC and local time with offset.
- Check system clocks (NTP drift) on involved hosts.
- Validate timezone database (IANA TZ) versions on servers if anomalies occur around DST transitions.
- Note any API contract mismatches and escalate to the responsible team.
One-table cheat sheet
| Representation | Example value | Interpreted time (UTC) |
|---|---|---|
| Epoch seconds | 1700000000 | 2023-11-14T…Z (after *1000) |
| Epoch milliseconds | 1700000000000 | 2023-11-14T…Z |
| ISO 8601 string | 2023-11-14T12:34:56+01:00 | 2023-11-14T11:34:56Z |
Common mistakes and how to avoid them
- Assuming unit: Treat numeric epoch values as ambiguous—apply heuristics or explicit unit fields.
- Ignoring timezone metadata: Prefer storing UTC and keep timezone conversions in the presentation layer.
- Using local machine TZ in CI/tests: Force a timezone in tests or use only UTC to avoid flaky results.
- Relying on string parsing without strict format: Require RFC 3339 / ISO 8601 for strings to avoid locale parsing differences.
Limitations and privacy considerations
Epoch timestamps themselves are not personally identifiable information (PII), but combining high-resolution timestamps with other logs can facilitate user activity reconstruction. When sharing logs externally, mask user identifiers and consider reducing timestamp resolution (for example, remove subsecond precision) if privacy is a concern. Also note that timezone databases change: IANA publishes updates when political decisions alter rules. Systems that rely on local timezone conversions should keep their TZ data current.
Using a converter during debugging
During an incident you can use a small converter to inspect values quickly. The placeholder tool Unix Timestamp Converter can convert between seconds and milliseconds, show UTC ISO strings, and display common timezone offsets. It is useful for quick checks, but cannot access your internal logs or apply organizational timezone policies—always verify with production data and authoritative libraries in your stack.
Two more concrete examples
Example: API mismatch causing spikes
// Client sends milliseconds but server saved as seconds
// Client payload
{ "createdAt": 1700000000000 }
// Server code (incorrectly) casts to int and stores as-is in a seconds column
INSERT INTO events (created_at_seconds) VALUES (1700000000000);
-- Querying and interpreting as seconds yields year ~53858
Fix: ensure the server validates units and either converts to UTC seconds or stores full milliseconds in a bigint column.
Example: user reports event “missing hour”
// Raw log line
2023-03-12 07:30:00Z - event=signup user=42
// User in America/Los_Angeles reports local time shows 00:30 but expected 01:30
// On 2023-03-12, DST spring-forward occurred at 02:00 local time (clocks jumped forward)
// Convert and explain: 07:30Z == 00:30 PST (before the DST jump), so behavior is correct.
FAQ
Q: Should I store epoch seconds or milliseconds?
A: Choose based on required precision. Milliseconds are common and work well with JavaScript and high-resolution logging. If you choose seconds, document it in the API contract and enforce conversions on ingest.
Q: How do I test DST-related behavior?
A: Write unit tests that mock timezone conversions using an up-to-date TZ database and include tests for dates at DST transitions (before, during, after). Run them in CI with a consistent TZ or explicitly set TZ in the test harness.
Q: Can I rely on client-side timezone for auditing?
A: No. Client timezones can be spoofed or misconfigured. For auditing, store UTC server timestamps and optionally record the client’s reported timezone as metadata for display only.
Final notes
Timing bugs are rarely about arithmetic alone; they’re about assumptions. Treat numeric timestamps as data with a declared unit, always convert and log UTC alongside any local representation, and make API contracts explicit. Use the ordered workflow and checklist above when troubleshooting—these steps make incidents reproducible and reduce the time to fix.
Sources
Editorial note: This guide is an educational overview. Confirm the output against the documentation and workflow that apply to your project.