日期时间实战
常见场景
实际开发中日期时间的常见需求,这篇汇总最佳实践。
日期计算
计算年龄
java
public static int calculateAge(LocalDate birthDate) {
return Period.between(birthDate, LocalDate.now()).getYears();
}
// 使用
int age = calculateAge(LocalDate.of(2000, 3, 22));计算两个日期之间的天数
java
// 方式一:Duration(精确到秒)
long days = Duration.between(startDate.atStartOfDay(), endDate.atStartOfDay()).toDays();
// 方式二:Period
Period period = Period.between(startDate, endDate);
long days2 = period.getDays(); // 不够准确,不推荐
// 方式三:直接计算
long days3 = startDate.until(endDate, ChronoUnit.DAYS);计算工作日
java
public static long countWorkdays(LocalDate start, LocalDate end) {
long count = 0;
LocalDate current = start;
while (!current.isAfter(end)) {
DayOfWeek dow = current.getDayOfWeek();
if (dow != DayOfWeek.SATURDAY && dow != DayOfWeek.SUNDAY) {
count++;
}
current = current.plusDays(1);
}
return count;
}获取月初和月末
java
LocalDate today = LocalDate.now();
// 月初
LocalDate monthStart = today.withDayOfMonth(1);
// 或者
LocalDate monthStart2 = today.with(TemporalAdjusters.firstDayOfMonth());
// 月末
LocalDate monthEnd = today.with(TemporalAdjusters.lastDayOfMonth());
// 下个月初
LocalDate nextMonthStart = today.plusMonths(1).withDayOfMonth(1);判断闰年
java
public static boolean isLeapYear(int year) {
return Year.isLeap(year);
}
// 或者
LocalDate.of(year, 1, 1).lengthOfYear() == 366;日期时间解析
解析常见格式
java
// yyyy-MM-dd
LocalDate date1 = LocalDate.parse("2026-03-22");
// yyyy-MM-dd HH:mm:ss
LocalDateTime dt1 = LocalDateTime.parse("2026-03-22 14:30:00",
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
// 中文格式
LocalDate date2 = LocalDate.parse("2026年03月22日",
DateTimeFormatter.ofPattern("yyyy年MM月dd日"));
// 带毫秒
LocalDateTime dt2 = LocalDateTime.parse("2026-03-22T14:30:45.123",
DateTimeFormatter.ISO_LOCAL_DATE_TIME);处理异常格式
java
// 宽松解析
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd")
.withResolverStyle(ResolverStyle.LENIENT);
LocalDate date = LocalDate.parse("2026/02/30", formatter);
// 自动修正为 2026-03-02(2 月 30 日 → 3 月 2 日)日期时间格式化
常用格式
java
LocalDateTime dt = LocalDateTime.of(2026, 3, 22, 14, 30, 45);
// 标准格式
dt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); // 2026-03-22
dt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); // 2026-03-22 14:30:45
dt.format(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm"));// 2026/03/22 14:30
// 中文格式
dt.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss")); // 2026年03月22日 14:30:45
// 带星期
dt.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日 E HH:mm")); // 2026年03月22日 日 14:30带时区格式化
java
ZonedDateTime zdt = ZonedDateTime.now();
// ISO 格式
String iso = zdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME);
// 2026-03-22T14:30:45+08:00[Asia/Shanghai]
// 自定义格式
String custom = zdt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss XXX"));
// 2026-03-22 14:30:45 +08:00时区转换
场景:API 统一用 UTC
java
// 存储:转为 UTC
ZonedDateTime utcTime = localDateTime.atZone(ZoneId.of("Asia/Shanghai"))
.withZoneSameInstant(ZoneOffset.UTC);
// 存入数据库
Instant instant = utcTime.toInstant();
long timestamp = instant.toEpochMilli();
// 读取:从 UTC 转换
ZonedDateTime localTime = Instant.ofEpochMilli(timestamp)
.atZone(ZoneId.of("Asia/Shanghai"));场景:跨时区时间显示
java
// 统一用 UTC 存储
ZonedDateTime utcNow = ZonedDateTime.now(ZoneOffset.UTC);
// 美国用户看到的本地时间
ZonedDateTime usTime = utcNow.withZoneSameInstant(ZoneId.of("America/New_York"));
// 中国用户看到的本地时间
ZonedDateTime cnTime = utcNow.withZoneSameInstant(ZoneId.of("Asia/Shanghai"));性能测量
java
// 用 Duration 精确测量
public static <T> T measure(String label, Supplier<T> task) {
Instant start = Instant.now();
T result = task.get();
Instant end = Instant.now();
Duration duration = Duration.between(start, end);
System.out.println(label + " 耗时: " + duration.toMillis() + " ms");
return result;
}
// 使用
List<String> sorted = measure("排序", () -> {
List<String> list = new ArrayList<>();
// ...
Collections.sort(list);
return list;
});线程安全
JDK 8+ 的日期时间 API 都是线程安全的:
java
// 可以安全地作为静态常量
public class DateUtil {
private static final DateTimeFormatter DATE_FORMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd");
private static final DateTimeFormatter DATETIME_FORMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public static String formatDate(LocalDate date) {
return date.format(DATE_FORMATTER);
}
public static String formatDateTime(LocalDateTime dt) {
return dt.format(DATETIME_FORMATTER);
}
}多线程环境下直接调用,无需任何同步:
java
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
executor.submit(() -> {
String today = DateUtil.formatDate(LocalDate.now()); // 安全!
});
}总结
| 场景 | 推荐方案 |
|---|---|
| 只关心日期 | LocalDate |
| 只关心时间 | LocalTime |
| 日期+时间 | LocalDateTime |
| 存储/传输 | Instant 或 UTC ZonedDateTime |
| 显示给用户 | 转换到用户时区 |
| 计算日期间隔 | Period |
| 计算时间间隔 | Duration |
| 格式化 | DateTimeFormatter(线程安全) |
