【Oracle】月末を取得する方法|LAST_DAY関数の使い方と実務パターンまとめ

Oracleデータベースで月末日を取得するには、LAST_DAY関数を使います。当月末・前月末・翌月末の取得はもちろん、月初日の算出や月末判定うるう年の自動対応まで、日付処理の重要な場面で活躍する関数です。

この記事では、LAST_DAY関数の基本構文から、ADD_MONTHSTRUNCとの組み合わせ、WHERE句での月末データ抽出月次締め処理などの実務パターンまで、網羅的に解説します。

この記事で学べること

  • LAST_DAY関数の基本構文と戻り値
  • 当月末・前月末・翌月末の取得方法
  • 月初日の取得(TRUNC + ADD_MONTHS)
  • うるう年2月末の自動判定
  • 今日が月末かどうかの月末判定
  • 月末のN営業日前を取得する方法
  • ADD_MONTHSとの組み合わせパターン
  • WHERE句での月末データ抽出
  • 他RDBMS比較(MySQL・PostgreSQL・SQL Server)
  • よくあるエラーと対処法
  • 実務パターン(月次締め処理等)
スポンサーリンク

LAST_DAY関数の基本構文

LAST_DAYは、指定した日付が属する月の最終日を返すOracle組み込み関数です。引数にDATE型またはTIMESTAMP型の値を渡すと、その月の末日をDATE型で返します。

構文
LAST_DAY(date)
項目 説明
引数 DATE型またはTIMESTAMP型の日付値
戻り値 引数の月の最終日(DATE型)
NULL 引数がNULLの場合、NULLを返す
時刻部分 引数の時刻部分がそのまま保持される

最も基本的な使い方を見てみましょう。

SQL
-- 指定日付の月末を取得
SELECT LAST_DAY(TO_DATE('2025-06-15', 'YYYY-MM-DD')) AS month_end
FROM DUAL;

実行結果

MONTH_END
-----------
2025-06-30

6月15日を渡すと、6月の最終日である6月30日が返されます。月の何日目を渡しても、必ずその月の末日を返すのがLAST_DAYの特徴です。

当月末・前月末・翌月末の取得

実務では、現在日付を基準に当月末前月末翌月末を求めるケースが多くあります。LAST_DAYとADD_MONTHSを組み合わせることで簡単に取得できます。

当月末の取得

SQL
-- 当月末を取得
SELECT LAST_DAY(SYSDATE) AS this_month_end
FROM DUAL;

実行結果(実行日: 2025-03-15の場合)

THIS_MONTH_END
--------------
2025-03-31

前月末の取得

SQL
-- 前月末を取得
SELECT LAST_DAY(ADD_MONTHS(SYSDATE, -1)) AS prev_month_end
FROM DUAL;

実行結果

PREV_MONTH_END
--------------
2025-02-28

翌月末の取得

SQL
-- 翌月末を取得
SELECT LAST_DAY(ADD_MONTHS(SYSDATE, 1)) AS next_month_end
FROM DUAL;

実行結果

NEXT_MONTH_END
--------------
2025-04-30

まとめて取得

3つの月末をまとめて1回のSQLで取得することもできます。

SQL
-- 前月末・当月末・翌月末をまとめて取得
SELECT
  LAST_DAY(ADD_MONTHS(SYSDATE, -1)) AS prev_month_end,
  LAST_DAY(SYSDATE)                  AS this_month_end,
  LAST_DAY(ADD_MONTHS(SYSDATE, 1))  AS next_month_end
FROM DUAL;

実行結果

PREV_MONTH_END  THIS_MONTH_END  NEXT_MONTH_END
--------------  --------------  --------------
2025-02-28      2025-03-31      2025-04-30

ポイント:ADD_MONTHSの第2引数に負の値を指定すると過去の月、正の値で未来の月を指定できます。例えば ADD_MONTHS(SYSDATE, -3) で3ヶ月前の月末を取得できます。

月初日の取得(TRUNC + ADD_MONTHS)

LAST_DAYは月末を取得する関数ですが、月初日(1日)を取得するにはいくつかの方法があります。

方法1: TRUNCを使う(推奨)

SQL
-- TRUNCで当月1日を取得
SELECT TRUNC(SYSDATE, 'MM') AS first_day
FROM DUAL;

実行結果

FIRST_DAY
-----------
2025-03-01

方法2: LAST_DAYから算出する

SQL
-- 前月末 + 1日 = 当月1日
SELECT LAST_DAY(ADD_MONTHS(SYSDATE, -1)) + 1 AS first_day
FROM DUAL;

実行結果

FIRST_DAY
-----------
2025-03-01

月初日と月末日をセットで取得

SQL
-- 当月の初日と末日をセットで取得
SELECT
  TRUNC(SYSDATE, 'MM')   AS first_day,
  LAST_DAY(SYSDATE)        AS last_day
FROM DUAL;

実行結果

FIRST_DAY    LAST_DAY
-----------  -----------
2025-03-01   2025-03-31

TRUNCの月切り捨てについて

  • TRUNC(date, 'MM') は日付を月の初日に切り捨てる
  • 時刻部分もリセットされ 00:00:00 になる
  • TRUNC(date, 'YYYY') とすれば年初(1月1日)も取得可能

うるう年2月末の動作

LAST_DAY関数はうるう年を自動的に判定します。手動で28日・29日を判定する必要はありません。

SQL
-- うるう年 vs 平年の2月末比較
SELECT
  LAST_DAY(TO_DATE('2024-02-01', 'YYYY-MM-DD')) AS leap_year_2024,
  LAST_DAY(TO_DATE('2025-02-01', 'YYYY-MM-DD')) AS normal_year_2025,
  LAST_DAY(TO_DATE('2028-02-15', 'YYYY-MM-DD')) AS leap_year_2028,
  LAST_DAY(TO_DATE('2100-02-10', 'YYYY-MM-DD')) AS century_2100
FROM DUAL;

実行結果

LEAP_YEAR_2024  NORMAL_YEAR_2025  LEAP_YEAR_2028  CENTURY_2100
--------------  ----------------  --------------  ------------
2024-02-29      2025-02-28        2028-02-29      2100-02-28
うるう年? 2月末日 理由
2024 Yes 2月29日 4で割り切れる
2025 No 2月28日 4で割り切れない
2028 Yes 2月29日 4で割り切れる
2100 No 2月28日 100で割り切れるが400で割り切れない

ポイント:LAST_DAY関数はうるう年のルール(4年に1回、ただし100年に1回はスキップ、400年に1回は適用)を完全に考慮します。手動で判定する必要はありません。

月末判定(今日が月末かどうか)

バッチ処理や月次処理で「今日が月末かどうか」を判定したいケースがあります。LAST_DAYとSYSDATEを比較することで簡単に実現できます。

基本的な月末判定

SQL
-- 今日が月末かどうか判定
SELECT
  SYSDATE AS today,
  LAST_DAY(SYSDATE) AS month_end,
  CASE
    WHEN TRUNC(SYSDATE) = LAST_DAY(TRUNC(SYSDATE))
    THEN '月末です'
    ELSE '月末ではありません'
  END AS is_month_end
FROM DUAL;

実行結果(実行日: 2025-03-31の場合)

TODAY       MONTH_END   IS_MONTH_END
----------  ----------  ------------------
2025-03-31  2025-03-31  月末です

注意:SYSDATEには時刻情報が含まれるため、比較時はTRUNCで時刻部分を切り捨てる必要があります。TRUNCを忘れると、時刻のずれで一致しなくなる場合があります。

PL/SQLでの月末判定

PL/SQL
DECLARE
  v_today  DATE := TRUNC(SYSDATE);
BEGIN
  IF v_today = LAST_DAY(v_today) THEN
    DBMS_OUTPUT.PUT_LINE('今日は月末です。月次処理を実行します。');
    -- 月次処理をここに記述
  ELSE
    DBMS_OUTPUT.PUT_LINE('今日は月末ではありません。');
  END IF;
END;
/

月末までの残日数を取得

SQL
-- 月末までの残日数
SELECT
  TRUNC(SYSDATE) AS today,
  LAST_DAY(SYSDATE) AS month_end,
  LAST_DAY(SYSDATE) - TRUNC(SYSDATE) AS remaining_days
FROM DUAL;

実行結果(実行日: 2025-03-15の場合)

TODAY       MONTH_END   REMAINING_DAYS
----------  ----------  --------------
2025-03-15  2025-03-31              16

月末のN営業日前を取得

経理・会計処理では「月末のN営業日前」を求める必要があることがあります。土日を除いた営業日ベースで計算する方法を紹介します。

月末の2営業日前を取得

SQL
-- 月末の2営業日前(土日を除く)
SELECT
  LAST_DAY(SYSDATE) AS month_end,
  CASE
    -- 月末が月曜の場合: 2営業日前 = 前週木曜(-4日)
    WHEN TO_CHAR(LAST_DAY(SYSDATE), 'DY', 'NLS_DATE_LANGUAGE=AMERICAN') = 'MON'
    THEN LAST_DAY(SYSDATE) - 4
    -- 月末が火曜の場合: 2営業日前 = 前週金曜(-4日)
    WHEN TO_CHAR(LAST_DAY(SYSDATE), 'DY', 'NLS_DATE_LANGUAGE=AMERICAN') = 'TUE'
    THEN LAST_DAY(SYSDATE) - 4
    -- 月末が日曜の場合: 2営業日前 = 木曜(-3日)
    WHEN TO_CHAR(LAST_DAY(SYSDATE), 'DY', 'NLS_DATE_LANGUAGE=AMERICAN') = 'SUN'
    THEN LAST_DAY(SYSDATE) - 4
    -- 月末が土曜の場合: 2営業日前 = 水曜(-3日)
    WHEN TO_CHAR(LAST_DAY(SYSDATE), 'DY', 'NLS_DATE_LANGUAGE=AMERICAN') = 'SAT'
    THEN LAST_DAY(SYSDATE) - 3
    -- それ以外(水〜金): 単純に-2日
    ELSE LAST_DAY(SYSDATE) - 2
  END AS two_biz_days_before
FROM DUAL;

汎用的なN営業日前関数

繰り返し使う場合は、ファンクションとして定義すると便利です。

PL/SQL
CREATE OR REPLACE FUNCTION get_biz_days_before_month_end(
  p_date   IN DATE,
  p_days   IN NUMBER
) RETURN DATE IS
  v_target DATE := LAST_DAY(p_date);
  v_count  NUMBER := 0;
BEGIN
  WHILE v_count < p_days LOOP
    v_target := v_target - 1;
    -- 土日(1=日曜, 7=土曜)をスキップ
    IF TO_CHAR(v_target, 'D') NOT IN ('1', '7') THEN
      v_count := v_count + 1;
    END IF;
  END LOOP;
  RETURN v_target;
END;
/
SQL
-- 使用例: 月末の3営業日前
SELECT get_biz_days_before_month_end(SYSDATE, 3) FROM DUAL;

注意:TO_CHAR(date, 'D') の値はNLS_TERRITORYの設定に依存します。日本(JAPAN)では日曜=1、土曜=7ですが、環境によって異なる場合があります。安全のため TO_CHAR(date, 'DY', 'NLS_DATE_LANGUAGE=AMERICAN') で曜日名を判定する方法も検討してください。

ADD_MONTHSとの組み合わせ

ADD_MONTHSとLAST_DAYを組み合わせると、任意の月の月末を柔軟に取得できます。

過去・未来の月末を一括取得

SQL
-- 過去6ヶ月〜未来6ヶ月の月末一覧
SELECT
  TO_CHAR(ADD_MONTHS(SYSDATE, LEVEL - 7), 'YYYY-MM') AS year_month,
  LAST_DAY(ADD_MONTHS(SYSDATE, LEVEL - 7)) AS month_end
FROM DUAL
CONNECT BY LEVEL <= 13;

実行結果

YEAR_MONTH  MONTH_END
----------  -----------
2024-09     2024-09-30
2024-10     2024-10-31
2024-11     2024-11-30
2024-12     2024-12-31
2025-01     2025-01-31
2025-02     2025-02-28
2025-03     2025-03-31
2025-04     2025-04-30
2025-05     2025-05-31
2025-06     2025-06-30
2025-07     2025-07-31
2025-08     2025-08-31
2025-09     2025-09-30

各月の日数を取得

SQL
-- LAST_DAYで月の日数を計算
SELECT
  TO_CHAR(SYSDATE, 'YYYY-MM') AS year_month,
  TO_NUMBER(TO_CHAR(LAST_DAY(SYSDATE), 'DD')) AS days_in_month
FROM DUAL;

実行結果(2025年3月の場合)

YEAR_MONTH  DAYS_IN_MONTH
----------  -------------
2025-03                31

ADD_MONTHSの月末特有の挙動

ADD_MONTHSには月末日に対する特殊な挙動があります。この特性を理解しておくことが重要です。

SQL
-- ADD_MONTHSの月末挙動
SELECT
  TO_DATE('2025-01-31', 'YYYY-MM-DD') AS base_date,
  ADD_MONTHS(TO_DATE('2025-01-31', 'YYYY-MM-DD'), 1) AS plus_1_month,
  ADD_MONTHS(TO_DATE('2025-01-31', 'YYYY-MM-DD'), 2) AS plus_2_months
FROM DUAL;

実行結果

BASE_DATE   PLUS_1_MONTH  PLUS_2_MONTHS
----------  ------------  -------------
2025-01-31  2025-02-28    2025-03-31

ADD_MONTHSの月末ルール

  • 元の日付が月末の場合、結果もその月の月末になる
  • 例: 1/31 + 1ヶ月 = 2/28(2月の月末)
  • 元の日付が月末でない場合、日は保持される(例: 1/15 + 1ヶ月 = 2/15)
  • この挙動によりLAST_DAYなしでも月末が維持される場合がある

WHERE句での月末データ抽出

テーブルから月末日のデータだけを抽出したり、月末を基準にした期間絞り込みを行うパターンです。

月末日のレコードを抽出

SQL
-- 日付が月末のレコードのみ抽出
SELECT *
FROM sales_data
WHERE TRUNC(sale_date) = LAST_DAY(TRUNC(sale_date));

当月の初日〜末日で絞り込み

SQL
-- 当月のデータを取得
SELECT *
FROM orders
WHERE order_date BETWEEN
  TRUNC(SYSDATE, 'MM')
  AND LAST_DAY(SYSDATE);

前月分データの抽出

SQL
-- 前月の1日〜末日で絞り込み
SELECT *
FROM orders
WHERE order_date BETWEEN
  TRUNC(ADD_MONTHS(SYSDATE, -1), 'MM')
  AND LAST_DAY(ADD_MONTHS(SYSDATE, -1));

月別集計で月末日を表示

SQL
-- 月別売上集計と月末日表示
SELECT
  TO_CHAR(sale_date, 'YYYY-MM')       AS year_month,
  LAST_DAY(MIN(sale_date))             AS month_end,
  COUNT(*)                              AS record_count,
  SUM(amount)                           AS total_amount
FROM sales_data
GROUP BY TO_CHAR(sale_date, 'YYYY-MM')
ORDER BY year_month;

注意:BETWEENで日付範囲を指定する場合、日付列に時刻情報が含まれていると月末23:59:59のデータが漏れる場合があります。時刻付きの場合は LAST_DAY(SYSDATE) + 1 - 1/86400< LAST_DAY(SYSDATE) + 1 のように指定しましょう。

他RDBMSとの比較

月末取得の方法はRDBMSによって異なります。Oracle以外のデータベースでの書き方を比較します。

RDBMS 関数 使用例
Oracle LAST_DAY(date) LAST_DAY(SYSDATE)
MySQL LAST_DAY(date) LAST_DAY(CURDATE())
PostgreSQL 直接的な関数なし (date_trunc('month', CURRENT_DATE) + interval '1 month - 1 day')::date
SQL Server EOMONTH(date) EOMONTH(GETDATE())
SQLite 直接的な関数なし date(date, 'start of month', '+1 month', '-1 day')

MySQL LAST_DAY

MySQLにもOracle同名のLAST_DAY関数があり、使い方はほぼ同じです。

MySQL
-- MySQL: 月末を取得
SELECT LAST_DAY(CURDATE()) AS month_end;

-- MySQL: 前月末を取得
SELECT LAST_DAY(CURDATE() - INTERVAL 1 MONTH) AS prev_month_end;

PostgreSQL

PostgreSQLにはLAST_DAY関数がないため、date_truncと日付演算で代用します。

PostgreSQL
-- PostgreSQL: 月末を取得
SELECT
  (date_trunc('month', CURRENT_DATE)
   + INTERVAL '1 month'
   - INTERVAL '1 day')::date AS month_end;

SQL Server EOMONTH

SQL Server 2012以降ではEOMONTH関数が使えます。第2引数でオフセット月数を指定できるのが便利です。

SQL Server
-- SQL Server: 月末を取得
SELECT EOMONTH(GETDATE()) AS month_end;

-- SQL Server: 前月末(第2引数でオフセット)
SELECT EOMONTH(GETDATE(), -1) AS prev_month_end;

ポイント:SQL ServerのEOMONTHは第2引数でオフセットを指定できる点がOracleのLAST_DAYと異なります。OracleではADD_MONTHSと組み合わせる必要がある処理を、SQL Serverでは1関数で完結できます。

よくあるエラーと対処法

LAST_DAY関数を使う際に発生しやすいエラーと、その対処法をまとめます。

エラー 原因 対処法
ORA-01841 文字列を日付に変換できない TO_DATEで正しいフォーマットを指定
ORA-01843 無効な月が指定された 月の値が1〜12であることを確認
ORA-01858 数値が必要な位置に非数値文字 日付文字列のフォーマット確認
NULL返却 引数がNULL NVLでデフォルト値を設定
時刻ずれ 比較時に時刻部分が不一致 TRUNCで時刻を切り捨てて比較

エラー例1: 文字列を直接渡す

NGパターン
-- NLS_DATE_FORMATに依存するNG例
SELECT LAST_DAY('2025-03-15') FROM DUAL;
-- → ORA-01841 になる可能性あり
OKパターン
-- TO_DATEで明示的に変換するOK例
SELECT LAST_DAY(TO_DATE('2025-03-15', 'YYYY-MM-DD')) FROM DUAL;

エラー例2: NULL引数の処理

SQL
-- NULLの場合にデフォルト値を設定
SELECT
  LAST_DAY(NVL(target_date, SYSDATE)) AS month_end
FROM some_table;

エラー例3: 月末の時刻問題

SQL
-- NG: 時刻情報があると不一致になる場合
SELECT * FROM orders
WHERE order_date = LAST_DAY(SYSDATE);  -- 時刻が一致しない!

-- OK: TRUNCで日付部分のみ比較
SELECT * FROM orders
WHERE TRUNC(order_date) = LAST_DAY(TRUNC(SYSDATE));

実務パターン(月次締め処理等)

実際の業務で頻繁に使われるLAST_DAYの活用パターンをまとめます。

月次締め処理の日付設定

PL/SQL
-- 月次締め処理: 前月分データの集計
DECLARE
  v_start_date DATE;  -- 前月1日
  v_end_date   DATE;  -- 前月末日
  v_close_ym   VARCHAR2(7); -- 締め年月
BEGIN
  -- 前月の期間を算出
  v_start_date := TRUNC(ADD_MONTHS(SYSDATE, -1), 'MM');
  v_end_date   := LAST_DAY(ADD_MONTHS(SYSDATE, -1));
  v_close_ym   := TO_CHAR(v_start_date, 'YYYY-MM');

  -- 月次集計テーブルにINSERT
  INSERT INTO monthly_summary (
    close_ym, start_date, end_date, total_sales, record_count
  )
  SELECT
    v_close_ym,
    v_start_date,
    v_end_date,
    SUM(amount),
    COUNT(*)
  FROM sales_data
  WHERE sale_date BETWEEN v_start_date AND v_end_date;

  COMMIT;
  DBMS_OUTPUT.PUT_LINE(v_close_ym || ' の月次締め完了');
END;
/

支払期限の算出(月末締め翌月末払い)

SQL
-- 月末締め翌月末払い: 支払期限を算出
SELECT
  invoice_no,
  invoice_date,
  LAST_DAY(invoice_date) AS closing_date,
  LAST_DAY(ADD_MONTHS(invoice_date, 1)) AS payment_due
FROM invoices;

実行結果

INVOICE_NO  INVOICE_DATE  CLOSING_DATE  PAYMENT_DUE
----------  ------------  ------------  -----------
INV-001     2025-03-10    2025-03-31    2025-04-30
INV-002     2025-03-25    2025-03-31    2025-04-30
INV-003     2025-04-05    2025-04-30    2025-05-31

月末締め翌月25日払い

SQL
-- 月末締め翌月25日払い
SELECT
  invoice_no,
  invoice_date,
  LAST_DAY(invoice_date) AS closing_date,
  TRUNC(ADD_MONTHS(invoice_date, 1), 'MM') + 24 AS payment_due
FROM invoices;

四半期末の取得

SQL
-- 四半期末(3月末, 6月末, 9月末, 12月末)を取得
SELECT
  SYSDATE AS today,
  LAST_DAY(TRUNC(SYSDATE, 'Q')
    + INTERVAL '2' MONTH) AS quarter_end
FROM DUAL;

実行結果(2025-03-15の場合)

TODAY       QUARTER_END
----------  -----------
2025-03-15  2025-03-31

月末スナップショットテーブルへのINSERT

SQL
-- 月末時点の残高スナップショット
INSERT INTO balance_snapshot (
  snapshot_date, account_id, balance
)
SELECT
  LAST_DAY(SYSDATE),
  account_id,
  current_balance
FROM accounts
WHERE status = 'ACTIVE';

まとめ

OracleのLAST_DAY関数は、月末日の取得に特化したシンプルかつ強力な関数です。この記事の要点を振り返りましょう。

用途 SQL
当月末 LAST_DAY(SYSDATE)
前月末 LAST_DAY(ADD_MONTHS(SYSDATE, -1))
翌月末 LAST_DAY(ADD_MONTHS(SYSDATE, 1))
月初日 TRUNC(SYSDATE, 'MM')
月の日数 TO_NUMBER(TO_CHAR(LAST_DAY(SYSDATE), 'DD'))
月末判定 TRUNC(SYSDATE) = LAST_DAY(TRUNC(SYSDATE))
月末までの残日数 LAST_DAY(SYSDATE) - TRUNC(SYSDATE)
四半期末 LAST_DAY(TRUNC(SYSDATE, 'Q') + INTERVAL '2' MONTH)

実務で押さえておくべきポイント

  • 日付比較時はTRUNCで時刻部分を除去してから比較する
  • 文字列を渡す場合は必ずTO_DATEで明示的に変換する
  • LAST_DAYはうるう年を自動判定するため手動判定は不要
  • ADD_MONTHSと組み合わせて前月末・翌月末を取得する
  • BETWEENで時刻付き日付を扱う場合は範囲漏れに注意する
  • 他RDBMSへの移行時は関数名の違いに注意する(PostgreSQLにはLAST_DAYがない)