Unixタイムスタンプ完全ガイド:秒/ミリ秒/マイクロ秒の変換とタイムゾーンのベストプラクティス
Unixタイムスタンプは「Unixエポック(1970年1月1日 00:00:00 UTC)からの経過時間」を表します。世界中のWebサーバーの95%以上、データベースシステムの90%以上が内部的にこの時間表現を使用しています。本ガイドでは、精度の違い、プログラミング言語ごとの実装、タイムゾーン処理、夏時間(DST)の注意点を解説します。
Unixタイムスタンプの起源と定義
Unixエポックは1970年1月1日 00:00:00 UTCに始まります。Unixタイムスタンプ 0 はこの瞬間を表し、1262304000 は 2010-01-01 00:00:00 UTC を表します。うるう秒の影響はデフォルトで無視されます。
当初は32ビット符号付き整数で格納されていたため、2038年問題が生じました。最大値が2038年1月19日 03:14:07 UTCにオーバーフローするのです。現代のシステムでは64ビット整数を使用しており、表現可能な時間範囲が大幅に拡張されています。
時間精度の比較
| 単位 | 1秒あたりのカウント | 桁数 | 代表的な用途 |
|---|---|---|---|
| 秒 (s) | 1 | 10桁 | 従来のUnix/Linuxシステム |
| ミリ秒 (ms) | 1,000 | 13桁 | JavaScript、Java、ログシステム |
| マイクロ秒 (μs) | 1,000,000 | 16桁 | 分散トレーシング、データベース |
| ナノ秒 (ns) | 1,000,000,000 | 19桁 | Go言語、パフォーマンス分析 |
実用的な判別法: 10桁は秒、13桁はミリ秒、16桁はマイクロ秒、19桁はナノ秒と覚えておくと便利です。
JavaScriptのタイムスタンプ
JavaScriptはネイティブでミリ秒を使用します。Date() コンストラクタは入力値をミリ秒として扱うため、秒単位のタイムスタンプを渡す場合は1000を掛ける必要があります。
// 現在のUnixタイムスタンプを取得(ミリ秒)
const timestampMs = Date.now();
console.log(timestampMs); // 出力例: 1692268800123
// 秒単位のタイムスタンプを取得(ミリ秒を1000で割って切り捨て)
const timestampSec = Math.floor(Date.now() / 1000);
console.log(timestampSec); // 出力例: 1692268800
// UnixタイムスタンプをDateオブジェクトに変換
let ts = 1692268800;
let date = new Date(ts * 1000);
console.log(date.toISOString()); // "2023-08-17T16:00:00.000Z"
Pythonのタイムスタンプ
Pythonの time.time() は秒単位のUnixタイムスタンプを浮動小数点数で返します。
import time
from datetime import datetime, timezone
# 現在のUnixタイムスタンプを取得(秒、浮動小数点数)
now_sec = time.time()
print(now_sec) # 出力例: 1692268800.123456
# ミリ秒を取得(整数)
now_millis = int(time.time() * 1000)
print(now_millis) # 例: 1692268800123
# ナノ秒を取得(Python 3.7以降)
now_nanos = time.time_ns()
print(now_nanos) # 例: 1692268800123456789
# datetimeに変換
ts = 1692268800
dt_local = datetime.fromtimestamp(ts)
dt_utc = datetime.fromtimestamp(ts, timezone.utc)
print(dt_local.strftime("%Y-%m-%d %H:%M:%S")) # ローカル時刻
print(dt_utc.strftime("%Y-%m-%d %H:%M:%S")) # UTC時刻
Go言語のタイムスタンプ
Goの time パッケージは複数の精度レベルを提供します。
package main
import (
"fmt"
"time"
)
func main() {
// 現在のUnixタイムスタンプを取得
sec := time.Now().Unix() // 秒
msec := time.Now().UnixMilli() // ミリ秒(Go 1.17以降)
nsec := time.Now().UnixNano() // ナノ秒
fmt.Println(sec) // 例: 1692268800
fmt.Println(msec) // 例: 1692268800123
fmt.Println(nsec) // 例: 1692268800123456789
// タイムスタンプをTimeオブジェクトに変換
t := time.Unix(sec, 0)
fmt.Println(t.UTC())
fmt.Println(t)
}
よくあるミスとベストプラクティス
ミスの例:単位が不明確
// ✗ 誤り:タイムスタンプの単位が不明確
const timestamp = 1692268800;
const date = new Date(timestamp); // ミリ秒として扱われ、1970年が表示される
// ✓ 正しい:単位を明示する
const timestampSec = 1692268800;
const timestampMs = 1692268800000;
const dateFromSec = new Date(timestampSec * 1000);
フィールド命名のベストプラクティス
log_entry = {
"timestamp_ms": 1692268800123, # ミリ秒精度
"timestamp_iso": "2023-08-17T16:00:00Z", # ISO 8601 UTC
"event_type": "user_login",
"user_id": 12345
}
タイムゾーン処理の落とし穴
タイムゾーンを扱う際の主な4つの落とし穴があります。
- ローカル時刻とUTC時刻の混同 ― Unixタイムスタンプは常にUTC基準です。ローカル時刻への変換は表示時のみに行いましょう
- タイムゾーン情報の保存漏れ ― 曖昧さを生みます。常にUTCで保存するか、オフセットを含めましょう
- システム間のタイムゾーンの不一致 ― すべてのシステムで同じタイムゾーン基準(UTC推奨)を使用しましょう
- DSTオフセットの手動計算 ― 組み込みライブラリに任せ、ハードコードは避けましょう
推奨アプローチ:「保存は標準化、表示はローカライズ」。保存・送信フェーズではUTCまたはUnixタイムスタンプを使用して一貫性を確保し、表示時にユーザーのタイムゾーンに合わせてフォーマットします。日本のシステムでは、JSTへの変換は表示レイヤーでのみ行うのがベストです。
夏時間(DST)の問題
夏時間には2つの問題となる期間があります。
- スキップされる時間:夏時間の開始時に時計が進みます。たとえば、02:00が直接03:00に飛び、02:30という時刻は存在しなくなります。
- 重複する時間:夏時間の終了時に01:00〜01:59が2回出現し、曖昧さが生じます。
Unixタイムスタンプ自体は連続しておりDSTの影響を受けませんが、ローカル時刻への変換には注意が必要です。なお、日本(JST)は現在夏時間を採用していませんが、海外のサーバーやユーザーを扱う場合はDSTへの対応が不可欠です。
ログ・データベース・APIのベストプラクティス
ログシステム
UTCタイムゾーン + ISO 8601形式に統一しましょう。必要に応じてミリ秒やマイクロ秒精度のタイムスタンプを含めます。
データベース
タイムゾーン付きのネイティブな日時型を優先的に使用しましょう。数値タイムスタンプにはBIGINTを使い、フィールド名で単位を明示します(例:*_epoch_ms)。
API
フォーマットと単位を明確に指定しましょう。外部向けインターフェースにはISO 8601(可読性が高く、タイムゾーン情報を含む)を使用し、内部システムでは数値タイムスタンプを使用できますが、ドキュメントで単位を明記してください。
よくあるエラーのパターン
- 13桁のパース失敗:ミリ秒タイムスタンプを秒単位を期待する関数に渡すとオーバーフローが発生します。まず桁数で単位を判断しましょう。
- フォーマットの不一致:無効な日付、タイムゾーンの欠落、DST切り替えタイミングがパースエラーの原因になります。
- タイムゾーンのずれ:整数時間単位のずれ(例:JSTなら+9時間)は、タイムゾーンの統一漏れを示していることが多いです。内部的にはUTCの使用を推奨します。
- 数値オーバーフロー:32ビットシステムでは2038年の制限があります。64ビット整数を優先的に使用しましょう。
よくある質問
なぜ1970年1月1日なのか?
Unixオペレーティングシステムの開発が1970年に始まり、切りの良い10年刻みの年だったため、記憶しやすく計算も簡単でした。32ビット整数で1970年から2038年までの日付を表現でき、当時としては十分でした。
秒とミリ秒を見分けるには?
3つの方法があります。桁数で判断する(10桁=秒、13桁=ミリ秒、16桁=マイクロ秒、19桁=ナノ秒)、パースして結果が妥当かどうか検証する、またはオンライン変換ツールを使用する方法です。
2038年問題はまだ影響があるのか?
64ビット整数を使用する現代のシステムでは、この問題はほぼ解消されています。主要なプログラミング言語はすでに64ビットタイムスタンプをサポートしています。ただし、レガシーな32ビットシステムや組み込みデバイスでは引き続き注意が必要です。
なぜ夏時間が問題を引き起こすのか?
スキップされる時間帯による不連続性と、重複する時間帯による曖昧さがパースの問題を生みます。システムによって曖昧な時刻の処理方法が異なります。解決策:内部的にUTCを使用し、表示時にのみローカル時刻に変換しましょう。
JavaScriptとPythonでタイムスタンプが違うのはなぜ?
JavaScriptの Date.now() はミリ秒(13桁)を返し、Pythonの time.time() は秒(マイクロ秒精度の浮動小数点数)を返します。変換するには1000で割るか掛ける必要があります。
最新トレンドと今後の展望
- 高精度化:金融取引、IoT、リアルタイムシステムではナノ秒レベルの精度が求められています
- 分散同期:NTPやPTPプロトコルにより同期精度が向上しています
- ブロックチェーンのタイムスタンプ:暗号通貨では改ざん耐性のある高精度タイムスタンプが必要です
- パフォーマンス最適化:SIMD命令、メモリキャッシュ、並列処理による変換の高速化が進んでいます
まとめ
本ガイドでは、Unixタイムスタンプの基本を体系的に解説しました。エポックの起源、複数精度の変換、各言語での実装、タイムゾーン処理、DSTの落とし穴、そしてログ・データベース・API設計でのベストプラクティスまでをカバーしています。最も重要なポイント:保存にはUTC、表示にはローカル時刻を使い、タイムスタンプの単位は常に明示することです。