Skip to content

Timestamp Converter — PHP source

Convert Unix epoch timestamps to human-readable dates and back. See seconds, milliseconds, ISO 8601, UTC, local and relative time at once.

This is the PHP implementation — the same logic the interactive tool runs, in a shareable, citable form.

<?php
/**
 * Unix epoch (seconds & milliseconds) <-> human forms: ISO 8601, UTC
 * string, local date string, and relative time ("3 hr ago" / "in 2 day").
 *
 * Language: PHP
 * CosmoDev polyglot showcase port of the `timestamp` tool.
 * Ported from src/tools/TimestampConverter.tsx — display source, part of
 * CosmoDev's polyglot tool pages.
 *
 * Every form derives from a single epoch-seconds value via the built-in
 * DateTime class (the "@" timestamp form). relative_time() buckets
 * |now - t| into sec/min/hr/day/month/yr using the same 60s / 60m / 24h /
 * 30d / 365d thresholds and signs the phrase ("ago" / "in …"). now_ms is
 * a parameter so the function is deterministic and unit-testable; it
 * defaults to the current wall clock.
 */

declare(strict_types=1);

const INVALID = '—';

/**
 * Return "3 hr ago" / "in 2 day" / "42 sec ago", mirroring the island.
 *
 * @param int      $ms    Target instant in epoch milliseconds.
 * @param int|null $nowMs Reference instant ("now"); defaults to now.
 */
function relative_time(int $ms, ?int $nowMs = null): string
{
    $nowMs ??= (int) (microtime(true) * 1000);
    $diff = $ms - $nowMs;
    $abs = abs($diff);
    $sec = 1000;
    $min = 60_000;
    $hr = 3_600_000;
    $day = 86_400_000;

    if ($abs < $min) {
        $n = (int) round($abs / $sec);
        $unit = 'sec';
    } elseif ($abs < $hr) {
        $n = (int) round($abs / $min);
        $unit = 'min';
    } elseif ($abs < $day) {
        $n = (int) round($abs / $hr);
        $unit = 'hr';
    } elseif ($abs < $day * 30) {
        $n = (int) round($abs / $day);
        $unit = 'day';
    } elseif ($abs < $day * 365) {
        $n = (int) round($abs / ($day * 30));
        $unit = 'month';
    } else {
        $n = (int) round($abs / ($day * 365));
        $unit = 'yr';
    }

    $s = "{$n} {$unit}";
    return $diff < 0 ? "{$s} ago" : "in {$s}";
}

/**
 * Render every form the tool exposes from epoch seconds.
 *
 * Matches the island's row table verbatim. "@<secs>" makes PHP interpret
 * the value as UTC internally; we render ISO and the UTC string in UTC,
 * and the local string in the script's default zone. Invalid instants
 * (out-of-range integers) render an em dash.
 *
 * @return array<string,string>
 */
function format_timestamp(int $secs, ?int $nowMs = null): array
{
    $dt = @DateTime::createFromFormat('U', (string) $secs);
    if ($dt === false) {
        return [
            'Epoch (s)' => (string) $secs,
            'Epoch (ms)' => (string) ($secs * 1000),
            'ISO 8601' => INVALID,
            'UTC' => INVALID,
            'Local' => INVALID,
            'Relative' => INVALID,
        ];
    }

    $utc = clone $dt;
    $utc->setTimezone(new DateTimeZone('UTC'));
    // Date.toISOString(): "2017-06-14T07:00:00.000Z" (ms always 000 for whole secs).
    $iso = $utc->format('Y-m-d\TH:i:s.v\Z');
    // toUTCString(): "Wed, 14 Jun 2017 07:00:00 GMT".
    $utcStr = $utc->format('D, d M Y H:i:s') . ' GMT';
    // toLocaleString() is locale-dependent; this is the default-zone form.
    $local = $dt->format('Y-m-d H:i:s');

    return [
        'Epoch (s)' => (string) $secs,
        'Epoch (ms)' => (string) ($secs * 1000),
        'ISO 8601' => $iso,
        'UTC' => $utcStr,
        'Local' => $local,
        'Relative' => relative_time($secs * 1000, $nowMs),
    ];
}

/**
 * Parse an ISO 8601 / datetime-local ("YYYY-MM-DDTHH:mm") string into
 * epoch seconds. Mirrors the island's <input type="datetime-local">
 * handler: parse in the host zone and floor to seconds. Returns null for
 * unparseable input (the UI silently no-ops).
 */
function from_iso(string $iso): ?int
{
    $dt = DateTime::createFromFormat(DateTime::ATOM, $iso)
        ?: DateTime::createFromFormat('Y-m-d\TH:i', $iso)
        ?: DateTime::createFromFormat('Y-m-d H:i:s', $iso)
        ?: DateTime::createFromFormat('Y-m-d', $iso);
    if ($dt === false) {
        return null;
    }
    return (int) $dt->getTimestamp();
}

// CLI demo.
$now = (int) (microtime(true) * 1000);
foreach (format_timestamp(time(), $now) as $key => $value) {
    echo str_pad($key . ':', 11) . ' ' . $value . PHP_EOL;
}

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →