Guide
タイムスタンプと日付時刻: 完全ガイド
はじめに
ソフトウェア開発とデータベース管理では、timestamps と DateTime のどちらのタイプを選択するかは、ストレージ効率、クエリ パフォーマンス、アプリケーションの動作に影響を与える重要な決定です。このガイドは、情報に基づいた選択を支援するための包括的な比較を提供します。
違いを理解する
タイムスタンプとは何ですか?
タイムスタンプは、特定のエポックからの秒数、ミリ秒数、ナノ秒数をカウントする時間を数値で表現したものです。最も一般的なエポックは Unix エポック (1970 年 1 月 1 日、00:00:00 UTC) です。
日時とは何ですか?
DateTime は、日付と時刻のコンポーネントを個別に格納する構造化データ型です。
- 年 (例: 2025)
- 月 (1-12)
- 日 (1-31)
- 時間 (0 ~ 23)
- 分 (0-59)
- 2 番目 (0-59)
- 多くの場合、タイムゾーン情報が含まれます
| 特集 | タイムスタンプ | 日時 |
|---|---|---|
| データ型 | 数値 (整数/浮動小数点) | 個別のコンポーネントを持つ構造化オブジェクト |
| ストレージサイズ | 4 ~ 8 バイト (精度による) | 変数 (通常は 16 ~ 24 バイト以上) |
| 可読性 | 低 (変換が必要) | 高 (人間が判読できる) |
| インデックス作成 | 優れた (数値比較) | 悪い (文字列解析が必要) |
| タイムゾーンのサポート | 暗黙的 (通常は UTC) | 内蔵(タイムゾーンオフセットを保存可能) |
| 並べ替え | 高速 (数値比較) | 遅い (日時解析が必要) |
| 算数 | 高速 (直接計算) | 遅い (日時変換が必要) |
ストレージの比較
MySQL
| 側面 | タイムスタンプ | 日時 |
|---|---|---|
| 範囲 | 1970-01-01 00:00:00 から 2038-01-19 03:14:07 | 1000-01-01 00:00:00 ~ 9999-12-31 23:59:59 |
| ストレージ | 4バイト | 8バイト |
| タイムゾーン | タイムゾーンはサポートされていません | タイムゾーンを個別に保存 |
| 使用例 | イベントログ、ポイントインタイム追跡 | カレンダーの日付、営業時間の保存 |
重要: MySQL TIMESTAMP は 2038 年問題の影響を受けます。 2038 を超える日付には DATETIME を使用するか、BIGINT タイムスタンプへのアップグレードを検討してください。
PostgreSQL
| 側面 | タイムスタンプ | タイムスタンプ |
|---|---|---|
| 範囲 | 1970-01-01 00:00:00 から 294276-12-31 23:59:59 | 紀元前 4713 年から西暦 294276 年 |
| ストレージ | 8バイト | 8バイト |
| タイムゾーン | タイムゾーンはサポートされていません | タイムゾーンを個別に保存 |
| 小数秒 | いいえ | はい (マイクロ秒の精度) |
| 使用例 | イベントログ、システムイベント | 正確な業務時間を保存する |
SQL サーバー
| 側面 | 日時 | 日時 2 |
|---|---|---|
| 精度 | 0.003 秒単位 | 0.0000033 秒まで |
| 範囲 | 1753-01-01 00:00:00 ~ 9999-12-31 23:59:59.997 | 0001-01-01 00:00:00 ~ 9999-12-31 23:59:59.997 |
| ストレージ | 8バイト | 8バイト |
| 文字の長さ | 23 文字 (YYYY-MM-DD HH:MM:SS) | 27 文字 (YYYY-MM-DD HH:MM:SS.nnnnnnn) |
パフォーマンスの比較
ストレージ効率
タイムスタンプは DateTime 文字列よりも 3 ~ 6 倍のストレージ効率があります。これは大容量テーブルにとって重要であり、データベース サイズを大幅に削減できます。
クエリのパフォーマンス|操作 |タイムスタンプ |日時 |
| --- | --- | --- |
|等価性チェック | O(1) - 単一の数値比較 | O(n) - 文字列の解析と比較が必要 |
|範囲クエリ (WHERE 列 >= X AND 列 <= Y) | O(1) - 数値範囲 | O(n) - 各行の日時解析が必要 |
|並べ替え (ORDER BY) | O(n log n) - 数値ソート | O(n²) - 行ごとの文字列比較 |
|インデックス作成 |優れた - コンパクトな数値 |悪い - インデックス付けする文字列が大きい |
|グループ化/集約 |高速 - 数値演算 |遅い - 日時の抽出と変換が必要です。
重要な発見: タイムスタンプにより、文字列パターン マッチングを除くすべての操作のクエリ パフォーマンスが大幅に向上します。 DateTime では、すべてのクエリで解析オーバーヘッドが必要です。
ベストプラクティス
タイムスタンプを使用する場合
次の場合にタイムスタンプを使用します。
-
イベントログと時系列
- アプリケーションログ、センサーデータ、金融取引
- 正確な時系列順が必要
- 利点: 高速ソート、コンパクトな保管、簡単な範囲クエリ
-
ポイントインタイム追跡
- 作成/変更時刻を記録する
- 解決までの時間のメトリクスを計算する
- 利点: 期間の計算のための簡単な算術
-
システム イベントとスケジュール
- Cron ジョブ、タスクキュー、プロセス監視
- 利点: スケジュール ロジックの数値比較
-
大容量の時間データ
- IoT センサーの測定値、パフォーマンス指標
- 利点: ストレージ効率、クエリパフォーマンス
-
API レスポンスと有効期限
- トークンの有効期限、キャッシュTTL
- 利点: シンプルな数値比較、最小限のストレージ
-
キャッシュとセッション管理
- キャッシュキー、セッションの有効期限
- 利点: 高速な無効化、シンプルな TTL 演算
DateTime を使用する場合
次の場合に DateTime を使用します。
-
人間が判読できるディスプレイ
- ユーザーに日付を表示する UI
- カレンダービューとスケジューラー
- 利点: 変換不要、ユーザーフレンドリーな形式
-
営業日のロジック
- 営業日、休日、会計期間
- 利点: 組み込みの日付演算、タイムゾーン処理
-
複数コンポーネントの日付表現
- 日付と時刻のコンポーネントを個別に分離
- 利点: データベース固有の最適化、明確さ
-
複雑な日付の計算
- 定期的なスケジュール、記念日
- 利点: ネイティブ日付ライブラリは特殊なケースに対応します
-
タイムゾーンに依存するストレージ
- 現地の営業時間、地域のイベント
- 利点: 適切なタイムゾーンの保持
-
カレンダーの統合
- UI カレンダー、スケジュール システム
- 利点: カレンダーの日付への直接マッピング
ハイブリッドアプローチ
タイムスタンプを保存し、人間が判読できるようにインデックスを使用します
一部のシステムではハイブリッド アプローチが使用されます。
- 効率化のために タイムスタンプ をデータベースに保存します
- 必要に応じて、計算されたインデックスまたはデータベース関数を使用して人間が読める形式にフォーマットします
-- MySQL: Using computed column for human-readable format
CREATE TABLE events (
id BIGINT PRIMARY KEY,
event_timestamp BIGINT NOT NULL,
event_date DATETIME AS (FROM_UNIXTIME(event_timestamp)),
INDEX idx_timestamp (event_timestamp),
INDEX idx_date (event_date)
);
DateTime を使用し、変換されたタイムスタンプをキャッシュする
効率的なストレージと高速な表示の両方を必要とするアプリケーションの場合:
- 読みやすくするために DateTime を保存します
- クエリの 変換されたタイムスタンプ をキャッシュします
- 異なるクエリパターンに対して個別のインデックスを使用する
// Application logic: cache both representations
const cache = new Map();
function getEvent(id) {
if (cache.has(id)) {
return cache.get(id);
}
const event = db.query('SELECT * FROM events WHERE id = ?', [id]);
// Cache both forms
cache.set(id, {
timestamp: event.event_timestamp,
date: event.event_date,
});
return event;
}
実装例
MySQL のベスト プラクティス
MySQL の実装
-- Recommendation: Use DATETIME for display, TIMESTAMP for range queries
-- Bad: Storing both (redundant)
CREATE TABLE orders_bad (
id INT PRIMARY KEY,
order_timestamp TIMESTAMP,
order_date DATETIME,
order_amount DECIMAL(10,2)
);
-- Good: Use TIMESTAMP for queries, generate DATETIME on demand
CREATE TABLE orders_good (
id INT PRIMARY KEY,
order_timestamp TIMESTAMP NOT NULL,
order_amount DECIMAL(10,2)
);
-- Query: Range by timestamp (fast)
SELECT id, order_amount
FROM orders_good
WHERE order_timestamp >= UNIX_TIMESTAMP('2025-01-01 00:00:00')
AND order_timestamp <= UNIX_TIMESTAMP('2025-01-31 23:59:59');
-- Application: Format on demand for display
SELECT id,
order_amount,
DATE_FORMAT(order_timestamp, '%Y-%m-%d %H:%i') AS readable_date
FROM orders_good;
PostgreSQL のベスト プラクティス
PostgreSQL の実装
-- Use TIMESTAMPTZ for timezone-aware timestamps
CREATE TABLE events (
id BIGSERIAL PRIMARY KEY,
event_time TIMESTAMPTZ NOT NULL DEFAULT NOW(),
event_date TIMESTAMP WITH TIME ZONE 'UTC' AS (event_time AT TIME ZONE 'UTC'),
INDEX idx_event_time (event_time)
);
-- Query by time range (efficient)
SELECT id, event_time
FROM events
WHERE event_time >= '2025-01-01 00:00:00 UTC'::timestamptz
AND event_time < '2025-01-31 23:59:59 UTC'::timestamptz;
アプリケーションのベスト プラクティス
JavaScript/Node.js
// Use timestamps for storage, format for display
const event = {
timestamp: Date.now(), // Unix timestamp in milliseconds
created_at: new Date().toISOString(), // ISO 8601 for storage
};
// Database query using timestamp (fast)
const events = db.query(`
SELECT * FROM events
WHERE event_timestamp >= ?
ORDER BY event_timestamp
`, [event.timestamp]);
// Format timestamp for display
const formatDate = (timestamp) => {
return new Date(timestamp).toLocaleString();
};
// Cache converted DateTime to avoid repeated conversions
const dateCache = new Map();
function getEventDate(eventId) {
if (dateCache.has(eventId)) {
return dateCache.get(eventId);
}
const { event_timestamp, created_at, updated_at } = await db.query(
'SELECT event_timestamp, created_at, updated_at FROM events WHERE id = ?',
[eventId]
);
// Cache formatted date (expensive conversion)
dateCache.set(eventId, {
formatted: formatDate(event_timestamp),
created: formatDate(created_at),
updated: formatDate(updated_at),
});
return dateCache.get(eventId);
}
パイソン
# Use timestamp for storage, datetime for display
from datetime import datetime
def create_event():
return {
'timestamp': int(datetime.now().timestamp()), # Unix timestamp
'created_at': datetime.now().isoformat() # ISO 8601 for display
}
def query_events_by_range(start_ts, end_ts):
# Fast query using timestamp comparison
start_dt = datetime.fromtimestamp(start_ts)
end_dt = datetime.fromtimestamp(end_ts)
# Query database
events = Event.objects.filter(
timestamp__gte=start_dt,
timestamp__lte=end_dt
)
return events
# Cache formatted dates to avoid repeated conversions
from functools import lru_cache
@lru_cache(maxsize=1000)
def get_formatted_date(timestamp):
# Expensive conversion (cached)
return datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d %H:%M')
アンチパターン
避けるべきよくある間違い:
- タイムスタンプと日付時刻の両方を保存する
-- DON'T: This doubles storage and creates redundancy
CREATE TABLE bad (
id INT,
created TIMESTAMP,
created_date DATETIME -- REDUNDANT
);
```2. **範囲クエリに DateTime を使用する**
```sql
-- DON'T: This requires string parsing for every row
SELECT * FROM events
WHERE created_date >= '2025-01-01'
AND created_date <= '2025-01-31';
```3. **タイムスタンプを VARCHAR として保存**
```sql
-- DON'T: Loses numeric benefits and string operations
CREATE TABLE bad (
id INT,
timestamp_str VARCHAR(255) -- USE BIGINT
);
```4. **タイムスタンプ列にインデックスを使用しない**
```sql
-- DON'T: Full table scans on large tables
SELECT * FROM orders
WHERE created_timestamp > UNIX_TIMESTAMP('2025-01-01');
```5. **クエリごとにタイムスタンプを DateTime に変換**
```javascript
-- DON'T: Unnecessary CPU overhead
events.forEach(event => {
const date = new Date(event.timestamp * 1000);
// Only convert for display, don't use in queries
});
移行戦略
DateTime からタイムスタンプまで
既存の DateTime 列をタイムスタンプに移行する必要がある場合:
-- MySQL: Add new timestamp column with default value
ALTER TABLE orders ADD COLUMN order_timestamp BIGINT
DEFAULT (UNIX_TIMESTAMP(created_at));
-- Backfill existing data
UPDATE orders
SET order_timestamp = UNIX_TIMESTAMP(created_at)
WHERE order_timestamp IS NULL;
-- After verification, you can drop the old column
-- ALTER TABLE orders DROP COLUMN created_at;
タイムスタンプから日時まで
-- Use generated column for human-readable dates
SELECT
id,
order_timestamp,
DATE_FORMAT(order_timestamp, '%Y-%m-%d %H:%i') AS readable_date
FROM orders
WHERE order_timestamp >= UNIX_TIMESTAMP('2025-01-01 00:00:00');
意思決定の枠組み
正しい選択をするには、このチェックリストを使用してください。
| 質問 | はい → タイムスタンプ | はい → 日付時刻 |
|---|---|---|
| 時間範囲のクエリを実行する必要がありますか? | ✓ | |
| 時系列に並べる必要がありますか? | ✓ | |
| 保管スペースが心配ですか? | ✓ | |
| 高速な等価/範囲クエリが必要ですか? | ✓ | |
| タイムゾーンのサポートが必要ですか? | ✓ | |
| 人間が判読できるディスプレイが必要ですか? | ✓ | |
| これはイベントログ用ですか、それとも時系列用ですか? | ✓ | |
| これはカレンダー/営業日用ですか? | ✓ |
推奨事項: 多くのアプリケーションは、クエリのタイムスタンプを保存し、表示に計算列やアプリケーション レベルの書式設定を使用する、ハイブリッド アプローチの恩恵を受けます。
結論
タイムスタンプと DateTime のどちらを選択するかは、画一的な決定ではありません。次のことを考慮してください。
- クエリ パターン (データへのアクセス方法)
- パフォーマンス要件 (データ量、クエリの複雑さ)
- ストレージ制約 (データベース サイズ、メモリ使用量)
- ユーザー エクスペリエンスのニーズ (読みやすさ、ローカリゼーション)
重要なポイント: タイムスタンプはデータ操作に優れたパフォーマンスを提供し、DateTime はより優れたユーザー エクスペリエンスを提供します。最良のソリューションでは、多くの場合、内部でタイムスタンプを使用し、必要に応じて表示用にフォーマットします。
関連ツール
- タイムスタンプ形式コンバーター - 複数の形式間の変換
- 現在のタイムスタンプ - 現在のタイムスタンプをさまざまな形式で取得します
- Unix タイムスタンプ コンバータ - Unix と DateTime 間の変換
- タイムスタンプバリデーター - タイムスタンプの形式と範囲を検証します。