A timestamp is meaningful only with a unit, epoch, sign convention, clock source, and precision contract. A 19-digit integer is often a contemporary Unix timestamp in nanoseconds, but digit count is only a heuristic: dates before the epoch, far-future values, leading zeros, negative values, and custom epochs break it. A nanosecond field also does not prove that the clock measured time accurately to one nanosecond.

Key Takeaways

  • State the unit explicitly in an API or schema, such as created_at_ns or an RFC 3339 string with a documented fractional precision.
  • Convert with integer division and remainder. Avoid floating-point conversion for 64-bit or 128-bit epoch values.
  • Unix wall-clock timestamps and monotonic durations are different domains. process.hrtime.bigint() and System.nanoTime() cannot be converted to a calendar date.
  • A unit's resolution, the clock's precision, its accuracy, and event-order guarantees are separate properties.
  • Use UTC instants for interchange and a named IANA time zone only for presentation. Do not add a fixed eight-hour offset as a general time-zone conversion.
  • Validate range, sign, overflow, fractional remainder, leap-second policy, and database/JSON representation at the boundary.

Define the Timestamp Contract

Document all of these fields:

Question Example decision
Epoch Unix epoch 1970-01-01T00:00:00Z
Unit integer nanoseconds
Sign negative values allowed for pre-epoch instants
Time scale UTC/Unix time; leap-second handling documented
Clock wall clock for dates, monotonic clock for durations
Precision stored digits versus measured clock resolution
Range minimum and maximum accepted instant
Serialization decimal string for values beyond consumer integer precision

Prefer names that carry units (event_time_ns, timeout_ms) or a typed schema. Do not make every consumer infer units from magnitude.

Units and Integer Conversion

Unix time counts units from the epoch:

text
1 second      = 1,000 milliseconds
1 millisecond = 1,000 microseconds
1 microsecond = 1,000 nanoseconds

To convert an integer count n into an instant, choose a unit and split it into whole seconds plus a non-negative fractional remainder. For nanoseconds:

text
seconds = floor_div(n, 1_000_000_000)
nanos   = floor_mod(n, 1_000_000_000)

The floor operations matter for negative timestamps. Language operators that truncate toward zero can produce a negative remainder and the wrong instant unless corrected.

Why Digit Counting Fails

The following values may be valid under different contracts:

  • 0 is the Unix epoch in every unit;
  • -1 can mean one unit before the epoch;
  • a date in 1960 has fewer digits than a current nanosecond timestamp;
  • a custom epoch or device tick count may have any magnitude;
  • a decimal string may contain leading zeros;
  • a 64-bit counter may be an elapsed duration rather than wall time.

If a legacy interface provides no unit, use a documented bounded heuristic only as a migration aid, emit an ambiguity error when multiple interpretations are plausible, and require explicit unit metadata for new interfaces.

Wall Clock, Monotonic Clock, and Accuracy

Wall clocks map an instant to an epoch and can jump because of synchronization, administrator changes, leap handling, or virtualization. Monotonic clocks are intended for elapsed-time measurement and do not map to UTC.

javascript
const started = process.hrtime.bigint();
// work
const elapsedNs = process.hrtime.bigint() - started;

elapsedNs is a duration. It must not be passed to new Date() or presented as a Unix timestamp. Use Date.now() or an injected wall clock for an event timestamp, and use the monotonic clock for timeout and latency measurement.

Resolution is the smallest representable increment; precision describes repeatability; accuracy describes closeness to a reference clock. A system can store nanoseconds while its physical clock is updated much less frequently. Distributed event ordering also needs sequence numbers, logical clocks, or trace IDs when equal or skewed wall times are possible.

JavaScript: Preserve Integer Precision

IEEE 754 Number safely represents integers only through 2^53 - 1. Contemporary epoch nanoseconds exceed that range. Keep the value as BigInt or a decimal string and convert only the part that the target API can represent:

javascript
const NS_PER_SECOND = 1_000_000_000n;
const NS_PER_MILLISECOND = 1_000_000n;

function epochNanosecondsToDate(ns) {
  const value = typeof ns === "bigint" ? ns : BigInt(ns);
  const milliseconds = value / NS_PER_MILLISECOND;
  const date = new Date(Number(milliseconds));
  if (!Number.isSafeInteger(Number(milliseconds)) || Number.isNaN(date.getTime())) {
    throw new RangeError("timestamp is outside JavaScript Date range");
  }
  return date;
}

const instant = epochNanosecondsToDate("1706140800000000000");
console.log(instant.toISOString());

Date stores milliseconds and discards sub-millisecond remainder. If the remainder matters, return { milliseconds, nanosecondsRemainder } or keep an RFC 3339 string with the supported fractional digits.

Do not compute “current nanoseconds” as Date.now() * 1_000_000 and call it a precise measurement. It is a millisecond wall-clock reading expressed in another unit.

Python: Avoid Float Epoch Conversion

Use integer seconds and nanoseconds, then attach a timezone explicitly:

python
from datetime import datetime, timezone

NANOS_PER_SECOND = 1_000_000_000


def split_epoch_nanos(value: int) -> tuple[int, int]:
    seconds, nanos = divmod(value, NANOS_PER_SECOND)
    return seconds, nanos


def epoch_nanos_to_utc(value: int) -> datetime:
    seconds, nanos = split_epoch_nanos(value)
    return datetime.fromtimestamp(seconds, tz=timezone.utc).replace(
        microsecond=nanos // 1_000
    )

Python datetime stores microseconds, so the final 0–999 nanoseconds are not retained. For exact interchange, keep the integer or a decimal string. time.time_ns() returns an integer wall-clock reading, but the operating system's clock resolution and accuracy may be lower than one nanosecond.

For display, convert the UTC instant with an IANA zone:

python
from zoneinfo import ZoneInfo

beijing = epoch_nanos_to_utc(1706140800123456789).astimezone(
    ZoneInfo("Asia/Shanghai")
)
print(beijing.isoformat())

Do not add a fixed offset as a general solution for time zones with daylight-saving or historical rule changes.

Go and Java: Use Native Epoch APIs

Go's time.Unix(seconds, nanos) accepts a seconds value and a nanosecond adjustment; use floor division for negative counts:

go
package timestamp

import "time"

const nanosPerSecond int64 = 1_000_000_000

func EpochNanosToTime(value int64) time.Time {
	seconds := value / nanosPerSecond
	nanos := value % nanosPerSecond
	if nanos < 0 {
		seconds--
		nanos += nanosPerSecond
	}
	return time.Unix(seconds, nanos).UTC()
}

Check the target range before converting. UnixNano() can overflow outside the representable range of an int64, and time.Now().UnixNano() is a wall-clock value, not a monotonic duration.

Java's Instant also accepts seconds plus a non-negative nanosecond adjustment:

java
import java.time.Instant;

public final class Timestamps {
    private static final long NANOS_PER_SECOND = 1_000_000_000L;

    public static Instant fromEpochNanos(long value) {
        long seconds = Math.floorDiv(value, NANOS_PER_SECOND);
        long nanos = Math.floorMod(value, NANOS_PER_SECOND);
        return Instant.ofEpochSecond(seconds, nanos);
    }
}

Use ZoneId.of("Asia/Shanghai") or another named IANA zone only when formatting for a person. Do not infer units from String.length() or ignore the negative remainder.

Generating Current Values

Use the API that matches the purpose:

python
import time

wall_epoch_ns = time.time_ns()       # epoch-based wall-clock reading
monotonic_ns = time.monotonic_ns()   # elapsed-time domain

The two values are not interchangeable. A monotonic clock may be the right source for a timeout, benchmark, retry budget, or span duration; an epoch clock is the right source for an event's approximate occurrence time. Neither API guarantees physical nanosecond accuracy.

Database and API Representation

Storage precision and range vary by engine and version. A timestamp column that stores microseconds cannot round-trip nanoseconds. An integer BIGINT can preserve a chosen range, but teams must define signedness, range, unit, indexing, and display conversion.

JSON has no native BigInt. For epoch nanoseconds beyond the consumer's safe integer range, transmit a decimal string with a schema:

json
{
  "event": "user_login",
  "timestamp_ns": "1706140800123456789",
  "timestamp_ms": 1706140800123
}

Do not include both units unless their consistency is validated. Reject malformed, out-of-range, ambiguous, or silently rounded values at the boundary. Record whether an input was rounded, truncated, or rejected.

Use Cases and Limits

Profiling

Measure durations with a monotonic clock and report the clock source, warm-up, sampling, and overhead. A wall-clock nanosecond timestamp cannot prove a function took a particular duration.

Logging and Tracing

Use an RFC 3339 UTC timestamp plus trace/span IDs and a monotonic duration when supported. Ordering by wall time alone is unsafe under clock skew and ties.

Financial and Scientific Systems

A nanosecond field does not establish exchange ordering, settlement correctness, or measurement accuracy. Define the time source, synchronization, uncertainty, sequence, correction, and audit rules. Monetary values should use a decimal representation rather than binary floating point.

Database Events

Store an instant and a domain sequence separately when ordering matters. Do not generate a unique ID from a timestamp alone; concurrent processes can share a timestamp and clocks can move backward.

Frequently Asked Questions

Can digit count identify timestamp precision?

Not reliably. It is only a heuristic for a bounded contemporary range. Require explicit units and reject ambiguous values in new APIs.

Does a nanosecond timestamp mean nanosecond accuracy?

No. Storage resolution, clock resolution, precision, accuracy, synchronization, and event-order guarantees are separate.

Can JavaScript Date preserve nanoseconds?

No. Date stores milliseconds. Use BigInt or a decimal string for the original value and retain the sub-millisecond remainder separately.

Are System.nanoTime() and process.hrtime.bigint() Unix timestamps?

No. They are monotonic elapsed-time sources. Use them for durations, not calendar dates.

How should time zones be handled?

Transmit and store an instant in UTC or an unambiguous offset. Apply a named IANA time zone only for presentation. Never assume “Beijing time” is a universal fixed-offset conversion for all historical or future rules.

Does Unix time include leap seconds?

Unix-time APIs and operating systems commonly ignore, smear, or otherwise model leap seconds according to their time scale. If leap-second labeling matters, document the time scale and use a domain library that supports it.

Primary Sources

Conclusion

High-resolution timestamps are a contract and clock-design problem, not merely a division by 1,000. State the epoch and unit, use integer arithmetic, separate wall time from monotonic durations, apply named time zones only at presentation, preserve precision across serialization, and measure accuracy independently. These rules prevent a plausible-looking date from hiding a wrong instant or an invalid performance claim.