Java Tips | 日付・時間:日付丸め

Java Java
スポンサーリンク

「日付丸め」とは何をするものか

「2025-03-26 を“月初”にそろえたい」「日付を“週単位”にまとめたい」「分析用に“日単位”までに丸めたい」
こういうときに出てくるのが「日付丸め」です。

日付丸めは、“ある日付を、指定した単位(1日・1週間・1ヶ月・1年など)の“きりのいいところ”に揃える処理” です。
時間丸めと同じく、切り捨て(floor)、切り上げ(ceil)、四捨五入(round)を意識すると整理しやすくなります。

情報セキュリティの観点では、
「ログの日時を“日単位”や“月単位”に丸めて保存し、個人の行動を細かく追えないようにする」
といった“匿名化・マスキング”のテクニックとしても使われます。


日単位の丸め(LocalDateTime → LocalDate)

時刻を落として「日付だけ」にする

まず一番基本の「日単位への丸め」です。
LocalDateTime から「日付だけ」を取り出すと、それは「その日の 0:00 に丸めた」と同じ意味になります。

import java.time.LocalDate;
import java.time.LocalDateTime;

public class DateFloorToDay {

    public static void main(String[] args) {
        LocalDateTime dt = LocalDateTime.of(2025, 3, 26, 15, 30, 45);

        LocalDate dateOnly = dt.toLocalDate();              // 2025-03-26
        LocalDateTime startOfDay = dt.toLocalDate().atStartOfDay(); // 2025-03-26T00:00

        System.out.println("元日時      : " + dt);
        System.out.println("日付だけ    : " + dateOnly);
        System.out.println("その日の0時 : " + startOfDay);
    }
}
Java

ここで重要なのは、
「日単位で扱うと決めたら、時刻は切り捨ててしまう」という割り切りです。

ログ分析や集計では、「何日のデータか」だけが重要で、
「何時何分か」は不要なことも多いので、
LocalDate にしてしまうだけで、データの扱いがかなりシンプルになります。


月単位の丸め(“月初”と“月末”)

月初に丸める(firstDayOfMonth)

例:2025-03-26 → 2025-03-01(3 月の月初)

import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;

public class DateFloorToMonth {

    public static void main(String[] args) {
        LocalDate any = LocalDate.of(2025, 3, 26);

        LocalDate monthStart = any.with(TemporalAdjusters.firstDayOfMonth());

        System.out.println("元日付 : " + any);        // 2025-03-26
        System.out.println("月初   : " + monthStart); // 2025-03-01
    }
}
Java

これは「月単位で切り捨てた」と考えられます。
「どの日でも、その月の代表として“月初”に揃える」イメージです。

月末に丸める(lastDayOfMonth)

例:2025-03-26 → 2025-03-31(3 月の月末)

import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;

public class DateCeilToMonth {

    public static void main(String[] args) {
        LocalDate any = LocalDate.of(2025, 3, 26);

        LocalDate monthEnd = any.with(TemporalAdjusters.lastDayOfMonth());

        System.out.println("元日付 : " + any);      // 2025-03-26
        System.out.println("月末   : " + monthEnd); // 2025-03-31
    }
}
Java

これは「月単位で切り上げた」と見ることもできます。
「この月の最後の日」を代表として使いたいときに便利です。

月単位の四捨五入(“月初か月末か”を決める)

例えば「月の前半なら月初、後半なら月末に丸める」というルールも作れます。

import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;

public class DateRoundToMonth {

    public static LocalDate roundToMonth(LocalDate date) {
        LocalDate start = date.with(TemporalAdjusters.firstDayOfMonth());
        LocalDate end   = date.with(TemporalAdjusters.lastDayOfMonth());

        long daysFromStart = java.time.temporal.ChronoUnit.DAYS.between(start, date);
        long daysToEnd     = java.time.temporal.ChronoUnit.DAYS.between(date, end);

        if (daysFromStart <= daysToEnd) {
            return start; // 前半寄り → 月初
        } else {
            return end;   // 後半寄り → 月末
        }
    }

    public static void main(String[] args) {
        System.out.println("2025-03-10 → " + roundToMonth(LocalDate.of(2025, 3, 10)));
        System.out.println("2025-03-25 → " + roundToMonth(LocalDate.of(2025, 3, 25)));
    }
}
Java

ここで深掘りしたいのは、
「丸めは“距離が近い方に寄せる”という発想で書ける」ということです。
月初と月末のどちらに近いかを日数で比べて、近い方に寄せています。


週単位の丸め(週の開始日・終了日)

週の開始日に丸める(例:月曜始まり)

例:2025-03-26(水) → 2025-03-24(月)

import java.time.DayOfWeek;
import java.time.LocalDate;

public class DateFloorToWeek {

    public static LocalDate floorToWeek(LocalDate date, DayOfWeek weekStart) {
        DayOfWeek dow = date.getDayOfWeek();
        int diff = dow.getValue() - weekStart.getValue();
        if (diff < 0) {
            diff += 7;
        }
        return date.minusDays(diff);
    }

    public static void main(String[] args) {
        LocalDate any = LocalDate.of(2025, 3, 26); // 水

        LocalDate weekStart = floorToWeek(any, DayOfWeek.MONDAY);

        System.out.println("元日付 : " + any);       // 2025-03-26
        System.out.println("週の開始: " + weekStart); // 2025-03-24
    }
}
Java

ここでのポイントは、
「“週の開始曜日”をパラメータにしておく」ことです。
日本だと月曜始まり・日曜始まりの両方があり得るので、
固定せずに引数で受け取る設計にしておくと柔軟です。

週の終了日に丸める(例:日曜終わり)

終了日は「開始日+6日」で表現できます。

import java.time.DayOfWeek;
import java.time.LocalDate;

public class DateCeilToWeek {

    public static LocalDate ceilToWeek(LocalDate date, DayOfWeek weekStart) {
        LocalDate start = DateFloorToWeek.floorToWeek(date, weekStart);
        return start.plusDays(6);
    }

    public static void main(String[] args) {
        LocalDate any = LocalDate.of(2025, 3, 26); // 水

        LocalDate weekEnd = ceilToWeek(any, DayOfWeek.MONDAY);

        System.out.println("元日付 : " + any);      // 2025-03-26
        System.out.println("週の終了: " + weekEnd); // 2025-03-30
    }
}
Java

週単位の集計(週次レポートなど)では、
「どの週に属するか」を決めるために、
このような“週の開始日”への丸めがよく使われます。


年単位の丸め(年初・年末)

年初に丸める(firstDayOfYear)

例:2025-03-26 → 2025-01-01

import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;

public class DateFloorToYear {

    public static void main(String[] args) {
        LocalDate any = LocalDate.of(2025, 3, 26);

        LocalDate yearStart = any.with(TemporalAdjusters.firstDayOfYear());

        System.out.println("元日付 : " + any);       // 2025-03-26
        System.out.println("年初   : " + yearStart); // 2025-01-01
    }
}
Java

年末に丸める(lastDayOfYear)

例:2025-03-26 → 2025-12-31

import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;

public class DateCeilToYear {

    public static void main(String[] args) {
        LocalDate any = LocalDate.of(2025, 3, 26);

        LocalDate yearEnd = any.with(TemporalAdjusters.lastDayOfYear());

        System.out.println("元日付 : " + any);     // 2025-03-26
        System.out.println("年末   : " + yearEnd); // 2025-12-31
    }
}
Java

年次集計・年度処理などでは、
「どの年に属するか」を決めるために、
年初・年末への丸めがよく使われます。

会計年度(4 月〜翌年 3 月など)の場合は、
「年度初日」「年度末日」を自前のユーティリティで定義して、
そこに丸めるイメージになります。


セキュリティ観点での「日付丸め」

ログ・分析データの“粒度”を落としてプライバシーを守る

情報セキュリティの観点では、
「日時が細かすぎると、個人の行動パターンが特定されやすくなる」という問題があります。

例えば、
「毎日 23:58 にアクセスしている」「毎週火曜の 7:05 にログインしている」
といった情報は、個人を推測する手がかりになり得ます。

そこで、保存時に日付を丸めてしまうという設計があり得ます。

例:
・日時を「日単位」に丸める(時刻は 0:00 にする)
・「月単位」に丸めて、日付は 1 日にする
・「週単位」に丸めて、週の開始日に揃える

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;

public class DateMasking {

    public static LocalDate maskToDate(LocalDateTime dt) {
        return dt.toLocalDate();
    }

    public static LocalDate maskToMonth(LocalDateTime dt) {
        return dt.toLocalDate().withDayOfMonth(1);
    }

    public static LocalDate maskToWeek(LocalDateTime dt) {
        LocalDate d = dt.toLocalDate();
        return DateFloorToWeek.floorToWeek(d, DayOfWeek.MONDAY);
    }

    public static void main(String[] args) {
        LocalDateTime dt = LocalDateTime.of(2025, 3, 26, 23, 58);

        System.out.println("元日時   : " + dt);
        System.out.println("日単位   : " + maskToDate(dt));   // 2025-03-26
        System.out.println("月単位   : " + maskToMonth(dt));  // 2025-03-01
        System.out.println("週単位   : " + maskToWeek(dt));   // 2025-03-24 (月曜始まり)
    }
}
Java

ここで大事なのは、
「分析に本当に必要な粒度はどこまでか?」を考えたうえで丸めることです。

・セキュリティインシデント調査には“日時”が必要か
・マーケティング分析には“日単位”で十分か
・長期統計には“月単位”で十分か

こうした問いに答えながら、
「どの単位に丸めるか」を決めるのが、セキュリティ設計としての“日付丸め”です。


日付丸めユーティリティとしてまとめる

「単位」と「丸め方」を名前で表現する

日付丸めも、あちこちで同じようなコードを書きがちなので、
ユーティリティクラスにまとめておくと再利用しやすくなります。

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.TemporalAdjusters;

public class DateRoundingUtils {

    public static LocalDate floorToDay(LocalDateTime dt) {
        return dt.toLocalDate();
    }

    public static LocalDate floorToMonth(LocalDate date) {
        return date.with(TemporalAdjusters.firstDayOfMonth());
    }

    public static LocalDate ceilToMonth(LocalDate date) {
        return date.with(TemporalAdjusters.lastDayOfMonth());
    }

    public static LocalDate floorToYear(LocalDate date) {
        return date.with(TemporalAdjusters.firstDayOfYear());
    }

    public static LocalDate ceilToYear(LocalDate date) {
        return date.with(TemporalAdjusters.lastDayOfYear());
    }

    public static LocalDate floorToWeek(LocalDate date, DayOfWeek weekStart) {
        return DateFloorToWeek.floorToWeek(date, weekStart);
    }
}
Java

こうしておけば、呼び出し側は

LocalDate monthKey = DateRoundingUtils.floorToMonth(date);
Java

のように、
「何単位で」「どの方向に」丸めているかが、コードから一目で分かるようになります。


まとめ:日付丸めで身につけてほしい感覚

日付丸めは、「日付を“きりのいい単位”に揃える」処理です。

まず「どの単位で扱うか」(日・週・月・年)を決める。
次に「どの方向に丸めるか」(切り捨て・切り上げ・四捨五入)を決める。
TemporalAdjustersChronoUnit を使うと、月初・月末・年初・年末・日数差などを安全に扱える。
セキュリティ・プライバシーの観点では、「必要以上に細かい日付情報を持たない」ための丸めとしても使える。
共通ユーティリティにして、「単位」と「丸め方」を名前で表現すると、業務コードが読みやすくなる。

もしあなたのコードのどこかに、
「月初を 1 日と決め打ち」「月末を 30 日や 31 日と決め打ち」しているような箇所があれば、
そこを一度 TemporalAdjusters や日付丸めユーティリティに置き換えられないか眺めてみてください。

その小さな置き換えが、
“時間にもセキュリティにも強いエンジニア”への、確かな一歩になります。

タイトルとURLをコピーしました