Unixタイムスタンプ解説:変換とよくある落とし穴
Unixタイムスタンプは、プログラミングにおいて最もシンプルでありながら最も誤解されやすい概念の一つです。Unixエポックと呼ばれる1970年1月1日 00:00:00 UTCからの経過秒数です。そのシンプルさにもかかわらず、タイムスタンプはタイムゾーン、精度、オーバーフローに関連するバグの原因になります。
Unixエポックとは?
Unixエポック — 1970年1月1日 00:00:00 UTC — はUnix時間の起点として選ばれました。すべてのタイムスタンプはこの時点を基準に測定されます:
| タイムスタンプ | 日時 (UTC) |
|---|---|
| 0 | Jan 1, 1970 00:00:00 |
| 86400 | Jan 2, 1970 00:00:00 |
| 1000000000 | Sep 9, 2001 01:46:40 |
| 1700000000 | Nov 14, 2023 22:13:20 |
| 2000000000 | May 18, 2033 03:33:20 |
負のタイムスタンプはエポック以前の日付を表します。例えば、-86400 は1969年12月31日です。
当サイトのタイムスタンプ変換ツールでタイムスタンプを即座に変換できます。
秒 vs ミリ秒
これが最も一般的な混乱の原因です。異なるシステムは異なる精度を使用します:
| システム | 精度 | 例 |
|---|---|---|
| Unix/POSIX | 秒 | 1700000000 |
| JavaScript | ミリ秒 | 1700000000000 |
| Java (System.currentTimeMillis) | ミリ秒 | 1700000000000 |
| Python (time.time) | 秒(浮動小数点) | 1700000000.123 |
| PostgreSQL (extract epoch) | 秒(浮動小数点) | 1700000000.123456 |
経験則: 数値が13桁ならミリ秒、10桁なら秒です。
// JavaScriptはミリ秒を返す
const nowMs = Date.now(); // 1700000000000
const nowSec = Math.floor(nowMs / 1000); // 1700000000
タイムゾーンの扱い
Unixタイムスタンプは常にUTCです。タイムゾーン情報は含まれていません。これは実際には利点であり、普遍的な基準点を提供します。
混乱が生じるのは、タイムスタンプを人間が読める日付に変換するときです:
const ts = 1700000000;
const date = new Date(ts * 1000);
date.toUTCString(); // "Tue, 14 Nov 2023 22:13:20 GMT"
date.toLocaleString(); // ユーザーのローカルタイムゾーンに依存
date.toISOString(); // "2023-11-14T22:13:20.000Z"
ベストプラクティス: タイムスタンプはUTCで保存・送信します。ローカル時間への変換は、ユーザーにできるだけ近い表示層でのみ行います。
2038年問題
従来のUnixシステムはタイムスタンプを32ビット符号付き整数で保存します。最大値は2,147,483,647で、これは2038年1月19日 03:14:07 UTCに相当します。
この時点以降、32ビットタイムスタンプは負の値にオーバーフローし、1901年12月13日に戻ります。これはY2K問題に類似しています。
現在の状況:
- ほとんどの最新システムは64ビットタイムスタンプを使用(約2920億年まで対応)
- Linuxカーネルはバージョン5.6(2020年)から64ビットタイムスタンプに完全対応
- 組み込みシステムやレガシーデータベースにはリスクが残る
- 2038年以降の日付を扱うソフトウェアを構築する場合は、タイムスタンプの保存方法を確認すること
各言語での変換
JavaScript
// 現在のタイムスタンプ(秒)
const now = Math.floor(Date.now() / 1000);
// タイムスタンプからDateへ
const date = new Date(1700000000 * 1000);
// Dateからタイムスタンプへ
const ts = Math.floor(new Date('2023-11-14').getTime() / 1000);
Python
import time, datetime
# 現在のタイムスタンプ
now = int(time.time())
# タイムスタンプからdatetimeへ
dt = datetime.datetime.fromtimestamp(1700000000, tz=datetime.timezone.utc)
# datetimeからタイムスタンプへ
ts = int(dt.timestamp())
SQL (PostgreSQL)
-- 現在のタイムスタンプ
SELECT EXTRACT(EPOCH FROM NOW());
-- タイムスタンプから日付へ
SELECT TO_TIMESTAMP(1700000000);
-- 日付からタイムスタンプへ
SELECT EXTRACT(EPOCH FROM '2023-11-14'::timestamp);
よくある落とし穴
1. 秒とミリ秒の混同
日付が1970年1月と表示される場合、ミリ秒が期待されているところに秒を渡した(またはその逆の)可能性があります。APIがどの精度を期待しているか常に確認しましょう。
2. 日付文字列のタイムゾーン無視
タイムゾーンなしで "2023-11-14" をパースすると、ローカルタイムゾーンで日付が作成され、サーバーの場所によって異なります。常にタイムゾーンを含めましょう:"2023-11-14T00:00:00Z"。
3. 浮動小数点の精度
タイムスタンプを浮動小数点数として保存すると、ミリ秒以上の精度が失われる可能性があります。マイクロ秒やナノ秒の精度が必要な場合は、適切な乗数を付けた整数を使用してください。
4. うるう秒
Unixタイムスタンプはうるう秒を考慮しません。Unixの1日は常に正確に86,400秒ですが、実際のUTCの日は時折86,401秒になることがあります。ほとんどのアプリケーションではこれは無関係です。科学や衛星のアプリケーションでは、代わりにTAI(国際原子時)を使用してください。
ISO 8601:人間が読める代替形式
タイムスタンプは計算に適していますが、ISO 8601は人間が読める日付表現の標準です:
2023-11-14T22:13:20Z # UTC
2023-11-14T17:13:20-05:00 # 東部時間
2023-11-14 # 日付のみ
ほとんどのAPIはISO 8601文字列を受け入れ、返すべきです。計算や保存には内部的にタイムスタンプを使用しましょう。
FAQ
Unixタイムはなぜ1970年1月1日から始まるのですか?
この日付は、1970年代初頭にベル研究所でUnixが開発されていたときに任意に選ばれました。遠い過去の日付でビットを無駄にしないため、十分に最近の日付が必要でした。32ビット整数は各方向に約68年を保存できるため、1970年を起点にすると1901年から2038年までの日付をカバーできました。
データベースにはタイムスタンプとフォーマット済み文字列のどちらで日付を保存すべきですか?
効率的なソート、比較、演算のために、タイムスタンプ(整数またはネイティブのdatetime型)で日付を保存しましょう。フォーマット済み文字列はクエリやソートが正しく行いにくくなります。ほとんどのデータベースにはこれをうまく処理するネイティブのdatetime型があります。文字列フォーマットは表示とAPIレスポンス用に限定しましょう。
関連リソース
- タイムスタンプ変換ツール — Unixタイムスタンプと人間が読める日付を相互変換
- JSONフォーマットのベストプラクティス — JSONレスポンスでの日付の扱い方
- UUIDガイド — 時間ベースのバリアントを持つ別の一般的な識別子形式