「今の時刻をデータベースに記録したい」
「作成日時や更新日時を自動で設定したい」
「NOW()とCURRENT_TIMESTAMPって何が違うの?」
「タイムゾーンの扱いがよく分からない…」
こんな疑問を持っていませんか?
日時の扱いは、どんなアプリケーションでも必要になる基本機能です。でも、PostgreSQLには現在日時を取得する関数がたくさんあって、どれを使えばいいか迷いますよね。
この記事では、PostgreSQLでの現在日時の取得方法から、日時の計算、フォーマット、タイムゾーンの扱いまで、実例を交えて分かりやすく解説していきます。
現在日時を取得する基本的な関数

NOW()関数 – 最もよく使う関数
現在の日時をタイムスタンプで取得する最も一般的な関数です。
基本的な使い方:
-- 現在の日時を取得
SELECT NOW();
-- 結果: 2024-01-20 14:30:45.123456+09
-- テーブルに挿入
INSERT INTO logs (message, created_at)
VALUES ('ログメッセージ', NOW());
-- 条件での使用
SELECT * FROM events
WHERE start_date <= NOW();
NOW()の特徴:
- タイムゾーン付きのタイムスタンプを返す
- ミリ秒単位まで取得
- トランザクション開始時の時刻を返す(重要!)
CURRENT_TIMESTAMP – SQL標準
NOW()とほぼ同じ機能ですが、SQL標準の書き方です。
-- CURRENT_TIMESTAMPの使用
SELECT CURRENT_TIMESTAMP;
-- 結果: 2024-01-20 14:30:45.123456+09
-- 精度を指定(0〜6)
SELECT CURRENT_TIMESTAMP(0); -- 秒まで
-- 結果: 2024-01-20 14:30:45+09
SELECT CURRENT_TIMESTAMP(3); -- ミリ秒まで
-- 結果: 2024-01-20 14:30:45.123+09
-- NOW()と同じ結果
SELECT NOW() = CURRENT_TIMESTAMP;
-- 結果: true
CURRENT_DATE – 日付のみ
時刻を除いた日付だけが必要な場合に使います。
-- 今日の日付を取得
SELECT CURRENT_DATE;
-- 結果: 2024-01-20
-- 今日のデータを検索
SELECT * FROM orders
WHERE order_date = CURRENT_DATE;
-- 年齢計算
SELECT
name,
birth_date,
EXTRACT(YEAR FROM AGE(CURRENT_DATE, birth_date)) AS age
FROM users;
CURRENT_TIME – 時刻のみ
現在の時刻だけを取得します。
-- 現在時刻を取得
SELECT CURRENT_TIME;
-- 結果: 14:30:45.123456+09
-- 精度指定
SELECT CURRENT_TIME(0);
-- 結果: 14:30:45+09
-- 営業時間内かチェック
SELECT
CASE
WHEN CURRENT_TIME BETWEEN '09:00:00' AND '18:00:00'
THEN '営業中'
ELSE '営業時間外'
END AS status;
タイムゾーンを考慮した日時取得
タイムゾーン付きとなしの違い
PostgreSQLには2種類のタイムスタンプ型があります。
-- タイムゾーン付き(TIMESTAMP WITH TIME ZONE)
SELECT NOW();
-- 結果: 2024-01-20 14:30:45.123456+09
-- タイムゾーンなし(TIMESTAMP WITHOUT TIME ZONE)
SELECT NOW()::TIMESTAMP;
-- 結果: 2024-01-20 14:30:45.123456
-- ローカルタイムスタンプ
SELECT LOCALTIMESTAMP;
-- 結果: 2024-01-20 14:30:45.123456
特定のタイムゾーンでの現在時刻
-- UTCでの現在時刻
SELECT NOW() AT TIME ZONE 'UTC';
-- 結果: 2024-01-20 05:30:45.123456
-- 東京時間
SELECT NOW() AT TIME ZONE 'Asia/Tokyo';
-- ニューヨーク時間
SELECT NOW() AT TIME ZONE 'America/New_York';
-- 現在のタイムゾーン設定を確認
SHOW TIMEZONE;
-- 結果: Asia/Tokyo
-- タイムゾーンの一時変更
SET TIMEZONE = 'UTC';
SELECT NOW();
SET TIMEZONE = 'Asia/Tokyo'; -- 元に戻す
トランザクションと時刻の関係
NOW()とCLOCK_TIMESTAMP()の違い
これは重要な違いです!
BEGIN;
-- トランザクション開始時の時刻
SELECT NOW();
-- 結果: 2024-01-20 14:30:45.123456+09
-- 1秒待つ
SELECT pg_sleep(1);
-- NOW()は同じ値を返す(トランザクション開始時)
SELECT NOW();
-- 結果: 2024-01-20 14:30:45.123456+09(同じ!)
-- CLOCK_TIMESTAMP()は現在の実時刻
SELECT CLOCK_TIMESTAMP();
-- 結果: 2024-01-20 14:30:46.234567+09(1秒後)
COMMIT;
使い分け:
-- ログの一貫性が必要な場合:NOW()
INSERT INTO audit_log (action, timestamp)
VALUES ('開始', NOW());
-- 処理...
INSERT INTO audit_log (action, timestamp)
VALUES ('終了', NOW()); -- 同じタイムスタンプ
-- 実際の処理時間を記録:CLOCK_TIMESTAMP()
INSERT INTO performance_log (start_time, end_time)
VALUES (
CLOCK_TIMESTAMP(),
CLOCK_TIMESTAMP() + INTERVAL '5 seconds'
);
STATEMENT_TIMESTAMP()
SQL文の実行開始時刻を返します。
-- 各タイムスタンプ関数の違い
SELECT
NOW() AS "トランザクション開始",
STATEMENT_TIMESTAMP() AS "SQL文開始",
CLOCK_TIMESTAMP() AS "現在時刻";
-- 大量データ処理での使用例
INSERT INTO process_log (batch_id, row_id, processed_at)
SELECT
STATEMENT_TIMESTAMP(), -- バッチ単位で同じ
id,
CLOCK_TIMESTAMP() -- 各行で異なる
FROM large_table;
デフォルト値として現在日時を設定
テーブル作成時のデフォルト設定
-- 作成日時と更新日時を自動設定
CREATE TABLE articles (
id SERIAL PRIMARY KEY,
title VARCHAR(200),
content TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- データ挿入(日時は自動設定)
INSERT INTO articles (title, content)
VALUES ('PostgreSQL入門', '内容...');
-- 確認
SELECT * FROM articles;
更新時に自動で日時を更新
トリガーを使って更新日時を自動更新します。
-- 更新日時を自動更新する関数
CREATE OR REPLACE FUNCTION update_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- トリガーの作成
CREATE TRIGGER update_articles_updated_at
BEFORE UPDATE ON articles
FOR EACH ROW
EXECUTE FUNCTION update_updated_at();
-- テスト
UPDATE articles SET title = '新しいタイトル' WHERE id = 1;
SELECT * FROM articles; -- updated_atが更新される
日時の計算と操作
日時の加算・減算
-- 現在から30日後
SELECT NOW() + INTERVAL '30 days';
-- 現在から1時間前
SELECT NOW() - INTERVAL '1 hour';
-- 複雑な計算
SELECT NOW() + INTERVAL '1 year 2 months 3 days 4 hours 5 minutes';
-- 変数を使った計算
SELECT NOW() + (7 * INTERVAL '1 day'); -- 7日後
-- 営業日の計算(週末を除く)
SELECT CURRENT_DATE +
CASE
WHEN EXTRACT(DOW FROM CURRENT_DATE) = 5 THEN INTERVAL '3 days' -- 金曜
WHEN EXTRACT(DOW FROM CURRENT_DATE) = 6 THEN INTERVAL '2 days' -- 土曜
ELSE INTERVAL '1 day'
END AS next_business_day;
日時の差分計算
-- 2つの日時の差
SELECT AGE(NOW(), '2020-01-01');
-- 結果: 4 years 19 days 14:30:45.123456
-- 日数の差
SELECT DATE_PART('day', NOW() - '2024-01-01'::TIMESTAMP);
-- 時間単位で差を計算
SELECT EXTRACT(EPOCH FROM (NOW() - '2024-01-20 12:00:00'::TIMESTAMP)) / 3600 AS hours_diff;
-- 稼働時間の計算
SELECT
task_name,
started_at,
completed_at,
completed_at - started_at AS duration
FROM tasks
WHERE completed_at IS NOT NULL;
日時の切り捨て・切り上げ
-- 時間単位で切り捨て
SELECT DATE_TRUNC('hour', NOW());
-- 結果: 2024-01-20 14:00:00+09
-- 日単位で切り捨て(その日の00:00:00)
SELECT DATE_TRUNC('day', NOW());
-- 結果: 2024-01-20 00:00:00+09
-- 月初
SELECT DATE_TRUNC('month', NOW());
-- 結果: 2024-01-01 00:00:00+09
-- 週の始まり(月曜日)
SELECT DATE_TRUNC('week', NOW());
-- 四半期の始まり
SELECT DATE_TRUNC('quarter', NOW());
-- 年初
SELECT DATE_TRUNC('year', NOW());
日時のフォーマット
TO_CHAR()関数でフォーマット
-- 基本的なフォーマット
SELECT TO_CHAR(NOW(), 'YYYY-MM-DD HH24:MI:SS');
-- 結果: 2024-01-20 14:30:45
-- 日本語形式
SELECT TO_CHAR(NOW(), 'YYYY年MM月DD日 HH24時MI分SS秒');
-- 結果: 2024年01月20日 14時30分45秒
-- 曜日付き
SELECT TO_CHAR(NOW(), 'YYYY-MM-DD (Day)');
-- 結果: 2024-01-20 (Saturday )
-- 月名表示
SELECT TO_CHAR(NOW(), 'Month DD, YYYY');
-- 結果: January 20, 2024
-- AM/PM表示
SELECT TO_CHAR(NOW(), 'YYYY-MM-DD HH12:MI:SS AM');
-- 結果: 2024-01-20 02:30:45 PM
よく使うフォーマットパターン
-- ISO 8601形式
SELECT TO_CHAR(NOW(), 'YYYY-MM-DD"T"HH24:MI:SS.MS');
-- ファイル名用(記号なし)
SELECT TO_CHAR(NOW(), 'YYYYMMDD_HH24MISS');
-- 結果: 20240120_143045
-- 短い日付
SELECT TO_CHAR(NOW(), 'YY/MM/DD');
-- 結果: 24/01/20
-- 時刻のみ
SELECT TO_CHAR(NOW(), 'HH24:MI');
-- 結果: 14:30
実践的な使用例
ログテーブルでの活用
-- アクセスログテーブル
CREATE TABLE access_logs (
id BIGSERIAL PRIMARY KEY,
user_id INT,
endpoint VARCHAR(255),
method VARCHAR(10),
status_code INT,
response_time_ms INT,
accessed_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
-- インデックス
INDEX idx_accessed_at (accessed_at),
INDEX idx_user_accessed (user_id, accessed_at)
);
-- 直近1時間のアクセス数
SELECT COUNT(*)
FROM access_logs
WHERE accessed_at >= NOW() - INTERVAL '1 hour';
-- 時間帯別アクセス数
SELECT
DATE_TRUNC('hour', accessed_at) AS hour,
COUNT(*) AS access_count
FROM access_logs
WHERE accessed_at >= CURRENT_DATE
GROUP BY hour
ORDER BY hour;
予約システムでの使用
-- 予約可能な時間枠を生成
WITH RECURSIVE time_slots AS (
SELECT CURRENT_DATE + TIME '09:00:00' AS slot_time
UNION ALL
SELECT slot_time + INTERVAL '30 minutes'
FROM time_slots
WHERE slot_time < CURRENT_DATE + TIME '17:30:00'
)
SELECT
slot_time,
CASE
WHEN slot_time < NOW() THEN '過去'
WHEN EXISTS (
SELECT 1 FROM reservations
WHERE reservation_time = slot_time
) THEN '予約済み'
ELSE '空き'
END AS status
FROM time_slots;
期限管理
-- 期限切れチェック
SELECT
id,
title,
deadline,
CASE
WHEN deadline < NOW() THEN '期限切れ'
WHEN deadline < NOW() + INTERVAL '1 day' THEN '本日期限'
WHEN deadline < NOW() + INTERVAL '7 days' THEN '1週間以内'
ELSE '余裕あり'
END AS status,
deadline - NOW() AS remaining_time
FROM tasks
ORDER BY deadline;
よくある質問と回答
Q1:NOW()とCURRENT_TIMESTAMPの違いは?
回答:
機能的には同じです。NOW()はPostgreSQL独自、CURRENT_TIMESTAMPはSQL標準です。どちらを使っても構いませんが、チーム内で統一することをお勧めします。
Q2:日本時間で保存されているか確認したい
回答:
-- タイムゾーン設定確認
SHOW TIMEZONE;
-- データベースのデフォルトタイムゾーン
SELECT current_setting('TIMEZONE');
-- 特定の時刻を日本時間で表示
SELECT created_at AT TIME ZONE 'Asia/Tokyo' FROM logs;
Q3:ミリ秒を切り捨てたい
回答:
-- 秒単位に切り捨て
SELECT DATE_TRUNC('second', NOW());
-- または精度指定
SELECT NOW()::TIMESTAMP(0);
Q4:文字列から日時に変換したい
回答:
-- 標準形式
SELECT '2024-01-20 14:30:45'::TIMESTAMP;
-- TO_TIMESTAMP関数
SELECT TO_TIMESTAMP('20/01/2024 14:30', 'DD/MM/YYYY HH24:MI');
まとめ:PostgreSQLの日時を使いこなそう!
PostgreSQLでの現在日時の扱いについて、基本から応用まで解説しました。
重要ポイントのおさらい:
- NOW():最も汎用的な現在日時取得
- トランザクション内では同じ値:一貫性が保たれる
- CLOCK_TIMESTAMP():実時刻が必要な場合
- タイムゾーン:WITH TIME ZONEを使う
よく使うパターン:
-- 基本の取得
SELECT NOW();
-- デフォルト値
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
-- 日時計算
NOW() + INTERVAL '30 days'
-- フォーマット
TO_CHAR(NOW(), 'YYYY-MM-DD HH24:MI:SS')
ベストプラクティス:
- タイムスタンプ型は
WITH TIME ZONE
を使用 - 作成日時・更新日時は自動設定
- インデックスを適切に設定
- タイムゾーンを意識した設計
日時の扱いは、どんなアプリケーションでも重要な要素です。この記事で紹介した技術を活用して、堅牢で使いやすいデータベース設計を実現してください!
コメント