Skip to content

タイムスタンプを ISO 8601 形式に整形する snippet

ISO 8601(実運用ではそのプロファイルである RFC 3339: 2026-08-25T09:30:00Z)は、API が合意できる唯一のタイムスタンプ形式です。ログ、JSON、データベースのすべてが単純な文字列として正しくソートされます。罠は次の通りです。UTC を Z または明示的なオフセット付きで出力し、オフセットなしのローカル時刻は決して出さないこと。秒にするか小数秒にするかを最初に決めること(消費側は文字レベルで一致を前提にします)。そして Go では、%Y-%m-%d のようなプレースホルダではなくリファレンスタイムを書いて整形することを思い出してください。Zig は省略です(標準ライブラリに暦がない)。C は strftime とタイムゾーンの手動配管が必要です。

ISO 8601(実運用ではそのプロファイルである RFC 3339: 2026-08-25T09:30:00Z)は、API が合意できる唯一のタイムスタンプ形式です。ログ、JSON、データベースのすべてが単純な文字列として正しくソートされます。罠は次の通りです。UTC を Z または明示的なオフセット付きで出力し、オフセットなしのローカル時刻は決して出さないこと。秒にするか小数秒にするかを最初に決めること(消費側は文字レベルで一致を前提にします)。そして Go では、%Y-%m-%d のようなプレースホルダではなくリファレンスタイムを書いて整形することを思い出してください。Zig は省略です(標準ライブラリに暦がない)。C は strftime とタイムゾーンの手動配管が必要です。

Runnable recipe · 14 languagestimestamp ツールを開く →
Dates & Timeiso8601rfc3339utcdatetimeformatting

Every language

14 languages, copy-ready. One at a time with syntax highlighting, or all inline.

SQLSQLrunnable
SELECT to_char(now() AT TIME ZONE 'UTC',
               'YYYY-MM-DD"T"HH24:MI:SS"Z"') AS iso_utc;

now() is timestamptz; AT TIME ZONE 'UTC' converts it to a naive UTC timestamp before formatting — skip it and the offset rule of the session leaks in. Runs in the playground.

Run in the SQL playground →
JSJavaScript
new Date().toISOString();
// '2026-08-25T09:30:00.123Z' — always UTC, always Z, milliseconds

toISOString never emits local time — that is its virtue. Parsing round-trips with new Date(str).

TSTypeScript
const isoNow = (): string => new Date().toISOString();

// drop the milliseconds when a consumer case-matches to the second
const isoSeconds = (d: Date = new Date()): string =>
  d.toISOString().replace(/\.\d{3}Z$/, 'Z');

The regex truncates ms; to ROUND instead, set them on the Date first (d.setMilliseconds(0)).

GoGo
now := time.Now().UTC()

fmt.Println(now.Format(time.RFC3339))     // 2026-08-25T09:30:00Z
fmt.Println(now.Format(time.RFC3339Nano)) // ...T09:30:00.123456789Z

Go formats by REFERENCE TIME — the layout is 2006-01-02 15:04:05 (Jan 2, 3:04:05pm). Skip .UTC() and you get a +02:00 local offset instead of Z.

RsRust
use chrono::{SecondsFormat, Utc};

fn main() {
    let iso = Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true);
    println!("{iso}"); // 2026-08-25T09:30:00.123Z
}

The second argument (true) picks Z over +00:00. chrono is the de-facto date crate; the lighter `time` crate has the same via format_description.

PHPPHP
$utc = new DateTimeImmutable('now', new DateTimeZone('UTC'));

echo $utc->format('Y-m-d\TH:i:s\Z'); // 2026-08-25T09:30:00Z
echo (new DateTimeImmutable('now'))->format('c'); // RFC 3339 with LOCAL offset

'c' is the built-in ISO constant — but it keeps the server timezone, so pin UTC explicitly for API output. DateTimeImmutable over DateTime, always.

PyPython
from datetime import datetime, timezone

now = datetime.now(timezone.utc)  # tz-AWARE — the only safe default

print(now.isoformat())                       # ...+00:00
print(now.strftime('%Y-%m-%dT%H:%M:%SZ'))    # hard Z requires UTC

datetime.utcnow() is deprecated since 3.12 — it returns a NAIVE datetime that serializes without an offset. now(timezone.utc) is aware.

CC
#include <stdio.h>
#include <time.h>

int main(void) {
    char buf[32];
    time_t now = time(NULL);
    struct tm tmv;
    gmtime_r(&now, &tmv);  // thread-safe POSIX — not gmtime()
    strftime(buf, sizeof buf, "%Y-%m-%dT%H:%M:%SZ", &tmv);
    puts(buf);             // 2026-08-25T09:30:00Z
}

gmtime_r (thread-safe POSIX) over gmtime — the plain one returns a pointer to ONE shared static buffer. localtime would emit a local wall clock you must offset manually — the Z suffix is only honest on the UTC path. Years before 1900 and beyond struct tm's range fail; Windows spells it gmtime_s.

C++C++
#include <chrono>
#include <format>

auto now = std::chrono::system_clock::now();
auto secs = std::chrono::floor<std::chrono::seconds>(now); // drop sub-second

std::format("{:%Y-%m-%dT%H:%M:%SZ}", secs); // 2026-08-25T09:30:00Z

The floor to seconds drops the sub-second part BEFORE formatting — without it std::format emits fractional digits. std::format("{}", secs) prints a sensible ISO-ish default, but only the explicit format string pins the trailing Z. Needs C++20 std::format: GCC 13+ or MSVC.

C#C#
// "o" = round-trip format: 7 fractional digits + real offset
string full = DateTimeOffset.UtcNow.ToString("o");

// plain Z-terminated seconds
string z = DateTimeOffset.UtcNow.ToString("yyyy-MM-dd'T'HH:mm:ss'Z'");

Prefer DateTimeOffset over DateTime — the latter's "o" output depends on the Kind flag, which is invisible at the call site.

JvJava
import java.time.Instant;

String iso = Instant.now().toString();
// 2026-08-25T09:30:00.123Z — drops .000 entirely when exactly on the second

Instant.toString IS ISO-8601 UTC — no formatter needed. The variable precision (with/without millis) surprises strict parsers; DateTimeFormatter.ISO_INSTANT pins it.

SwSwift
import Foundation

let formatter = ISO8601DateFormatter() // seconds + Z by default
let iso = formatter.string(from: Date())

// fractional seconds is a separate, explicit option
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]

Formatters are expensive — build one once and reuse (ISO8601DateFormatter is thread-safe). Date.ISO8601FormatStyle is the Swift 5.5+ spelling.

KtKotlin
import java.time.Instant

val iso: String = Instant.now().toString() // ISO-8601 UTC, always

java.time (JSR-310) is immutable and thread-safe — none of the old Date/Calendar hazards. OffsetDateTime for non-UTC displays.

RbRuby
require 'time' # stdlib — patches Time with iso8601

Time.now.utc.iso8601    # 2026-08-25T09:30:00Z
Time.now.utc.iso8601(3) # 2026-08-25T09:30:00.123Z

Time#iso8601 takes the fractional-seconds digit count as an argument — an integer, not a format string.

Keep going

Try the interactive timestamp tool →