一、核心概念
LocalDate
是 Java 8 引入的现代化日期 API,位于 java.time
包中,表示一个 不包含时区和时间的纯日期(如 2025-08-16
)。
它提供了多个静态工厂方法来创建 LocalDate
实例,其中最常用的是:
LocalDate.of(year, month, day)
:通过年、月、日创建LocalDate.ofYearDay(year, dayOfYear)
:通过年份和年中的第几天创建
二、LocalDate.of()
方法
1. 方法定义
public static LocalDate of(int year, int month, int dayOfMonth)
public static LocalDate of(int year, Month month, int dayOfMonth)
- 参数:
year
:年份(支持负数表示公元前)month
:月份(1-12),或使用Month
枚举(如Month.AUGUST
)dayOfMonth
:该月中的日期(1-31,根据月份和闰年自动校验)
- 返回值:
LocalDate
实例 - 异常:
DateTimeException
(当日期无效时抛出)
2. 示例代码
import java.time.LocalDate;
import java.time.Month;
public class OfExample {
public static void main(String[] args) {
// 方式1:使用数字表示月份
LocalDate date1 = LocalDate.of(2025, 8, 16);
System.out.println("日期1: " + date1); // 2025-08-16
// 方式2:使用 Month 枚举(推荐,更清晰)
LocalDate date2 = LocalDate.of(2025, Month.AUGUST, 16);
System.out.println("日期2: " + date2); // 2025-08-16
// 其他示例
LocalDate newYear = LocalDate.of(2025, 1, 1); // 新年
LocalDate christmas = LocalDate.of(2025, 12, 25); // 圣诞节
LocalDate leapDay = LocalDate.of(2024, 2, 29); // 闰年2月29日
System.out.println("新年: " + newYear);
System.out.println("圣诞节: " + christmas);
System.out.println("闰日: " + leapDay);
}
}
3. 常见错误
// ❌ 错误1:无效日期(2月30日)
// LocalDate invalid = LocalDate.of(2025, 2, 30);
// 抛出: DateTimeException: Invalid date 'FEBRUARY 30'
// ❌ 错误2:月份超出范围
// LocalDate invalid = LocalDate.of(2025, 13, 1);
// 抛出: DateTimeException: Invalid value for MonthOfYear (valid values 1 - 12): 13
// ❌ 错误3:空的 Month 枚举
// LocalDate invalid = LocalDate.of(2025, null, 1);
// 抛出: NullPointerException
三、LocalDate.ofYearDay()
方法
1. 方法定义
public static LocalDate ofYearDay(int year, int dayOfYear)
- 参数:
year
:年份dayOfYear
:该年中的第几天(1-365 或 1-366)
- 返回值:
LocalDate
实例 - 异常:
DateTimeException
(当dayOfYear
超出范围时)
💡 提示:此方法非常适合处理“年积日”(Day of Year)数据,常见于气象、科学计算等领域。
2. 示例代码
import java.time.LocalDate;
public class OfYearDayExample {
public static void main(String[] args) {
// 获取 2025 年的第 1 天
LocalDate firstDay = LocalDate.ofYearDay(2025, 1);
System.out.println("2025年第1天: " + firstDay); // 2025-01-01
// 获取 2025 年的第 100 天
LocalDate day100 = LocalDate.ofYearDay(2025, 100);
System.out.println("2025年第100天: " + day100); // 2025-04-10
// 获取 2025 年的最后一天(第365天)
LocalDate lastDay = LocalDate.ofYearDay(2025, 365);
System.out.println("2025年第365天: " + lastDay); // 2025-12-31
// 闰年示例
LocalDate leapDay = LocalDate.ofYearDay(2024, 60);
System.out.println("2024年第60天: " + leapDay); // 2024-02-29 (闰年)
// 验证:今天是今年的第几天
LocalDate today = LocalDate.now();
int dayOfYear = today.getDayOfYear();
System.out.println("今天是今年的第 " + dayOfYear + " 天");
// 反向验证:ofYearDay 是否正确
LocalDate fromDay = LocalDate.ofYearDay(today.getYear(), dayOfYear);
System.out.println("验证结果: " + today.equals(fromDay)); // true
}
}
3. 常见错误
// ❌ 错误1:超出非闰年范围
// LocalDate invalid = LocalDate.ofYearDay(2025, 366);
// 抛出: DateTimeException: Invalid dayOfYear: 366 for year: 2025
// ❌ 错误2:超出闰年范围
// LocalDate invalid = LocalDate.ofYearDay(2024, 367);
// 抛出: DateTimeException: Invalid dayOfYear: 367 for year: 2024
// ❌ 错误3:dayOfYear <= 0
// LocalDate invalid = LocalDate.ofYearDay(2025, 0);
// 抛出: DateTimeException: Invalid dayOfYear: 0
四、使用技巧
✅ 技巧 1:结合 getDayOfYear()
使用
LocalDate date = LocalDate.of(2025, 8, 16);
int dayOfYear = date.getDayOfYear(); // 228
LocalDate reconstructed = LocalDate.ofYearDay(2025, dayOfYear);
System.out.println(date.equals(reconstructed)); // true
✅ 技巧 2:计算年中进度
LocalDate today = LocalDate.now();
double progress = (double) today.getDayOfYear() / (today.isLeapYear() ? 366 : 365);
System.out.printf("今年已过去 %.1f%%\n", progress * 100);
✅ 技巧 3:批量生成日期
// 生成某年所有周五
int year = 2025;
for (int day = 1; day <= 365; day++) {
LocalDate d = LocalDate.ofYearDay(year, day);
if (d.getDayOfWeek().getValue() == 5) { // Friday
System.out.println(d);
}
}
五、注意事项
- 不可变性:
LocalDate
是不可变对象,所有操作返回新实例。 - 范围检查:两个方法都会进行严格的日期有效性检查。
- 性能:都是轻量级操作,可频繁调用。
- 线程安全:
LocalDate
是线程安全的,适合在多线程环境使用。
六、最佳实践
✅ 推荐使用 Month
枚举
// 推荐(清晰、避免错误)
LocalDate.of(2025, Month.AUGUST, 16)
// 可接受但易错
LocalDate.of(2025, 8, 16)
✅ 优先使用 ofYearDay()
处理年积日数据
// 当输入是 "2025-228" 这种格式时
LocalDate date = LocalDate.ofYearDay(2025, 228);
✅ 结合 isLeapYear()
判断
LocalDate date = LocalDate.of(2024, 2, 29);
if (date.isLeapYear()) {
System.out.println("是闰年");
}
七、总结对比
方法 | 适用场景 | 参数 | 优点 | 缺点 |
---|---|---|---|---|
of(year, month, day) |
通用创建 | 年、月、日 | 直观、常用 | 月份易错(0-11 vs 1-12) |
ofYearDay(year, dayOfYear) |
年积日处理 | 年、年第几天 | 简洁处理 DOY 数据 | 需知道具体天数 |
💡 终极建议:
在 Java 8+ 项目中,优先使用LocalDate.of()
和ofYearDay()
创建日期,它们比传统的Calendar
和Date
构造函数更安全、更直观、更现代化。