A Unix timestamp is a number in a defined time scale relative to an epoch. Conversion is reliable only when the input contract states the epoch, unit, time scale, sign convention, and display zone. A number’s digit count is a useful hint for contemporary data, but it is not proof of seconds, milliseconds, microseconds, or nanoseconds.

TL;DR

  • Record the unit explicitly: s, ms, us, or ns. Do not make a digit-count guess the protocol.
  • Unix time is normally expressed relative to 1970-01-01T00:00:00Z; negative values represent instants before that epoch.
  • UTC is a stable interchange representation. Use an IANA time-zone identifier, such as Asia/Shanghai or America/New_York, for local display.
  • Date, Python datetime, and database types have different precision and range limits. Converting nanoseconds to milliseconds is a lossy presentation step.
  • A monotonic clock measures elapsed duration; it is not an epoch timestamp and cannot be converted to a calendar date.

Define the Timestamp Contract

Before opening an online converter or writing code, capture the metadata next to the value:

Field Example
Epoch Unix epoch, 1970-01-01T00:00:00Z
Time scale UTC-like POSIX time, or a documented system scale
Unit ms
Encoding signed decimal integer, string, or binary integer
Precision policy preserve, truncate, or round
Display UTC, or Europe/Berlin via IANA tzdata

The Unix/POSIX model does not encode a leap second as a separate civil timestamp. If a source uses TAI, GPS time, a device tick counter, or a vendor-specific scale, it needs a different conversion contract. “Epoch time” is not a license to assume the source’s time scale.

An interactive timestamp converter is useful for a one-off value when the unit is known. Do not paste credentials, full log lines, or personal data into a service until its processing and retention behavior are verified; for sensitive material, run a local script.

Units and Precision

Unit Multiplier to seconds Typical representation
seconds (s) 1 1738886400
milliseconds (ms) 1,000 1738886400000
microseconds (us) 1,000,000 1738886400000000
nanoseconds (ns) 1,000,000,000 1738886400000000000

The examples above happen to describe the same instant, but the values are not interchangeable. A 13-digit value may be milliseconds today, but an application can choose another unit or a different date range. Negative values also make a simplistic length <= 10 rule unreliable. Infer a unit only as a bounded diagnostic heuristic and confirm it from the schema, field name, producer code, or sample data.

Precision, resolution, accuracy, synchronization, and ordering are different properties. A nanosecond-shaped integer does not prove that a clock measured events 1 ns apart, and two timestamps with nanosecond fields do not automatically establish causal order across machines.

Time Zones and Civil Time

Store an instant and its unit, preferably in a typed database column or a canonical UTC representation. Convert to local civil time only at an interface boundary. A time zone is not a fixed offset: IANA zones include historical changes and daylight-saving transitions. UTC+08:00 and Asia/Shanghai are not interchangeable for every historical date.

A displayed local time can be ambiguous or nonexistent during a daylight-saving transition. The conversion API should define whether it chooses the earlier offset, later offset, or rejects the input. Keep the original instant so a later display-zone change does not lose information.

Correct Conversion Code

JavaScript: Require a Unit and Preserve Sub-milliseconds

This example accepts an integer string or bigint. It returns the millisecond Date plus the remainder instead of pretending that Date can represent nanoseconds:

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

function toEpochNanoseconds(value, unit) {
  if (!/^-?\d+$/.test(String(value))) {
    throw new TypeError("value must be a signed integer");
  }
  const integer = BigInt(value);
  const multipliers = {
    s: NS_PER_SECOND,
    ms: NS_PER_MILLISECOND,
    us: 1_000n,
    ns: 1n,
  };
  if (!(unit in multipliers)) throw new RangeError("unsupported unit");
  return integer * multipliers[unit];
}

export function epochToUtc(value, unit) {
  const ns = toEpochNanoseconds(value, unit);
  const milliseconds = ns >= 0n
    ? ns / NS_PER_MILLISECOND
    : -((-ns + NS_PER_MILLISECOND - 1n) / NS_PER_MILLISECOND);
  const remainder = ns - milliseconds * NS_PER_MILLISECOND;
  const numericMilliseconds = Number(milliseconds);
  if (!Number.isSafeInteger(numericMilliseconds)) {
    throw new RangeError("outside the exact JavaScript Date range");
  }
  const date = new Date(numericMilliseconds);
  if (Number.isNaN(date.getTime())) throw new RangeError("invalid Date range");
  return { date, remainderNanoseconds: remainder };
}

console.log(epochToUtc("1738886400000000000", "ns"));

Date stores milliseconds, so the remainder is application data, not a property of the Date object. Use Intl.DateTimeFormat with an explicit timeZone for presentation. Do not pass an unsafe integer Number when the original value can exceed 2^53 - 1.

Python: Floor-Safe Subdivision

Python keeps integers exact, while datetime commonly exposes microsecond precision. Split the integer first and state the discarded precision:

python
from datetime import datetime, timezone

UNITS = {"s": 1, "ms": 1_000, "us": 1_000_000, "ns": 1_000_000_000}


def epoch_to_utc(value: int, unit: str) -> tuple[datetime, int]:
    if unit not in UNITS:
        raise ValueError("unit must be s, ms, us, or ns")
    if not isinstance(value, int):
        raise TypeError("value must be an integer")

    total_ns = value * (1_000_000_000 // UNITS[unit])
    seconds, remainder_ns = divmod(total_ns, 1_000_000_000)
    microseconds, discarded_ns = divmod(remainder_ns, 1_000)
    instant = datetime.fromtimestamp(seconds, tz=timezone.utc).replace(
        microsecond=microseconds
    )
    return instant, discarded_ns

instant, discarded = epoch_to_utc(-1, "ms")
assert instant.isoformat() == "1969-12-31T23:59:59.999000+00:00"
assert discarded == 0

divmod gives the correct floor-based result for negative values. Python’s datetime cannot retain nanoseconds beyond its microsecond field, so the returned remainder must not be silently discarded if ordering or audit evidence depends on it.

Go: Use the Unit-Specific API

Go’s time.Unix accepts seconds and nanoseconds within the same call; UnixMilli and UnixMicro make other contracts explicit:

go
package main

import (
	"fmt"
	"time"
)

func epochToUTC(value int64, unit string) (time.Time, error) {
	switch unit {
	case "s":
		return time.Unix(value, 0).UTC(), nil
	case "ms":
		return time.UnixMilli(value).UTC(), nil
	case "us":
		return time.UnixMicro(value).UTC(), nil
	case "ns":
		seconds := value / 1_000_000_000
		nanos := value % 1_000_000_000
		return time.Unix(seconds, nanos).UTC(), nil
	default:
		return time.Time{}, fmt.Errorf("unsupported unit %q", unit)
	}
}

The int64 range and time.Time range still apply. Validate the source’s range before conversion and keep a string or wider representation when the protocol can exceed it.

Diagnostic Workflow

For an API response or log:

  1. Identify the producer, field type, epoch, unit, and time scale from the schema or source code.
  2. Preserve the original value and a log/event identifier; redact unrelated fields.
  3. Convert to UTC without rounding, then display in the operator’s IANA time zone.
  4. Compare several known events, including a negative or boundary value when relevant.
  5. Check clock synchronization, serialization, database precision, and whether the field is a duration rather than an instant.

A timestamp conversion only checks representation. It does not prove that a request happened, that two machines were synchronized, or that a log entry is authentic.

Epoch Time Is Not a Monotonic Clock

Wall-clock time can move backward or forward because of synchronization, manual changes, virtualization, or leap-smearing policy. A monotonic clock is designed for measuring elapsed duration:

  • JavaScript: performance.now()
  • Python: time.monotonic_ns()
  • Java: System.nanoTime()
  • Go: the monotonic component carried by time.Time values from time.Now()

These values have no Unix epoch and must not be sent to a timestamp converter. Use a wall-clock instant for event time and a monotonic reading for timeout or latency measurement; store both when a trace needs both meanings.

FAQ

Can digit count identify the unit?

No. It is a heuristic tied to a date range and a particular producer. Confirm the unit from a schema, API contract, code, or repeated observations before converting.

What does a negative Unix timestamp mean?

Under the Unix epoch convention, it represents an instant before 1970-01-01T00:00:00Z. The exact behavior for historical civil time still depends on the time scale and time-zone database used for display.

Does a nanosecond timestamp guarantee nanosecond accuracy?

No. It may only be a storage or serialization resolution. Clock accuracy, measurement latency, synchronization, and cross-host ordering require separate evidence.

Why does a Python or JavaScript result lose precision?

The target type may have a smaller precision or range. JavaScript Date stores milliseconds, Python datetime stores microseconds, and JSON/Number may lose large integer precision. Preserve the original integer and an explicit remainder when that information matters.

Is an online converter appropriate for production data?

It is useful for a single, non-sensitive value after the unit is known. Production pipelines should use versioned code, tests, access controls, and an auditable data path; never assume a browser conversion proves local processing or data deletion without verifying the implementation and policy.

Conclusion

Timestamp conversion is a contract problem before it is a UI problem. State the epoch, time scale, unit, precision policy, and display zone; preserve exact input; distinguish instants from durations; and treat digit-based auto-detection as a diagnostic hint only. These practices make an online lookup convenient without allowing a plausible-looking date to become false evidence.

References