Java / LESSON 17 / JV17

Java:日付とユーティリティ

1学習の目的

2基礎解説

日付の計算は驚くほど間違えやすい処理です。月末、うるう年、月をまたぐ計算——自分で実装すると必ずどこかで破綻します。LocalDate はこれらをすべて正しく処理してくれます。

クラス扱うもの
LocalDate日付のみ2026-09-08
LocalDateTime日付+時刻2026-09-08T14:30
DateTimeFormatter表示の書式2026/09/08
ChronoUnit期間の計算日数差など
✅ 覚えるべき重要ポイント
  1. LocalDate は変更できない(イミュータブル)。d.plusDays(7)新しい日付を返すだけで、元の d は変わらない——結果を受け取らないと意味がない。
  2. 月またぎ・うるう年は自動で処理される1月31日.plusMonths(1) は2月28日(または29日)になる。自分で計算しようとしてはいけない
  3. 日数差は ChronoUnit.DAYS.between(開始, 終了)。「納期まであと何日か」といった計算が1行で書ける。
  4. 表示の書式は DateTimeFormatteryyyy/MM/dd のように指定する。MM は月、mm は分——大文字小文字を間違えると意図しない結果になる。
  5. 存在しない日付は例外になるLocalDate.of(2026, 2, 30)DateTimeException で拒否される——不正なデータが入り込まない

現場使用例:納期の算出、有効期限の判定、月次レポートの期間指定、経過日数の計算、ログの日時記録。

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

// ① 日付の生成
LocalDate d = LocalDate.of(2026, 9, 8);
LocalDate today = LocalDate.now();          // 今日

// ② 部品の取り出し
d.getYear();          // 2026
d.getMonthValue();    // 9
d.getDayOfMonth();    // 8
d.getDayOfWeek();     // TUESDAY

// ③ 計算(元は変わらない)
LocalDate week  = d.plusDays(7);       // 2026-09-15
LocalDate month = d.plusMonths(1);     // 2026-10-08
LocalDate last  = d.minusYears(1);     // 2025-09-08

// ④ 日数差
long days = ChronoUnit.DAYS.between(d, LocalDate.of(2026, 12, 31));  // 114

// ⑤ 比較
d.isBefore(other);    // より前か
d.isAfter(other);     // より後か
d.isEqual(other);     // 同じ日か

// ⑥ 書式(MM は月、mm は分)
DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy/MM/dd");
String text = d.format(f);                  // "2026/09/08"

// ⑦ 文字列から日付へ
LocalDate parsed = LocalDate.parse("2026-01-15");

// ⑧ 月末日を求める
d.withDayOfMonth(d.lengthOfMonth());        // 2026-09-30

// ⑨ Math の便利メソッド
Math.round(3.7);      // 4(四捨五入)
Math.max(3, 7);       // 7
Math.abs(-5);         // 5
Math.pow(2, 10);      // 1024.0

// ⑩ String.format の書式
String.format("%,d", 1234567);     // "1,234,567"
String.format("%.2f", 3.14159);    // "3.14"
String.format("%5d", 42);          // "   42"(右寄せ5桁)

// ⑪ StringBuilder(大量の文字列連結に)
StringBuilder sb = new StringBuilder();
for (int i = 1; i <= 3; i++) {
    sb.append(i).append(",");
}
sb.toString();                     // "1,2,3,"

3基本ドリル(10問)

JV17-D01 2026年9月8日の LocalDate を作り、「2026年9月8日」の形式で出力せよ。 コーディング★☆☆無料
期待される結果

出力 == 2026年9月8日

ヒント

LocalDate.of(2026, 9, 8) で作り、getYear などで取り出す。

模範解答
import java.time.*;

public class Main {
    public static void main(String[] args) {
        LocalDate d = LocalDate.of(2026, 9, 8);

        System.out.println(d.getYear() + "年" + d.getMonthValue()
                + "月" + d.getDayOfMonth() + "日");
    }
}
解説

月は1〜12でそのまま指定できる。古い Date クラスでは0始まりで混乱の元だったが、LocalDate では直感的になった。

JV17-D02 d.plusDays(7) を実行すると、元の d はどうなるか。
A. 7日後に変わる B. 変わらない C. null になる D. エラー
選択★☆☆無料
期待される結果

解答 == B

ヒント

LocalDate はイミュータブル。

模範解答
B
解説

新しい日付が返されるだけd = d.plusDays(7); のように受け取らないと、計算結果が捨てられる。

JV17-D03 2026年9月8日の7日後1ヶ月後を出力せよ(2行)。 コーディング★☆☆無料
期待される結果

出力2行。2026-09-15 / 2026-10-08

ヒント

plusDays と plusMonths を使う。

模範解答
import java.time.*;

public class Main {
    public static void main(String[] args) {
        LocalDate d = LocalDate.of(2026, 9, 8);

        System.out.println(d.plusDays(7));
        System.out.println(d.plusMonths(1));
    }
}
解説

月またぎも自動で処理される。9月8日の1ヶ月後は10月8日——日数ではなく「月」として加算されている。

JV17-D04 日付を作るメソッドを補ってコードを完成させよ。 穴埋め★☆☆無料
コード
import java.time.*;

public class Main {
    public static void main(String[] args) {
        LocalDate d = LocalDate.____(2026, 1, 15);

        System.out.println(d);
    }
}
期待される結果

出力 == 2026-01-15

ヒント

「〜の」を意味する2文字。Path.of と同じ。

模範解答
of
解説

of は「指定した値の」という意味。Javaの新しいAPIでは、この命名が広く使われている。

JV17-D05 DateTimeFormatter.ofPattern("yyyy/MM/dd")MM は何を表すか。
A. 分 B. 月 C. ミリ秒 D. 曜日
選択★☆☆無料
期待される結果

解答 == B

ヒント

小文字の mm は分。

模範解答
B
解説

大文字MMが月、小文字mmが分。間違えると「2026/30/08」のような意味不明な表示になる——実務でよくある間違い。

JV17-D06 2026年9月8日から2026年12月31日までの日数を計算して出力せよ。 コーディング★★☆広告解放
期待される結果

出力 == 114

ヒント

ChronoUnit.DAYS.between(開始, 終了) を使う。

模範解答
import java.time.*;
import java.time.temporal.ChronoUnit;

public class Main {
    public static void main(String[] args) {
        LocalDate from = LocalDate.of(2026, 9, 8);
        LocalDate to = LocalDate.of(2026, 12, 31);

        System.out.println(ChronoUnit.DAYS.between(from, to));
    }
}
解説

月の日数差やうるう年を意識しなくてよい。自分で計算しようとすると、ほぼ確実にどこかで間違える。

JV17-D07 日付を指定の書式で表示するコードを完成させよ。 穴埋め★★☆広告解放
コード
import java.time.*;
import java.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) {
        LocalDate d = LocalDate.of(2026, 9, 8);
        DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy/MM/dd");

        System.out.println(d.____(f));
    }
}
期待される結果

出力 == 2026/09/08

ヒント

「書式化する」を意味する6文字のメソッド。

模範解答
format
解説

format で表示用の文字列に変換する。逆に文字列から日付を作るなら LocalDate.parse を使う。

JV17-D08 LocalDate.of(2026, 2, 30) を実行するとどうなるか。
A. 3月2日になる B. 例外が発生する C. 2月28日になる D. null
選択★★☆広告解放
期待される結果

解答 == B

ヒント

2月30日は存在しない。

模範解答
B
解説

DateTimeException で拒否される。不正な日付がシステムに入り込むのを、生成時点で防いでくれる。

JV17-D09 StringBuilder を使い、1から3までをカンマ区切りで連結して出力せよ。 コーディング★★☆広告解放
期待される結果

出力 == 1,2,3,

ヒント

append をつなげて書ける。最後に toString() する。

模範解答
public class Main {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();

        for (int i = 1; i <= 3; i++) {
            sb.append(i).append(",");
        }

        System.out.println(sb.toString());
    }
}
解説

ループ内で += を使うと、毎回新しい文字列が作られて非効率。数千回の連結では、StringBuilder が圧倒的に速い。

JV17-D10 Math の各メソッドを使い、四捨五入・最大値・絶対値を1行で出力せよ(3.7 → 4、max(3,7) → 7、abs(-5) → 5)。 コーディング★★☆広告解放
期待される結果

出力 == 4 / 7 / 5

ヒント

Math.round、Math.max、Math.abs を使う。

模範解答
public class Main {
    public static void main(String[] args) {
        System.out.println(Math.round(3.7) + " / "
                + Math.max(3, 7) + " / "
                + Math.abs(-5));
    }
}
解説

Math は import が不要(java.lang パッケージのため)。他にも minpowsqrt などがある。

4実践シナリオ(5問)

JV17-S01 【納期の計算】注文日2026年9月8日から5営業日後(単純に5日後とする)を「納期:2026/09/13」の形式で出力せよ。 コーディング★★☆広告解放
期待される結果

出力 == 納期:2026/09/13

ヒント

plusDays(5) して format する。

模範解答
import java.time.*;
import java.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) {
        LocalDate order = LocalDate.of(2026, 9, 8);
        LocalDate due = order.plusDays(5);

        DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy/MM/dd");
        System.out.println("納期:" + due.format(f));
    }
}
解説

実際の営業日計算は土日祝を除く必要があるが、まずは単純な日数加算から。祝日は国ごとに違うので、外部データが必要になる。

JV17-S02 【有効期限の判定】期限2026年12月31日に対し、基準日2026年9月8日が期限内かどうか残り日数を2行出力せよ。 コーディング★★☆広告解放
期待される結果

出力2行。期限内です / 残り114日

ヒント

isBefore で判定し、ChronoUnit.DAYS.between で日数を求める。

模範解答
import java.time.*;
import java.time.temporal.ChronoUnit;

public class Main {
    public static void main(String[] args) {
        LocalDate today = LocalDate.of(2026, 9, 8);
        LocalDate expiry = LocalDate.of(2026, 12, 31);

        System.out.println(today.isBefore(expiry) ? "期限内です" : "期限切れです");
        System.out.println("残り" + ChronoUnit.DAYS.between(today, expiry) + "日");
    }
}
解説

期限管理は業務システムの定番処理。ポイントの失効、契約の更新、在庫の賞味期限——どれも同じ形で書ける。

JV17-S03 【月末日の算出】2026年9月の月末日と、2026年2月の月末日を2行出力せよ(うるう年の判定も自動)。 コーディング★★☆広告解放
期待される結果

出力2行。2026-09-30 / 2026-02-28

ヒント

withDayOfMonth(lengthOfMonth()) で月末が求まる。

模範解答
import java.time.*;

public class Main {
    public static void main(String[] args) {
        LocalDate sep = LocalDate.of(2026, 9, 1);
        LocalDate feb = LocalDate.of(2026, 2, 1);

        System.out.println(sep.withDayOfMonth(sep.lengthOfMonth()));
        System.out.println(feb.withDayOfMonth(feb.lengthOfMonth()));
    }
}
解説

うるう年の判定が不要。2026年は平年なので2月は28日、2028年なら29日が自動的に返る。

JV17-S04 【全角文字の桁ずれ】%-8s で商品名を左寄せし、3件を出力せよ。日本語では見た目が揃わないことを確認すること(末尾に | を付けて確認)。 コーディング★★★広告解放
期待される結果

出力3行。文字数は揃うが表示幅はずれる

ヒント

String.format("%-8s|", 名前) で出力する。全角は1文字として数えられる。

模範解答
public class Main {
    public static void main(String[] args) {
        String[] names = {"ノートPC", "マウス", "モニター"};

        StringBuilder sb = new StringBuilder();
        for (String name : names) {
            sb.append(String.format("%-8s|%n", name));
        }

        System.out.print(sb.toString());
    }
}
解説

「ノートPC」は5文字、「マウス」は3文字——文字数では揃っているのに、表示は揃わない。全角文字は半角の約2倍の幅で表示されるためだ。%-8s文字数で数えるので、日本語混じりの表では桁がずれる。等幅フォントで表を作るなら、全角を2文字分として自分で計算する必要がある——日本語を扱う開発者が必ず遭遇する問題

JV17-S05 【日付の解析と再整形】文字列 "2026-01-15" を日付に変換し、「2026年1月15日(木)」の形式で出力せよ。 コーディング★★★広告解放
期待される結果

出力 == 2026年1月15日(木)

ヒント

LocalDate.parse で変換し、DateTimeFormatter.ofPattern("yyyy年M月d日(E)", Locale.JAPAN) で整形する。

模範解答
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        LocalDate d = LocalDate.parse("2026-01-15");

        DateTimeFormatter f = DateTimeFormatter.ofPattern(
                "yyyy年M月d日(E)", Locale.JAPAN);

        System.out.println(d.format(f));
    }
}
解説

Locale.JAPAN を指定すると曜日が日本語になる。指定しないと環境によって「Thu」と英語で出るため、明示するのが安全。

5仕上げ課題

JV17-FINAL 【注文管理レポート】日付とユーティリティを組み合わせて、注文一覧を出力せよ。

Order クラス:name(商品名)、orderDate(LocalDate)、price(int)、qty(int)

データ(基準日は2026年9月8日)
・ノートPC / 2026-09-01 / 128000 / 2
・マウス / 2026-08-15 / 3200 / 10
・モニター / 2026-09-05 / 45000 / 1

処理
① 各注文を「ノートPC 2026/09/01 256,000円(7日経過)」の形式で出力
 ・商品名は左寄せ10桁、金額は3桁区切り
 ・経過日数は注文日から基準日までを計算
納期は注文日の14日後とし、基準日時点で期限を過ぎた注文に「※要確認」を付ける
③ 合計金額を出力
最も古い注文の日付を「最古:2026年8月15日」の形式で出力

期待される出力(5行)
ノートPC 2026/09/01 256,000円(7日経過)
マウス 2026/08/15 32,000円(24日経過)※要確認
モニター 2026/09/05 45,000円(3日経過)
---
合計:333,000円 / 最古:2026年8月15日


※ 商品名の桁がずれて見えるのは正常です(S04で確認した全角の問題)
総合★★★広告解放
期待される結果

出力5行が完全一致

ヒント

%-10s で左寄せ、%,9d で右寄せ3桁区切り。経過日数は ChronoUnit.DAYS.between(注文日, 基準日)。納期超過は 注文日.plusDays(14).isBefore(基準日) で判定。最古は Comparator で比較するか、ループで更新する。

模範解答
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;

public class Main {

    static class Order {
        String name;
        LocalDate orderDate;
        int price;
        int qty;

        Order(String name, LocalDate orderDate, int price, int qty) {
            this.name = name;
            this.orderDate = orderDate;
            this.price = price;
            this.qty = qty;
        }

        int amount() {
            return price * qty;
        }
    }

    public static void main(String[] args) {
        LocalDate baseDate = LocalDate.of(2026, 9, 8);

        List<Order> orders = List.of(
                new Order("ノートPC", LocalDate.of(2026, 9, 1), 128000, 2),
                new Order("マウス", LocalDate.of(2026, 8, 15), 3200, 10),
                new Order("モニター", LocalDate.of(2026, 9, 5), 45000, 1));

        DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy/MM/dd");

        int total = 0;
        LocalDate oldest = orders.get(0).orderDate;

        StringBuilder sb = new StringBuilder();

        for (Order o : orders) {
            long days = ChronoUnit.DAYS.between(o.orderDate, baseDate);
            boolean overdue = o.orderDate.plusDays(14).isBefore(baseDate);

            sb.append(String.format("%-10s%s %,9d円(%d日経過)%s%n",
                    o.name,
                    o.orderDate.format(f),
                    o.amount(),
                    days,
                    overdue ? "※要確認" : ""));

            total += o.amount();

            if (o.orderDate.isBefore(oldest)) {
                oldest = o.orderDate;
            }
        }

        System.out.print(sb.toString());

        DateTimeFormatter jp = DateTimeFormatter.ofPattern("yyyy年M月d日");

        System.out.println("---");
        System.out.println("合計:" + String.format("%,d", total)
                + "円 / 最古:" + oldest.format(jp));
    }
}
解説

このレポートには、実務の日付処理が凝縮されている。経過日数の計算、納期超過の判定、最古日付の抽出——どれも業務システムで日常的に必要になる処理だ。

もし日付を「20260901」のような数値で管理していたらどうなるか。経過日数を求めるには、月の日数やうるう年を自分で計算する必要がある。8月15日から9月8日まで何日か——8月は31日だから16日+8日で24日、と手計算するコードを書くことになる。LocalDate なら1行で済み、しかも間違えない。

%,9d による数値の右寄せは有効に働いている。桁数が揃うので、256,000 と 32,000 の大小が一目で分かる。一方 %-10s のほうは、S04で見たとおり全角文字のせいで見た目が揃わない。日本語を含む表を等幅で整形したいなら、全角を2文字分として数える処理を自分で書く必要がある——Javaの標準機能だけでは解決できない、日本語環境特有の課題だ。

StringBuilder を使った理由も実務的だ。3件なら差は無いが、1万件の明細を出力するなら性能差が歴然と現れる。文字列を += でつなぐと、毎回新しい文字列が作られるためだ。

次章がいよいよ最終回だ。JV01からここまでの17章すべてを統合し、在庫管理システムを完成させる。