博客时间字段重构:从 Date 到 LocalDateTime(AI 辅助学习 Java 8)

14 阅读4分钟

博客时间字段重构:从 Date 到 LocalDateTime(AI 辅助学习 Java 8)

前两篇学了 LocalDateTime 基础和 DateTimeFormatter。今天在一个完整的博客项目中,把时间字段从旧的 Date 全面升级到 LocalDateTime,实现"今日发布"、"本周发布"、"月度统计"等实用功能。


一、旧版 Date 的问题

旧版实体类

public class BlogPost {
    private Long id;
    private String title;
    private Date createTime;    // java.util.Date
    private Date updateTime;    // java.util.Date
}

旧版打印输出

创建时间: Fri Jun 05 10:30:00 CST 2026
更新时间: Fri Jun 12 15:20:00 CST 2026

四个核心问题

  1. 格式不直观Fri Jun 05 10:30:00 CST 2026 谁看得懂?
  2. 线程不安全Date 是可变对象,SimpleDateFormat 也不是线程安全的
  3. 时区混乱CST 可能指中国标准时间,也可能指美国中部时间
  4. API 贫乏:想获取"本月第一天"需要大量 Calendar 操作

二、新版 LocalDateTime 实体类

public class BlogPost {
    private final Long id;
    private final String title;
    private final LocalDateTime createTime;
    private final LocalDateTime updateTime;

    BlogPost(Long id, String title, LocalDateTime createTime, LocalDateTime updateTime) {
        this.id = id;
        this.title = title;
        this.createTime = createTime;
        this.updateTime = updateTime;
    }
    // getter 省略
}

新版打印输出

创建: 2026-06-05 10:30
更新: 2026-06-12 15:20

四大优势:不可变、线程安全、格式清晰、API 丰富。

三、今日发布

LocalDate today = LocalDate.now();

List<BlogPost> todayPosts = blogs.stream()
        .filter(b -> b.getCreateTime().toLocalDate().equals(today))
        .collect(Collectors.toList());

System.out.println("今日发布: " + todayPosts.size() + " 篇");

一行 filter 搞定,不需要手动比较毫秒级时间戳。

四、本周发布

LocalDate weekStart = today.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));

List<BlogPost> weekPosts = blogs.stream()
        .filter(b -> !b.getCreateTime().toLocalDate().isBefore(weekStart))
        .collect(Collectors.toList());

System.out.println("本周发布 (从 " + weekStart + " 起): " + weekPosts.size() + " 篇");

TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY) 一行算出本周一,不需要判断今天是星期几再减天数。

五、本月发布

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

List<BlogPost> monthPosts = blogs.stream()
        .filter(b -> !b.getCreateTime().toLocalDate().isBefore(monthStart))
        .collect(Collectors.toList());

同样一行搞定本月第一天。

六、N 天内发布

List<BlogPost> recentPosts = blogs.stream()
        .filter(b -> ChronoUnit.DAYS.between(b.getCreateTime().toLocalDate(), today) <= 7)
        .collect(Collectors.toList());

ChronoUnit.DAYS.betweenDate.getTime() 除以 86400000 清晰得多。

七、月度发文统计

Map<String, Long> monthlyCount = blogs.stream()
        .collect(Collectors.groupingBy(
                b -> b.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM")),
                Collectors.counting()
        ));
// 输出: {2026-05=2, 2026-06=5}

按月份格式化分组,一行完成统计。

八、按星期统计

Map<DayOfWeek, Long> dayOfWeekCount = blogs.stream()
        .collect(Collectors.groupingBy(
                b -> b.getCreateTime().getDayOfWeek(),
                Collectors.counting()
        ));
// 输出: {WEDNESDAY=1, MONDAY=2, FRIDAY=3, SATURDAY=1}

直接调用 getDayOfWeek() 获取枚举值,分组结果一目了然。

九、发文周期计算

LocalDateTime earliest = blogs.stream()
        .map(BlogPost::getCreateTime)
        .min(LocalDateTime::compareTo)
        .orElse(LocalDateTime.now());
LocalDateTime latest = blogs.stream()
        .map(BlogPost::getCreateTime)
        .max(LocalDateTime::compareTo)
        .orElse(LocalDateTime.now());

long totalDays = ChronoUnit.DAYS.between(earliest.toLocalDate(), latest.toLocalDate());
double avgDays = totalDays * 1.0 / (blogs.size() - 1);
System.out.println(String.format("发文周期: 平均每 %.1f 天发一篇", avgDays));
// 输出: 发文周期: 平均每 4.8 天发一篇

十、最近更新

BlogPost lastUpdated = blogs.stream()
        .max((a, b) -> a.getUpdateTime().compareTo(b.getUpdateTime()))
        .orElse(null);

DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
System.out.println("最近更新: " + lastUpdated.getTitle()
        + " (" + lastUpdated.getUpdateTime().format(fmt) + ")");
// 输出: 最近更新: 今日新文章 (2026-06-13 07:00)

十一、运行结果

=== 旧版 Date 方式(模拟) ===
  创建时间: Fri Jun 05 10:30:00 CST 2026
  更新时间: Fri Jun 12 15:20:00 CST 2026
  问题: 格式不直观、时区混乱、线程不安全、可变对象

=== 新版 LocalDateTime 方式 ===
  标题: Lambda 入门
  创建: 2026-06-05 10:30
  更新: 2026-06-12 15:20
  优势: 不可变、线程安全、格式清晰、API 丰富

=== 博客时间查询 ===
  今日发布 (2026-06-13): 1 
    - 今日新文章
  本周发布 (从 2026-06-08 起): 4 
  本月发布 (从 2026-06-01 起): 5 
  7天内发布: 4 

=== 博客时间统计 ===
  月度发文: {2026-05=2, 2026-06=5}
  按星期统计: {WEDNESDAY=1, MONDAY=2, FRIDAY=3, SATURDAY=1}
  发文最多的月份: 2026-06
  发文周期: 平均每 4.8 天发一篇
  最近更新: 今日新文章 (2026-06-13 07:00)

十二、Date → LocalDateTime 迁移对照表

旧版(Date)新版(LocalDateTime)
new Date()LocalDateTime.now()
new Date(2026-1900, 5, 13)LocalDateTime.of(2026, 6, 13, 0, 0)
SimpleDateFormat.parse()LocalDateTime.parse()
date.getTime()ChronoUnit.MILLIS.between(a, b)
Calendar.add(Calendar.DAY, 1)dateTime.plusDays(1)
Calendar.getFirstDayOfMonth()date.with(TemporalAdjusters.firstDayOfMonth())

十三、本周学习总结

第六周我们学习了 Java 8 新时间 API,完整路线:

周二:为什么 Date/Calendar 该被淘汰?LocalDateTime 详解
周四:DateTimeFormatter 自定义格式、时区处理
周六:博客时间字段重构:从 DateLocalDateTime(本篇)

核心收获

场景用哪个
只需要日期LocalDate
只需要时间LocalTime
日期+时间LocalDateTime
带时区ZonedDateTime
自定义格式DateTimeFormatter.ofPattern()
可选部分DateTimeFormatterBuilder
本月第一天with(TemporalAdjusters.firstDayOfMonth())
下周一with(TemporalAdjusters.next(DayOfWeek.MONDAY))
相差天数ChronoUnit.DAYS.between(a, b)
时区转换withZoneSameInstant()

关键原则

  • 所有时间对象不可变,操作返回新实例
  • DateTimeFormatter线程安全的,可定义为常量
  • TemporalAdjusters 一行搞定各种"某天"计算
  • ChronoUnit.between 替代毫秒级除法

十四、下周预告

第七周进入 方法引用 & 构造器引用,用 :: 双冒号让 Lambda 表达式更简洁。


系列文章导航

更多请关注我的微信公众号,感谢!

微信图片_20260427132414_18_737.jpg