2. 方法列表与功能说明
方法 | 功能 |
---|---|
withDayOfMonth(int dayOfMonth) |
修改为指定的月内日期 |
withDayOfYear(int dayOfYear) |
修改为指定的年内日期(1-365/366) |
withMonth(int month) |
修改为指定的月份(1-12) |
withYear(int year) |
修改为指定的年份 |
返回类型:
LocalDate
(新实例)
3. 详细操作步骤
3.1 withDayOfMonth(int dayOfMonth)
步骤 1:获取原始 LocalDate
import java.time.LocalDate;
LocalDate original = LocalDate.of(2025, 8, 15);
System.out.println("原始日期: " + original); // 2025-08-15
步骤 2:修改日(月内)
// 修改为当月1日
LocalDate firstDay = original.withDayOfMonth(1);
System.out.println("当月1日: " + firstDay); // 2025-08-01
// 修改为当月最后一天(先获取当月天数)
int lastDay = original.lengthOfMonth(); // 8月有31天
LocalDate lastDayOfMonth = original.withDayOfMonth(lastDay);
System.out.println("当月最后一天: " + lastDayOfMonth); // 2025-08-31
步骤 3:验证原对象不变
System.out.println("原始日期仍为: " + original); // 2025-08-15
3.2 withDayOfYear(int dayOfYear)
步骤 1:获取原始 LocalDate
LocalDate original = LocalDate.of(2025, 8, 15);
System.out.println("原始日期: " + original); // 2025-08-15
步骤 2:修改为年内第N天
// 修改为年内第1天(1月1日)
LocalDate firstDayOfYear = original.withDayOfYear(1);
System.out.println("年内第1天: " + firstDayOfYear); // 2025-01-01
// 修改为年内第100天
LocalDate day100 = original.withDayOfYear(100);
System.out.println("年内第100天: " + day100); // 2025-04-10
// 修改为年内最后一天
int lastDayOfYear = original.lengthOfYear(); // 2025年有365天
LocalDate lastDay = original.withDayOfYear(lastDayOfYear);
System.out.println("年内最后一天: " + lastDay); // 2025-12-31
步骤 3:验证原对象不变
System.out.println("原始日期仍为: " + original); // 2025-08-15
3.3 withMonth(int month)
步骤 1:获取原始 LocalDate
LocalDate original = LocalDate.of(2025, 8, 15);
System.out.println("原始日期: " + original); // 2025-08-15
步骤 2:修改月份
// 修改为1月
LocalDate january = original.withMonth(1);
System.out.println("1月15日: " + january); // 2025-01-15
// 修改为12月
LocalDate december = original.withMonth(12);
System.out.println("12月15日: " + december); // 2025-12-15
// 使用 Month 枚举(更清晰)
import java.time.Month;
LocalDate march = original.withMonth(Month.MARCH.getValue());
System.out.println("3月15日: " + march); // 2025-03-15
步骤 3:处理跨月日溢出
// 原日期是1月31日,改为2月
LocalDate jan31 = LocalDate.of(2025, 1, 31);
// LocalDate feb31 = jan31.withMonth(2);
// 抛出 DateTimeException: Invalid date 'FEBRUARY 31'
// 正确做法:先调整日,再调整月
LocalDate febLast = jan31.withDayOfMonth(28).withMonth(2);
System.out.println("2月最后一天: " + febLast); // 2025-02-28
3.4 withYear(int year)
步骤 1:获取原始 LocalDate
LocalDate original = LocalDate.of(2025, 8, 15);
System.out.println("原始日期: " + original); // 2025-08-15
步骤 2:修改年份
// 修改为2023年
LocalDate past = original.withYear(2023);
System.out.println("2023年8月15日: " + past); // 2023-08-15
// 修改为未来年份
LocalDate future = original.withYear(2030);
System.out.println("2030年8月15日: " + future); // 2030-08-15
步骤 3:处理闰年2月29日
// 原日期是闰年2月29日
LocalDate leapDay = LocalDate.of(2024, 2, 29);
System.out.println("闰年2月29日: " + leapDay); // 2024-02-29
// 修改为非闰年
// LocalDate nonLeap = leapDay.withYear(2025);
// 抛出 DateTimeException: Invalid date 'FEBRUARY 29'
// 正确做法:调整为2月28日或3月1日
LocalDate feb28 = leapDay.withYear(2025).withDayOfMonth(28);
System.out.println("非闰年2月28日: " + feb28); // 2025-02-28
4. 常见错误
4.1 无效日期值
LocalDate date = LocalDate.of(2025, 8, 15);
// ❌ 错误:8月没有40日
// date.withDayOfMonth(40); // DateTimeException
// ❌ 错误:年内没有400天
// date.withDayOfYear(400); // DateTimeException
// ❌ 错误:月份超出1-12
// date.withMonth(13); // DateTimeException
4.2 日期溢出(跨月)
LocalDate jan31 = LocalDate.of(2025, 1, 31);
// ❌ 错误:2月没有31日
// jan31.withMonth(2); // DateTimeException
4.3 闰年2月29日问题
LocalDate leapDay = LocalDate.of(2024, 2, 29);
// ❌ 错误:2025年不是闰年
// leapDay.withYear(2025); // DateTimeException
5. 注意事项
- 不可变性:所有
with
方法都返回新对象,原对象不变。 - 有效性验证:方法会自动验证日期有效性,无效日期抛出异常。
- 月份范围:
withMonth()
参数为 1-12,不是 0-11。 - 链式调用安全:可以安全地链式调用,但要注意中间状态的有效性。
- 性能:创建新对象有轻微开销,但通常可忽略。
6. 使用技巧
6.1 链式调用
LocalDate date = LocalDate.of(2025, 8, 15);
// 链式修改多个字段
LocalDate modified = date.withYear(2023)
.withMonth(12)
.withDayOfMonth(25);
System.out.println(modified); // 2023-12-25
6.2 获取月份第一天/最后一天
LocalDate anyDay = LocalDate.of(2025, 8, 15);
// 当月第一天
LocalDate firstDay = anyDay.withDayOfMonth(1);
// 当月最后一天
LocalDate lastDay = anyDay.withDayOfMonth(anyDay.lengthOfMonth());
6.3 获取年份第一天/最后一天
LocalDate anyDay = LocalDate.of(2025, 8, 15);
// 年内第一天
LocalDate firstDay = anyDay.withDayOfYear(1);
// 年内最后一天
LocalDate lastDay = anyDay.withDayOfYear(anyDay.lengthOfYear());
6.4 安全的月份修改(处理31日问题)
public static LocalDate withMonthSafe(LocalDate date, int month) {
try {
return date.withMonth(month);
} catch (DateTimeException e) {
// 如果目标月天数不足,返回最后一天
LocalDate temp = date.withMonth(month);
return temp.withDayOfMonth(temp.lengthOfMonth());
}
}
// 使用
LocalDate jan31 = LocalDate.of(2025, 1, 31);
LocalDate feb28 = withMonthSafe(jan31, 2); // 返回 2025-02-28
7. 最佳实践与性能优化
7.1 最佳实践
- 优先使用
with
方法:比手动解析字符串更高效、更安全。 - 处理异常情况:在可能产生无效日期的场景中,提前验证或捕获异常。
- 使用常量:对于固定的修改(如每月1日),可直接使用
withDayOfMonth(1)
。 - 结合其他方法:与
plus()
、minus()
等方法配合使用。
7.2 性能优化
- 避免不必要的创建:如果新旧日期相同,
with
方法仍会创建新对象。 - 批量操作优化:在循环中使用时,确保逻辑正确。
- 缓存常用日期:对于频繁使用的特殊日期(如每月1日),可考虑缓存。
8. 总结
方法 | 用途 | 参数范围 | 典型用例 |
---|---|---|---|
withDayOfMonth() |
修改月内日期 | 1 至 当月天数 | 获取每月1日/最后一天 |
withDayOfYear() |
修改年内日期 | 1-365/366 | 获取年内第N天、年初/年末 |
withMonth() |
修改月份 | 1-12 | 日期跨月调整 |
withYear() |
修改年份 | int 范围 | 日期跨年调整 |
核心要点:
- 所有方法都返回新实例,原对象不变。
- 会进行严格的有效性检查,无效日期抛出
DateTimeException
。 - 支持链式调用,代码简洁。
- 处理闰年和月份天数差异时需特别注意。
推荐使用场景:
// 获取当月第一天
LocalDate first = date.withDayOfMonth(1);
// 获取当年最后一天
LocalDate last = date.withDayOfYear(date.lengthOfYear());
// 将日期调整到指定年月(安全版本)
LocalDate target = date.withYear(2023).withMonth(2);
try {
return target.withDayOfMonth(Math.min(date.getDayOfMonth(), target.lengthOfMonth()));
} catch (DateTimeException e) {
return target.withDayOfMonth(target.lengthOfMonth());
}