「本番サーバーのログを見たら、ローカルと9時間ずれてる…」という経験、心当たりがある方も多いと思います。アプリケーション開発においてタイムゾーン周りのバグは地味に厄介で、テスト環境では気づかず本番リリース後にはじめて発覚するケースも珍しくありません。

この記事では、UTC・JST・ISO 8601といった基本的な概念を整理しつつ、JavaScriptやPythonでの実装上の注意点まで順を追って解説します。

タイムゾーンとは?

タイムゾーンとは、地球上のある地域が採用している「標準時」のことです。地球は自転しているため、場所によって太陽が真上に来る時刻(正午)が異なります。それを国・地域ごとにまとめたのがタイムゾーンです。

コードの文脈でタイムゾーンを扱う際には、オフセットタイムゾーン名という2つの異なる概念を区別することが重要です。

  • オフセット:UTCを基準にした時差。+09:00-05:00 のように表記する固定値
  • タイムゾーン名Asia/TokyoAmerica/New_York のように地名で表すもの。夏時間(DST)の切り替えルールも含む

「JST = +09:00 で固定だから同じでは?」と思うかもしれませんが、夏時間を採用している国では同じタイムゾーン名でもオフセットが季節によって変わります。そのためシステム間でやり取りする際はオフセット付きのISO形式か、UTCで統一するのが安全です。

主要な概念を整理する

UTC(協定世界時)

UTC(Coordinated Universal Time)は世界の時刻基準です。オフセットは ±00:00 で、うるう秒を除けばグリニッジ標準時(GMT)とほぼ同じと考えて構いません。サーバー内部やデータベースでは基本的にUTCを使うのがベストプラクティスです。

JST(日本標準時)

JSTはUTCより9時間進んでいます(UTC+9)。日本は夏時間を採用していないため、オフセットは通年 +09:00 で固定です。

ISO 8601形式

日付と時刻を文字列で表現する国際規格がISO 8601です。タイムゾーン情報をオフセットとして末尾に付けるのが特徴です。

2026-01-15T10:30:00+09:00   // JST
2026-01-15T01:30:00Z        // UTC(Zはオフセット±00:00を意味する)

APIのレスポンスやログ出力にはこのISO 8601形式を使うことで、受け取り側が確実にタイムゾーンを解釈できます。オフセットなしの 2026-01-15T10:30:00 は曖昧なので避けましょう。

言語別の実装例

JavaScript

new Date() は実行環境のローカル時刻を返しますが、.toISOString() はUTCに変換して返します。この違いを把握していないとバグの原因になります。

const now = new Date();
console.log(now.toString());       // ローカル時刻(実行環境依存)
console.log(now.toISOString());    // UTCのISO 8601形式で出力
// → "2026-01-15T01:30:00.000Z"

// タイムゾーンを指定して表示
const formatted = now.toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' });
console.log(formatted); // "2026/1/15 10:30:00"

Python

from datetime import datetime, timezone, timedelta

# タイムゾーンなし(naive datetime)は危険
naive = datetime.now()

# UTCで取得(推奨)
utc_now = datetime.now(timezone.utc)
print(utc_now.isoformat())  # 2026-01-15T01:30:00+00:00

# JSTに変換
jst = timezone(timedelta(hours=9))
jst_now = utc_now.astimezone(jst)
print(jst_now.isoformat())  # 2026-01-15T10:30:00+09:00

データベースへの保存はUTCで

MySQLやPostgreSQLにタイムスタンプを保存するときは、UTC基準で統一するのが鉄則です。表示のタイムゾーン変換はアプリケーション側で行います。サーバーの設置場所やユーザーの地域が変わっても、UTCで保存していれば計算がずれません。

-- MySQLでUTC_TIMESTAMPを使う
INSERT INTO events (title, created_at) VALUES ('リリース', UTC_TIMESTAMP());

-- PostgreSQLではTIMESTAMPTZ(タイムゾーン付き)を使う
CREATE TABLE events (
  title TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

タイムゾーン変換の計算が面倒なときは タイムゾーン変換ツール を使うと、UIで簡単に確認できます。Unixタイムスタンプとの相互変換は Unixタイムスタンプ変換 が便利です。2つの日時の差を求めたい場合は タイムスタンプ差分計算 も活用してください。

よくある質問

Unixタイムスタンプはタイムゾーンの影響を受けますか?
受けません。Unixタイムスタンプ(エポック秒)は、1970年1月1日00:00:00 UTCからの経過秒数です。タイムゾーンに関係なく世界共通の値なので、異なる地域のシステム間で時刻を比較・保存する際にも安全に使えます。表示する際にタイムゾーンを適用して変換します。
夏時間(DST)とは何ですか?
夏時間(Daylight Saving Time / DST)は、春から秋にかけて時計を1時間進める制度です。アメリカやヨーロッパの多くの国が採用しています。例えば America/New_York は通常 UTC-5 ですが、夏時間中は UTC-4 になります。日本は夏時間を採用していないため、JSTは常に UTC+9 で固定です。
JSTとJSTD(日本標準時)は同じものですか?
基本的には同じ意味で使われます。ただし「JSTD」という表記は一般的ではなく、公式にはJST(Japan Standard Time)が正式な略称です。システムやIANAタイムゾーンデータベースでは Asia/Tokyo という識別子が使われます。JSTという略称は他の国の標準時(Jamaica Standard Timeなど)と衝突する場合があるため、コード内では Asia/Tokyo を使う方が明確です。
GMTとUTCの違いは何ですか?
GMT(グリニッジ標準時)はイギリスのグリニッジ天文台を基準とした時刻で、歴史的な概念です。UTC(協定世界時)は原子時計を基準にした現代の国際標準で、GMT後継として定められました。日常的な用途では同一視して問題ありませんが、厳密には定義が異なります。現代のシステムでは「UTC」を使うのが正確です。

まとめ

  • UTCは世界の時刻基準。データベースへの保存はUTCで統一する
  • JSTはUTC+9で固定(日本は夏時間なし)
  • ISO 8601形式でオフセットを明示することで曖昧さをなくせる
  • オフセット(+09:00)とタイムゾーン名(Asia/Tokyo)は別概念。夏時間を扱うならタイムゾーン名を使う
  • JavaScriptの new Date() はローカル時刻。toISOString() でUTCに変換できる

タイムゾーン関連の変換・計算には以下のツールを活用してください。