【完全ガイド】Oracle DATE vs TIMESTAMP 違い|精度・タイムゾーン・INTERVAL 徹底解説
- 作成日 2026.07.27
- Oracle Database
Oracle 開発者が必ず一度は悩む問い:
CREATE TABLE events (
event_id NUMBER PRIMARY KEY,
event_time DATE, -- ← これ?
event_ts TIMESTAMP -- ← それとも?
);
結論から言うと、両者は目的が違います:
- DATE: 秒精度、7バイト、タイムゾーンなし
- TIMESTAMP: ナノ秒精度、11-13バイト、タイムゾーン対応
しかし、実際は5種類のバリエーションがあり、選択が複雑:
- DATE: 秒まで
- TIMESTAMP: ナノ秒まで、TZ なし
- TIMESTAMP(n): 精度指定
- TIMESTAMP WITH TIME ZONE: TZ 含む
- TIMESTAMP WITH LOCAL TIME ZONE: DB TZ に正規化
さらに、多くの Oracle 開発者が誤解している事実:
DATEは日付のみではない(時刻も持つ)SYSDATEとSYSTIMESTAMPの返り値が違うCURRENT_DATEはサーバーの日付ではない(セッションの日付)TIMESTAMP WITH TIME ZONEは保存時に TZ を保持TIMESTAMP WITH LOCAL TIME ZONEは保存時に UTC 変換- INTERVAL 型の存在を知らない
- NLS_DATE_FORMAT がセッション毎
実務では:
- 監査ログ: TIMESTAMP(順序保証)
- 金融取引: TIMESTAMP WITH TIME ZONE
- 国際サービス: TIMESTAMP WITH TIME ZONE
- 業務日: DATE で十分
- メール送信時刻: TIMESTAMP
- DB マイグレーション: 型変換の考慮
さらに、日付演算・タイムゾーン計算・NLS 設定・INTERVAL 型・現在時刻関数の使い分けなど、日本語で体系的な解説がほぼないのが現状です。
本記事では、Oracle DATE vs TIMESTAMP の完全ガイドを、リファレンスとして実用的に整理します。5種類の日付型、内部バイト構造、精度と範囲、7つの現在時刻関数、タイムゾーン計算、INTERVAL 型、日付演算、変換関数、NLS 設定、パフォーマンス、他 DB との比較、Rails/Java/Python 対応、実践シナリオ、FAQまで完全網羅。この1本で Oracle 日付型を根本から理解・選択できるようになります。
- 1. 結論:3つの型を覚える
- 2. まず理解する:DATE 型の詳細
- 3. TIMESTAMP 型の詳細
- 4. TIMESTAMP WITH TIME ZONE の詳細
- 5. TIMESTAMP WITH LOCAL TIME ZONE
- 6. 7つの現在時刻関数
- 7. 日付演算
- 8. INTERVAL 型
- 9. 変換関数
- 10. NLS 設定
- 11. パフォーマンス
- 12. 他 DB との比較
- 13. Rails / Java / Python 対応
- 14. 実践シナリオ
- 15. 予防のベストプラクティス
- 16. トラブルシューティング
- 17. よくある質問(FAQ)
- 17.1. Q1. DATE と TIMESTAMP どちらを使うべき?
- 17.2. Q2. DATE は本当に日付だけ?
- 17.3. Q3. SYSDATE と SYSTIMESTAMP の違い
- 17.4. Q4. CURRENT_DATE と SYSDATE の違い
- 17.5. Q5. TIMESTAMP WITH TIME ZONE と LOCAL TIME ZONE の違い
- 17.6. Q6. INTERVAL 型はいつ使う?
- 17.7. Q7. NLS_DATE_FORMAT の変更
- 17.8. Q8. Rails での使い分け
- 17.9. Q9. PostgreSQL からの移行
- 17.10. Q10. 日付範囲検索の最適化
- 17.11. Q11. UTC 統一のメリット
- 17.12. Q12. Autonomous DB のデフォルト
- 18. 参考リンク
- 19. まとめ
結論:3つの型を覚える
時間がない方向けに、最速の理解を示します。
3つの主要型
| 型 | 精度 | サイズ | TZ | 用途 |
|---|---|---|---|---|
| DATE | 秒 | 7バイト | なし | 業務日、日付管理 |
| TIMESTAMP | ナノ秒 | 11バイト | なし | 高精度ログ、順序保証 |
| TIMESTAMP WITH TIME ZONE | ナノ秒 | 13バイト | あり | 国際サービス、金融 |
DATE の実態
日付だけではない!時刻も持つ:
CREATE TABLE t (d DATE);
INSERT INTO t VALUES (SYSDATE); -- 2026-06-15 14:30:45
DATE = 年月日時分秒(秒まで)。
5種類の全型
| 型 | 精度 | サイズ | TZ |
|---|---|---|---|
| DATE | 秒 | 7 | なし |
| TIMESTAMP | ナノ秒 | 11 | なし |
| TIMESTAMP(n) | n桁 | 7-11 | なし |
| TIMESTAMP WITH TIME ZONE | ナノ秒 | 13 | 明示 |
| TIMESTAMP WITH LOCAL TIME ZONE | ナノ秒 | 11 | 暗黙UTC |
7つの現在時刻関数
| 関数 | 返り値 | 参照時刻 |
|---|---|---|
| SYSDATE | DATE | サーバー |
| SYSTIMESTAMP | TIMESTAMP WITH TZ | サーバー |
| CURRENT_DATE | DATE | セッション |
| CURRENT_TIMESTAMP | TIMESTAMP WITH TZ | セッション |
| LOCALTIMESTAMP | TIMESTAMP | セッション |
| DBTIMEZONE | VARCHAR2 | DB の TZ |
| SESSIONTIMEZONE | VARCHAR2 | セッション TZ |
選択ガイド
日付だけ(時刻不要) → DATE
秒精度で十分 → DATE
ミリ秒〜必要 → TIMESTAMP
国際サービス → TIMESTAMP WITH TIME ZONE
DB TZ 統一で OK → TIMESTAMP WITH LOCAL TIME ZONE
期間 → INTERVAL
詳細は以下で解説します。
まず理解する:DATE 型の詳細
内部構造(7バイト)
バイト 1: 世紀(+100)
バイト 2: 年(+100)
バイト 3: 月
バイト 4: 日
バイト 5: 時(+1)
バイト 6: 分(+1)
バイト 7: 秒(+1)
例: 2026-06-15 14:30:45
バイト: 120, 126, 6, 15, 15, 31, 46
精度と範囲
範囲: 4712 BC 〜 9999 AD
精度: 1秒
サイズ: 7バイト(固定長)
DATE リテラル
-- ANSI 標準リテラル
SELECT DATE '2026-06-15' FROM DUAL;
-- 時刻は 00:00:00
-- TO_DATE 関数
SELECT TO_DATE('2026-06-15 14:30:45', 'YYYY-MM-DD HH24:MI:SS') FROM DUAL;
DATE の落とし穴
時刻を含む:
SELECT * FROM orders WHERE order_date = DATE '2026-06-15';
-- 2026-06-15 の 00:00:00 のみ!
-- 14:30 の注文は含まれない
-- 正しくは
SELECT * FROM orders
WHERE order_date >= DATE '2026-06-15'
AND order_date < DATE '2026-06-16';
-- または
SELECT * FROM orders
WHERE TRUNC(order_date) = DATE '2026-06-15';
-- ただしインデックス使えなくなる
TRUNC の問題は Oracle EXPLAIN PLAN や関数索引で解決可能。
TIMESTAMP 型の詳細
3種類のバリエーション
-- ① TIMESTAMP(TZ なし)
TIMESTAMP
TIMESTAMP(6) -- 精度 6 (デフォルト、マイクロ秒)
TIMESTAMP(9) -- 精度 9 (ナノ秒)
-- ② TIMESTAMP WITH TIME ZONE
TIMESTAMP WITH TIME ZONE
TIMESTAMP(6) WITH TIME ZONE
-- ③ TIMESTAMP WITH LOCAL TIME ZONE
TIMESTAMP WITH LOCAL TIME ZONE
TIMESTAMP(6) WITH LOCAL TIME ZONE
内部構造
TIMESTAMP(11バイト):
バイト 1-7: DATE と同じ(世紀・年・月・日・時・分・秒)
バイト 8-11: fractional seconds(ナノ秒対応)
TIMESTAMP WITH TIME ZONE(13バイト):
バイト 1-11: TIMESTAMP と同じ
バイト 12-13: タイムゾーンオフセット
TIMESTAMP WITH LOCAL TIME ZONE(11バイト):
TIMESTAMP と同じサイズ
値は UTC で保存、表示時に session TZ に変換
fractional_seconds_precision
0〜9 桁指定可能:
TIMESTAMP(0) -- 秒まで(DATE と同等の精度)
TIMESTAMP(3) -- ミリ秒
TIMESTAMP(6) -- マイクロ秒(デフォルト)
TIMESTAMP(9) -- ナノ秒(最高精度)
TIMESTAMP リテラル
-- ANSI 標準
SELECT TIMESTAMP '2026-06-15 14:30:45.123456' FROM DUAL;
-- TZ 付き
SELECT TIMESTAMP '2026-06-15 14:30:45 +09:00' FROM DUAL;
-- TZ 地域名
SELECT TIMESTAMP '2026-06-15 14:30:45 Asia/Tokyo' FROM DUAL;
TIMESTAMP WITH TIME ZONE の詳細
特徴
TZ 情報を保存:
CREATE TABLE international_events (
event_id NUMBER,
event_at TIMESTAMP WITH TIME ZONE
);
INSERT INTO international_events VALUES (
1, TIMESTAMP '2026-06-15 09:00:00 America/New_York'
);
INSERT INTO international_events VALUES (
2, TIMESTAMP '2026-06-15 22:00:00 Asia/Tokyo'
);
SELECT event_id, event_at FROM international_events;
-- 1: 2026-06-15 09:00:00 -04:00
-- 2: 2026-06-15 22:00:00 +09:00
両方とも UTC で同じ瞬間(09:00 EDT = 13:00 UTC = 22:00 JST)。
比較・演算
-- TZ を考慮して比較(UTC 換算)
SELECT * FROM international_events
WHERE event_at = TIMESTAMP '2026-06-15 13:00:00 UTC';
-- 上記2件とも該当(同じ UTC 瞬間)
タイムゾーン変換
-- AT TIME ZONE で TZ 変換
SELECT event_at,
event_at AT TIME ZONE 'Asia/Tokyo' AS tokyo_time,
event_at AT TIME ZONE 'UTC' AS utc_time
FROM international_events;
タイムゾーン名 vs オフセット
-- 地域名(DST 対応)
TIMESTAMP '2026-07-15 09:00:00 America/New_York'
-- サマータイム時: -04:00
-- 冬時間: -05:00
-- 固定オフセット
TIMESTAMP '2026-07-15 09:00:00 -05:00'
-- 常に -05:00
DST(サマータイム) を考慮するなら地域名。
TIMESTAMP WITH LOCAL TIME ZONE
動作
保存時: セッションTZ → DB TZ に変換して保存 取得時: DB TZ → セッションTZ に変換して表示
-- セッションTZ = 'Asia/Tokyo'
CREATE TABLE t (ts TIMESTAMP WITH LOCAL TIME ZONE);
INSERT INTO t VALUES (TIMESTAMP '2026-06-15 14:00:00');
-- DB内部(UTC): 2026-06-15 05:00:00
-- 別セッションが 'America/New_York'
SELECT ts FROM t;
-- 2026-06-15 01:00:00 (JST 14:00 の東部時間)
用途
- 国際チーム全員が自分の TZ で見たい
- DB は UTC 統一したい
- DBTIMEZONE = ‘UTC’ 必須
制限
- DBTIMEZONE を後から変更不可(データがある場合)
- 保存サイズ 11バイト(TZ なし版と同じ)
7つの現在時刻関数
SYSDATE
サーバーの DATE:
SELECT SYSDATE FROM DUAL;
-- 2026-06-15 14:30:45 (秒まで)
- 型: DATE
- TZ: サーバーの OS TZ
SYSTIMESTAMP
サーバーの TIMESTAMP WITH TIME ZONE:
SELECT SYSTIMESTAMP FROM DUAL;
-- 2026-06-15 14:30:45.123456 +09:00
- 型: TIMESTAMP(6) WITH TIME ZONE
- TZ: サーバー OS TZ
CURRENT_DATE
セッションのDATE:
SELECT CURRENT_DATE FROM DUAL;
-- セッションTZ での現在日時
- 型: DATE
- TZ: SESSIONTIMEZONE
CURRENT_TIMESTAMP
セッションの TIMESTAMP WITH TIME ZONE:
SELECT CURRENT_TIMESTAMP FROM DUAL;
- 型: TIMESTAMP WITH TIME ZONE
- TZ: SESSIONTIMEZONE
LOCALTIMESTAMP
セッションのTIMESTAMP(TZ なし):
SELECT LOCALTIMESTAMP FROM DUAL;
- 型: TIMESTAMP(TZなし)
- 値: セッションTZ の時刻
DBTIMEZONE
DB の TZ:
SELECT DBTIMEZONE FROM DUAL;
-- '+00:00' または 'UTC' 等
SESSIONTIMEZONE
セッションの TZ:
SELECT SESSIONTIMEZONE FROM DUAL;
-- 'Asia/Tokyo' 等
-- 変更
ALTER SESSION SET TIME_ZONE = 'Asia/Tokyo';
使い分け表
| 用途 | 関数 |
|---|---|
| ログのタイムスタンプ(サーバー時刻) | SYSDATE / SYSTIMESTAMP |
| ユーザー体験(クライアント時刻) | CURRENT_DATE / CURRENT_TIMESTAMP |
| TZ 情報不要 | SYSDATE / LOCALTIMESTAMP |
| 高精度 | SYSTIMESTAMP |
日付演算
DATE の演算
-- ① 日数の加算
SELECT SYSDATE + 1 FROM DUAL; -- 明日
SELECT SYSDATE + 1/24 FROM DUAL; -- 1時間後
SELECT SYSDATE + 1/1440 FROM DUAL; -- 1分後
-- ② 日数の減算
SELECT SYSDATE - 7 FROM DUAL; -- 1週間前
-- ③ 日付の差(日数)
SELECT DATE '2026-06-30' - DATE '2026-06-15' FROM DUAL;
-- 15
-- ④ 月加算
SELECT ADD_MONTHS(SYSDATE, 3) FROM DUAL; -- 3ヶ月後
-- ⑤ 月数の差
SELECT MONTHS_BETWEEN(DATE '2026-12-31', DATE '2026-06-15') FROM DUAL;
-- 6.516...
TIMESTAMP の演算
INTERVAL 型が返る:
SELECT TIMESTAMP '2026-06-15 14:00:00' - TIMESTAMP '2026-06-15 12:00:00' FROM DUAL;
-- +000000000 02:00:00.000000 ← INTERVAL DAY TO SECOND
-- INTERVAL の加減
SELECT SYSTIMESTAMP + INTERVAL '1' HOUR FROM DUAL;
SELECT SYSTIMESTAMP + INTERVAL '30' MINUTE FROM DUAL;
SELECT SYSTIMESTAMP + INTERVAL '1' DAY FROM DUAL;
DATE と TIMESTAMP の混在
-- DATE + INTERVAL は可能
SELECT SYSDATE + INTERVAL '1' HOUR FROM DUAL;
INTERVAL 型
2種類の INTERVAL
INTERVAL YEAR TO MONTH:
INTERVAL '2-6' YEAR TO MONTH -- 2年6ヶ月
INTERVAL '5' YEAR -- 5年
INTERVAL '18' MONTH -- 18ヶ月
INTERVAL DAY TO SECOND:
INTERVAL '10 12:30:45.5' DAY TO SECOND -- 10日12時30分45.5秒
INTERVAL '3' DAY -- 3日
INTERVAL '2' HOUR -- 2時間
INTERVAL '30' MINUTE -- 30分
INTERVAL '45' SECOND -- 45秒
INTERVAL の使い方
-- テーブル定義
CREATE TABLE subscriptions (
sub_id NUMBER,
duration INTERVAL YEAR TO MONTH,
trial_period INTERVAL DAY TO SECOND
);
-- 演算
SELECT SYSDATE + INTERVAL '1-3' YEAR TO MONTH FROM DUAL;
-- 1年3ヶ月後
変換関数
-- NUMTOYMINTERVAL(数値 → INTERVAL YEAR TO MONTH)
SELECT NUMTOYMINTERVAL(1.5, 'YEAR') FROM DUAL; -- 1年6ヶ月
-- NUMTODSINTERVAL(数値 → INTERVAL DAY TO SECOND)
SELECT NUMTODSINTERVAL(2, 'HOUR') FROM DUAL; -- 2時間
-- EXTRACT
SELECT EXTRACT(HOUR FROM INTERVAL '5:30' HOUR TO MINUTE) FROM DUAL;
-- 5
変換関数
TO_CHAR
-- DATE → VARCHAR2
SELECT TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS') FROM DUAL;
-- TIMESTAMP → VARCHAR2
SELECT TO_CHAR(SYSTIMESTAMP, 'YYYY-MM-DD HH24:MI:SS.FF6') FROM DUAL;
-- TZ 付き
SELECT TO_CHAR(SYSTIMESTAMP, 'YYYY-MM-DD HH24:MI:SS TZR') FROM DUAL;
TO_DATE
SELECT TO_DATE('2026-06-15', 'YYYY-MM-DD') FROM DUAL;
SELECT TO_DATE('15/06/2026 14:30', 'DD/MM/YYYY HH24:MI') FROM DUAL;
日付変換のエラーは ORA-01843: not a valid month の記事、ORA-01830: date format ends before の記事、ORA-01858: non-numeric character の記事も参照してください。
TO_TIMESTAMP
SELECT TO_TIMESTAMP('2026-06-15 14:30:45.123', 'YYYY-MM-DD HH24:MI:SS.FF3')
FROM DUAL;
TO_TIMESTAMP_TZ
SELECT TO_TIMESTAMP_TZ('2026-06-15 14:30:45 +09:00',
'YYYY-MM-DD HH24:MI:SS TZH:TZM')
FROM DUAL;
CAST
-- DATE → TIMESTAMP
SELECT CAST(SYSDATE AS TIMESTAMP) FROM DUAL;
-- TIMESTAMP → DATE(fractional 削除)
SELECT CAST(SYSTIMESTAMP AS DATE) FROM DUAL;
-- TIMESTAMP → TIMESTAMP WITH TIME ZONE
SELECT CAST(LOCALTIMESTAMP AS TIMESTAMP WITH TIME ZONE) FROM DUAL;
FROM_TZ
-- TIMESTAMP に TZ を追加
SELECT FROM_TZ(LOCALTIMESTAMP, 'Asia/Tokyo') FROM DUAL;
NLS 設定
NLS_DATE_FORMAT
-- 現在の設定
SELECT * FROM v$nls_parameters WHERE parameter LIKE 'NLS_DATE%';
-- 変更(セッション)
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';
SELECT SYSDATE FROM DUAL;
-- 2026-06-15 14:30:45(指定形式)
-- リセット
ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MON-YY';
NLS_TIMESTAMP_FORMAT
ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF6';
NLS_TIMESTAMP_TZ_FORMAT
ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT =
'YYYY-MM-DD HH24:MI:SS.FF6 TZR';
システムレベル設定
-- DB 全体
ALTER SYSTEM SET NLS_DATE_FORMAT = 'YYYY-MM-DD' SCOPE = SPFILE;
-- 再起動必要
パラメータ関連は Oracle パラメータ確認(V$PARAMETER)の記事も参照してください。
常に明示的に指定推奨
-- ❌ NLS 依存(環境で変わる)
SELECT TO_DATE('15/06/2026') FROM DUAL;
-- ✅ 明示的
SELECT TO_DATE('15/06/2026', 'DD/MM/YYYY') FROM DUAL;
パフォーマンス
ストレージ
| 型 | サイズ | 100万行 |
|---|---|---|
| DATE | 7B | 7 MB |
| TIMESTAMP(0) | 7B | 7 MB |
| TIMESTAMP(6) | 11B | 11 MB |
| TIMESTAMP WITH TZ | 13B | 13 MB |
インデックス
通常の B-Tree インデックスが有効:
CREATE INDEX idx_order_date ON orders(order_date);
範囲検索
-- ✅ インデックス使用
WHERE order_date >= DATE '2026-06-01'
AND order_date < DATE '2026-07-01'
-- ❌ 関数使用でインデックス無効化
WHERE TO_CHAR(order_date, 'YYYY-MM') = '2026-06'
WHERE EXTRACT(MONTH FROM order_date) = 6
-- ✅ 関数ベース索引で解決
CREATE INDEX idx_month ON orders(EXTRACT(MONTH FROM order_date));
TRUNC の問題
-- ❌ インデックス無効化
WHERE TRUNC(order_date) = DATE '2026-06-15'
-- ✅ 範囲で
WHERE order_date >= DATE '2026-06-15'
AND order_date < DATE '2026-06-16'
パフォーマンス関連は Oracle 分析関数(OVER/PARTITION BY)の記事、EXPLAIN PLAN 系記事も参考になります。
パーティション化
-- 日付ベースパーティション
CREATE TABLE orders (
id NUMBER,
order_date DATE,
...
)
PARTITION BY RANGE (order_date)
INTERVAL (NUMTOYMINTERVAL(1, 'MONTH'))
(
PARTITION p_start VALUES LESS THAN (DATE '2020-01-01')
);
大量データで必須。tablespace 関連は Oracle Tablespace 管理の記事も参照してください。
他 DB との比較
型マッピング
| Oracle | PostgreSQL | MySQL | SQL Server |
|---|---|---|---|
| DATE | DATE | DATE | DATE |
| DATE(時刻含む) | TIMESTAMP | DATETIME | DATETIME2 |
| TIMESTAMP | TIMESTAMP | DATETIME(6) | DATETIME2 |
| TIMESTAMP WITH TZ | TIMESTAMPTZ | (なし) | DATETIMEOFFSET |
重要な違い
PostgreSQL/MySQL の DATE:
- 日付のみ(時刻なし)
Oracle の DATE:
- 年月日 + 時分秒(一般の DATETIME 相当)
移行時の落とし穴:
PostgreSQL: DATE → 時刻情報なし
Oracle: DATE → 時刻情報あり
→ 混同すると誤動作
PostgreSQL からの移行
PostgreSQL DATE → Oracle DATE(時刻情報は 00:00:00)
PostgreSQL TIMESTAMP → Oracle TIMESTAMP or DATE
PostgreSQL TIMESTAMPTZ → Oracle TIMESTAMP WITH TIME ZONE
Rails / Java / Python 対応
Rails ActiveRecord
Rails のマイグレーション:
class CreateOrders < ActiveRecord::Migration[8.0]
def change
create_table :orders do |t|
t.date :order_date # DATE
t.datetime :ordered_at # TIMESTAMP(6)
t.timestamps # created_at, updated_at (TIMESTAMP)
end
end
end
oracle_enhanced adapter:
date→ DATEdatetime→ TIMESTAMP(6)time→ DATE(Oracle には TIME 型なし)
モデル:
class Order < ApplicationRecord
# DateTime との変換は自動
def formatted_time
ordered_at.strftime("%Y-%m-%d %H:%M:%S")
end
end
Rails 8 系の詳細は Rails 8 アップグレードガイドの記事、rails db:migrate 使い方の記事、find/find_by/where の記事も参照してください。
Java (JDBC)
// DATE
java.sql.Date sqlDate = rs.getDate("order_date");
// TIMESTAMP
java.sql.Timestamp ts = rs.getTimestamp("ordered_at");
// TIMESTAMP WITH TIME ZONE (Oracle 拡張)
oracle.sql.TIMESTAMPTZ tstz = rs.getObject("event_at", oracle.sql.TIMESTAMPTZ.class);
// Java 8+: LocalDateTime
LocalDateTime ldt = rs.getObject("ordered_at", LocalDateTime.class);
OffsetDateTime odt = rs.getObject("event_at", OffsetDateTime.class);
Python (oracledb)
import oracledb
from datetime import datetime, timezone
# DATE
cursor.execute("SELECT SYSDATE FROM DUAL")
d = cursor.fetchone()[0]
# type(d) == datetime
# TIMESTAMP
cursor.execute("SELECT SYSTIMESTAMP FROM DUAL")
ts = cursor.fetchone()[0]
# type(ts) == datetime (with tzinfo)
# INSERT
cursor.execute("""
INSERT INTO orders VALUES (:1, :2, :3)
""", (1, datetime.now(), datetime.now(timezone.utc)))
実践シナリオ
シナリオ1:業務システムの標準設計
CREATE TABLE orders (
order_id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
order_date DATE NOT NULL DEFAULT SYSDATE, -- 業務日
created_at TIMESTAMP(6) DEFAULT SYSTIMESTAMP, -- 高精度
updated_at TIMESTAMP(6) DEFAULT SYSTIMESTAMP
);
-- 業務日: DATE(秒精度で十分)
-- 監査タイムスタンプ: TIMESTAMP
シナリオ2:国際サービス
CREATE TABLE international_events (
event_id NUMBER PRIMARY KEY,
event_at TIMESTAMP WITH TIME ZONE NOT NULL,
location VARCHAR2(100)
);
-- ユーザーのTZで検索
SELECT event_id,
event_at AT TIME ZONE 'Asia/Tokyo' AS tokyo_time
FROM international_events;
シナリオ3:ログ・監査
CREATE TABLE audit_log (
log_id NUMBER GENERATED ALWAYS AS IDENTITY,
user_id NUMBER,
action VARCHAR2(100),
logged_at TIMESTAMP(9) -- ナノ秒精度(順序保証)
);
ナノ秒精度 = 高並行性下でも順序保証。
シナリオ4:金融トランザクション
CREATE TABLE transactions (
txn_id NUMBER PRIMARY KEY,
amount NUMBER(15, 2),
executed_at TIMESTAMP(6) WITH TIME ZONE NOT NULL
);
-- 24時間365日、複数タイムゾーンでの取引
INSERT INTO transactions VALUES
(1, 1000.00, TIMESTAMP '2026-06-15 14:30:45.123 Asia/Tokyo');
数値型(NUMBER(15, 2))は Oracle NUMBER型 vs INTEGER の違いの記事も参照してください。
シナリオ5:期間管理
CREATE TABLE subscriptions (
sub_id NUMBER PRIMARY KEY,
user_id NUMBER,
start_date DATE,
duration INTERVAL YEAR TO MONTH
);
-- 契約期限計算
SELECT sub_id, start_date, duration,
start_date + duration AS end_date
FROM subscriptions;
シナリオ6:Rails での使い分け
class Event < ApplicationRecord
# order_date: DATE
# scheduled_at: TIMESTAMP
scope :today, -> { where(order_date: Date.current) }
scope :recent, -> { where(scheduled_at: 1.hour.ago..) }
end
シナリオ7:Cron 実行の TZ 処理
# バッチジョブ
class DailyBatch < ApplicationJob
def perform
# サーバー TZ に依存しない
yesterday = Time.current.utc.beginning_of_day - 1.day
Order.where(created_at: yesterday..(yesterday + 1.day)).each do |o|
process(o)
end
end
end
Solid Queue の詳細は Solid Queue 使い方の記事も参照してください。
シナリオ8:Data Pump での TZ 移行
# TZ 情報保持でエクスポート
expdp system/pw dumpfile=data.dmp tables=events
# 別 DB へインポート
impdp system/pw dumpfile=data.dmp tables=events
注意: TIMESTAMP WITH LOCAL TIME ZONE は DB TZ が同じ必要。
Data Pump の詳細は Oracle Data Pump 使い方の記事も参照してください。
シナリオ9:Docker Oracle での TZ 設定
docker run -e TZ=Asia/Tokyo \
-e ORACLE_TIMEZONE=Asia/Tokyo \
gvenzl/oracle-xe:21
Docker 関連は docker daemon 接続エラーの記事、Docker no space left on device の記事も参照してください。
シナリオ10:AWS RDS Oracle での TZ
Parameter Group:
- timezone = Asia/Tokyo(設定)
- sessions がその TZ で開始
予防のベストプラクティス
1. DATE の落とし穴意識
-- DATE は時刻も含む
-- 日付だけ比較したい場合、範囲指定を
WHERE d >= DATE '2026-06-15' AND d < DATE '2026-06-16'
2. 常に NLS_DATE_FORMAT を明示
-- ❌ 環境依存
TO_DATE('01/02/2026')
-- ✅ 明示
TO_DATE('01/02/2026', 'DD/MM/YYYY')
3. 国際サービスは TIMESTAMP WITH TZ
event_at TIMESTAMP(6) WITH TIME ZONE
4. UTC 統一が現代の標準
-- DB TZ を UTC に
ALTER DATABASE SET TIME_ZONE = 'UTC';
5. インデックス設計
-- 範囲検索を活用
WHERE d >= X AND d < Y -- インデックス有効
-- 関数使用は最小限
-- 必要なら関数ベース索引
6. INTERVAL 型の活用
-- 期間管理は INTERVAL
duration INTERVAL YEAR TO MONTH
7. パーティション化
-- 大量履歴データ
PARTITION BY RANGE (created_at)
INTERVAL (NUMTOYMINTERVAL(1, 'MONTH'))
8. TZ 変更の慎重な扱い
-- TIMESTAMP WITH LOCAL TIME ZONE を使用中は
-- DBTIMEZONE 変更禁止(データがあると不可)
9. Rails の Time.current 活用
Time.current # config.time_zone の時刻
Time.now # サーバー時刻(避ける)
10. 精度の意識
-- 必要以上の精度は無駄
TIMESTAMP(0) -- 秒
TIMESTAMP(3) -- ミリ秒(Web アプリ)
TIMESTAMP(6) -- マイクロ秒(デフォルト)
TIMESTAMP(9) -- ナノ秒(監査)
トラブルシューティング
ORA-01843: not a valid month
日付フォーマット不一致:
-- 明示的フォーマット指定
TO_DATE('15-06-2026', 'DD-MM-YYYY')
ORA-01843 の詳細は ORA-01843: not a valid month の記事を参照してください。
時刻がずれる
TZ 設定確認:
SELECT DBTIMEZONE, SESSIONTIMEZONE FROM DUAL;
DATE 検索でヒットしない
時刻が含まれている:
-- 修正
WHERE d >= DATE 'X' AND d < DATE 'X+1'
fractional_seconds が消える
DATE にキャストで消える:
CAST(SYSTIMESTAMP AS DATE) -- fractional 消失
TIMESTAMP 演算の結果が INTERVAL
SELECT TIMESTAMP '...' - TIMESTAMP '...' FROM DUAL;
-- INTERVAL DAY TO SECOND
-- 日数に変換
SELECT EXTRACT(DAY FROM (t1 - t2)) FROM DUAL;
DST の落とし穴
-- サマータイム開始時、1時間ジャンプ
-- 存在しない時刻を指定するとエラー
TIMESTAMP '2026-03-08 02:30:00 America/New_York'
-- ORA-01878 (fractional seconds error)
UTC 統一で回避。
よくある質問(FAQ)
Q1. DATE と TIMESTAMP どちらを使うべき?
- 秒精度で十分: DATE
- ミリ秒〜必要: TIMESTAMP
- 国際サービス: TIMESTAMP WITH TZ
Q2. DATE は本当に日付だけ?
いいえ、年月日 + 時分秒(秒精度)。
Q3. SYSDATE と SYSTIMESTAMP の違い
- SYSDATE: DATE(秒精度)
- SYSTIMESTAMP: TIMESTAMP WITH TZ(マイクロ秒精度)
Q4. CURRENT_DATE と SYSDATE の違い
- CURRENT_DATE: セッションTZ
- SYSDATE: サーバーOS TZ
Q5. TIMESTAMP WITH TIME ZONE と LOCAL TIME ZONE の違い
- WITH TIME ZONE: TZ を明示保存
- WITH LOCAL TIME ZONE: DB TZ に正規化、表示時変換
Q6. INTERVAL 型はいつ使う?
期間管理(契約期間、経過時間等)。
Q7. NLS_DATE_FORMAT の変更
-- セッション
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD';
-- 環境変数
export NLS_DATE_FORMAT="YYYY-MM-DD"
Q8. Rails での使い分け
t.date→ DATEt.datetime→ TIMESTAMPt.timestamps→ TIMESTAMP × 2
Q9. PostgreSQL からの移行
- PG DATE → Oracle DATE(時刻情報 00:00:00)
- PG TIMESTAMPTZ → Oracle TIMESTAMP WITH TZ
Q10. 日付範囲検索の最適化
WHERE d >= X AND d < Y -- インデックス有効
Q11. UTC 統一のメリット
- TZ 変換不要
- DST 問題回避
- 国際対応
Q12. Autonomous DB のデフォルト
UTC 統一。TIMESTAMP WITH TIME ZONE 推奨。
参考リンク
Oracle 公式
- Oracle Database Globalization Support Guide: Datetime Data Types
- Oracle SQL Language Reference: Data Types
- SYSDATE Function
- SYSTIMESTAMP Function
まとめ
Oracle DATE vs TIMESTAMP の要点を再整理します。
5種類の日付型
| 型 | 精度 | サイズ | TZ |
|---|---|---|---|
| DATE | 秒 | 7B | なし |
| TIMESTAMP | ナノ秒 | 11B | なし |
| TIMESTAMP(n) | n桁 | 7-11B | なし |
| TIMESTAMP WITH TZ | ナノ秒 | 13B | 明示 |
| TIMESTAMP WITH LOCAL TZ | ナノ秒 | 11B | 暗黙UTC |
7つの現在時刻関数
SYSDATE -- DATE、サーバー
SYSTIMESTAMP -- TIMESTAMP WITH TZ、サーバー
CURRENT_DATE -- DATE、セッション
CURRENT_TIMESTAMP -- TIMESTAMP WITH TZ、セッション
LOCALTIMESTAMP -- TIMESTAMP、セッション
DBTIMEZONE -- DB の TZ
SESSIONTIMEZONE -- セッション TZ
選択ガイド
業務日、日付管理 → DATE
高精度ログ → TIMESTAMP
国際サービス、金融 → TIMESTAMP WITH TIME ZONE
DB TZ 統一で OK → TIMESTAMP WITH LOCAL TIME ZONE
期間 → INTERVAL
演算
-- DATE
DATE + NUMBER = DATE -- 日数加算
DATE - DATE = NUMBER -- 日数差
-- TIMESTAMP
TIMESTAMP + INTERVAL = TIMESTAMP
TIMESTAMP - TIMESTAMP = INTERVAL DAY TO SECOND
-- 関数
ADD_MONTHS(d, n)
MONTHS_BETWEEN(d1, d2)
LAST_DAY(d)
NEXT_DAY(d, 'MONDAY')
TRUNC(d, 'MM')
INTERVAL 型
INTERVAL YEAR TO MONTH -- 年月
INTERVAL DAY TO SECOND -- 日時分秒
NUMTOYMINTERVAL(n, 'YEAR')
NUMTODSINTERVAL(n, 'HOUR')
タイムゾーン
event_at AT TIME ZONE 'Asia/Tokyo'
FROM_TZ(LOCALTIMESTAMP, 'Asia/Tokyo')
ALTER SESSION SET TIME_ZONE = 'Asia/Tokyo';
変換関数
TO_CHAR(d, 'YYYY-MM-DD')
TO_DATE('...', 'YYYY-MM-DD')
TO_TIMESTAMP('...', 'YYYY-MM-DD HH24:MI:SS.FF6')
TO_TIMESTAMP_TZ('...', '... TZH:TZM')
CAST(SYSDATE AS TIMESTAMP)
パフォーマンス
- 範囲検索を使う (>= AND <)
- TRUNC/関数を避ける(or 関数ベース索引)
- パーティション化
- 適切な精度選択
これらの知識は、Oracle でのアプリ設計・国際サービス・金融システム・ログ設計・監査ログ・パフォーマンスチューニング・Rails / Java / Python 開発・他 DB 移行など、あらゆる場面で活用できます。本記事をブックマークしておけば、Oracle 日付型を確実に理解・選択できるようになります。
本記事は2026年6月時点の情報をもとに、Oracle Database 19c〜23ai での動作確認・公式ドキュメントに基づき作成しています。Oracle のバージョンにより挙動が異なる場合があるため、最新の情報は Oracle 公式ドキュメント(docs.oracle.com)もあわせてご確認ください。
-
前の記事
【完全ガイド】Oracle LOB(BLOB/CLOB)操作 徹底解説|DBMS_LOB・SecureFiles・暗号化まで 2026.07.27
-
次の記事
【完全ガイド】Oracle EXPLAIN PLAN 見方 徹底解説|DBMS_XPLAN・実行計画の読み方・チューニング 2026.07.28
コメントを書く