Java LocalDateTime
什么是LocalDateTime?
LocalDateTime是Java 8引入的日期时间API的核心类之一,它表示没有时区信息的日期-时间,例如"2023年10月25日14点30分45秒"。这个类是不可变的和线程安全的,这意味着所有修改操作都会返回一个新的LocalDateTime实例,而不是修改原始对象。
与旧的Date和Calendar类相比,LocalDateTime提供了更直观、更强大的API,用于处理日期和时间。
LocalDateTime的创建
创建LocalDateTime实例有多种方法:
获取当前日期和时间
java
// 获取当前日期和时间
LocalDateTime currentDateTime = LocalDateTime.now();
System.out.println("当前日期和时间: " + currentDateTime);
// 可能的输出: 当前日期和时间: 2023-10-25T14:30:45.123456789
使用特定的日期和时间创建
java
// 使用年、月、日、时、分、秒创建
LocalDateTime specificDateTime = LocalDateTime.of(2023, 10, 25, 14, 30, 45);
System.out.println("指定的日期和时间: " + specificDateTime);
// 可能的输出: 指定的日期和时间: 2023-10-25T14:30:45
// 使用LocalDate和LocalTime创建
LocalDate date = LocalDate.of(2023, 10, 25);
LocalTime time = LocalTime.of(14, 30, 45);
LocalDateTime dateTime = LocalDateTime.of(date, time);
System.out.println("使用LocalDate和LocalTime创建: " + dateTime);
// 可能的输出: 使用LocalDate和LocalTime创建: 2023-10-25T14:30:45
获取LocalDateTime的组成部分
从LocalDateTime对象中获取年、月、日、时、分、秒等信息非常简单:
java
LocalDateTime now = LocalDateTime.now();
int year = now.getYear();
Month month = now.getMonth(); // 返回Month枚举
int monthValue = now.getMonthValue(); // 返回1-12的整数
int dayOfMonth = now.getDayOfMonth();
DayOfWeek dayOfWeek = now.getDayOfWeek(); // 返回DayOfWeek枚举
int dayOfYear = now.getDayOfYear();
int hour = now.getHour();
int minute = now.getMinute();
int second = now.getSecond();
int nano = now.getNano();
System.out.println("年: " + year);
System.out.println("月(英文): " + month);
System.out.println("月(数字): " + monthValue);
System.out.println("日: " + dayOfMonth);
System.out.println("星期: " + dayOfWeek);
System.out.println("一年中的第几天: " + dayOfYear);
System.out.println("小时: " + hour);
System.out.println("分钟: " + minute);
System.out.println("秒: " + second);
System.out.println("纳秒: " + nano);
修改LocalDateTime
由于LocalDateTime是不可变的,所有"修改"操作都会返回一个新的LocalDateTime实例:
java
LocalDateTime dateTime = LocalDateTime.of(2023, 10, 25, 14, 30, 45);
// 添加年、月、日、时、分、秒
LocalDateTime plusYears = dateTime.plusYears(1);
LocalDateTime plusMonths = dateTime.plusMonths(2);
LocalDateTime plusDays = dateTime.plusDays(3);
LocalDateTime plusHours = dateTime.plusHours(4);
LocalDateTime plusMinutes = dateTime.plusMinutes(5);
LocalDateTime plusSeconds = dateTime.plusSeconds(6);
System.out.println("原始日期时间: " + dateTime);
System.out.println("加1年后: " + plusYears);
System.out.println("加2个月后: " + plusMonths);
System.out.println("加3天后: " + plusDays);
System.out.println("加4小时后: " + plusHours);
System.out.println("加5分钟后: " + plusMinutes);
System.out.println("加6秒后: " + plusSeconds);
// 减去年、月、日、时、分、秒
LocalDateTime minusYears = dateTime.minusYears(1);
LocalDateTime minusMonths = dateTime.minusMonths(2);
LocalDateTime minusDays = dateTime.minusDays(3);
LocalDateTime minusHours = dateTime.minusHours(4);
LocalDateTime minusMinutes = dateTime.minusMinutes(5);
LocalDateTime minusSeconds = dateTime.minusSeconds(6);
System.out.println("减1年后: " + minusYears);
System.out.println("减2个月后: " + minusMonths);
System.out.println("减3天后: " + minusDays);
System.out.println("减4小时后: " + minusHours);
System.out.println("减5分钟后: " + minusMinutes);
System.out.println("减6秒后: " + minusSeconds);
// 直接设置特定字段的值
LocalDateTime withYear = dateTime.withYear(2024);
LocalDateTime withMonth = dateTime.withMonth(12);
LocalDateTime withDayOfMonth = dateTime.withDayOfMonth(20);
LocalDateTime withHour = dateTime.withHour(10);
LocalDateTime withMinute = dateTime.withMinute(15);
LocalDateTime withSecond = dateTime.withSecond(30);
System.out.println("设置年为2024: " + withYear);
System.out.println("设置月为12: " + withMonth);
System.out.println("设置日为20: " + withDayOfMonth);
System.out.println("设置小时为10: " + withHour);
System.out.println("设置分钟为15: " + withMinute);
System.out.println("设置秒为30: " + withSecond);
提示
链式操作是一种常见的做法,可以在单行代码中执行多个操作:
java
LocalDateTime newDateTime = dateTime
.plusYears(1)
.minusMonths(2)
.withDayOfMonth(15)
.withHour(10);
格式化和解析
格式化LocalDateTime
要将LocalDateTime对象转换为特定格式的字符串,可以使用DateTimeFormatter类:
java
LocalDateTime dateTime = LocalDateTime.of(2023, 10, 25, 14, 30, 45);
// 使用预定义的格式
DateTimeFormatter isoFormatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
String isoFormatted = dateTime.format(isoFormatter);
System.out.println("ISO格式: " + isoFormatted); // 2023-10-25T14:30:45
// 使用自定义的格式
DateTimeFormatter customFormatter1 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒");
String customFormatted1 = dateTime.format(customFormatter1);
System.out.println("自定义格式1: " + customFormatted1); // 2023年10月25日 14时30分45秒
DateTimeFormatter customFormatter2 = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
String customFormatted2 = dateTime.format(customFormatter2);
System.out.println("自定义格式2: " + customFormatted2); // 2023/10/25 14:30:45
// 使用本地化的格式
DateTimeFormatter localizedFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
.withLocale(Locale.CHINA);
String localizedFormatted = dateTime.format(localizedFormatter);
System.out.println("本地化格式(中国): " + localizedFormatted); // 2023年10月25日 下午2:30:45
解析字符串为LocalDateTime
从字符串解析成LocalDateTime对象也使用DateTimeFormatter:
java
String dateTimeStr1 = "2023-10-25T14:30:45";
LocalDateTime dateTime1 = LocalDateTime.parse(dateTimeStr1); // 使用默认ISO_LOCAL_DATE_TIME格式
System.out.println("解析的日期时间1: " + dateTime1);
// 使用自定义格式解析
String dateTimeStr2 = "2023年10月25日 14时30分45秒";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒");
LocalDateTime dateTime2 = LocalDateTime.parse(dateTimeStr2, formatter);
System.out.println("解析的日期时间2: " + dateTime2);
String dateTimeStr3 = "2023/10/25 14:30:45";
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
LocalDateTime dateTime3 = LocalDateTime.parse(dateTimeStr3, formatter2);
System.out.println("解析的日期时间3: " + dateTime3);
日期时间比较
LocalDateTime提供了多种方法来比较日期时间:
java
LocalDateTime dateTime1 = LocalDateTime.of(2023, 10, 25, 14, 30, 0);
LocalDateTime dateTime2 = LocalDateTime.of(2023, 10, 25, 15, 30, 0);
// 比较两个日期时间
boolean isEqual = dateTime1.isEqual(dateTime2); // false
boolean isBefore = dateTime1.isBefore(dateTime2); // true
boolean isAfter = dateTime1.isAfter(dateTime2); // false
System.out.println("dateTime1 等于 dateTime2: " + isEqual);
System.out.println("dateTime1 早于 dateTime2: " + isBefore);
System.out.println("dateTime1 晚于 dateTime2: " + isAfter);
// 计算两个日期时间之间的差异
Duration duration = Duration.between(dateTime1, dateTime2);
long seconds = duration.getSeconds(); // 3600
System.out.println("相差的秒数: " + seconds);
LocalDateTime与其他类型的转换
转换为LocalDate和LocalTime
java
LocalDateTime dateTime = LocalDateTime.of(2023, 10, 25, 14, 30, 45);
// 转换为LocalDate
LocalDate date = dateTime.toLocalDate();
System.out.println("转换后的LocalDate: " + date);
// 转换为LocalTime
LocalTime time = dateTime.toLocalTime();
System.out.println("转换后的LocalTime: " + time);
转换为时间戳(Instant)
java
LocalDateTime dateTime = LocalDateTime.of(2023, 10, 25, 14, 30, 45);
// 将LocalDateTime转换为Instant(需要提供时区信息)
ZoneId zoneId = ZoneId.systemDefault(); // 使用系统默认时区
Instant instant = dateTime.atZone(zoneId).toInstant();
System.out.println("转换后的Instant: " + instant);
// 将Instant转换回LocalDateTime
LocalDateTime newDateTime = LocalDateTime.ofInstant(instant, zoneId);
System.out.println("从Instant转换回的LocalDateTime: " + newDateTime);
与传统的Date和Calendar互相转换
java
// LocalDateTime转换为Date
LocalDateTime dateTime = LocalDateTime.of(2023, 10, 25, 14, 30, 45);
ZoneId zoneId = ZoneId.systemDefault();
Date date = Date.from(dateTime.atZone(zoneId).toInstant());
System.out.println("转换后的Date: " + date);
// Date转换为LocalDateTime
Date oldDate = new Date();
LocalDateTime newDateTime = LocalDateTime.ofInstant(oldDate.toInstant(), ZoneId.systemDefault());
System.out.println("从Date转换的LocalDateTime: " + newDateTime);
// LocalDateTime转换为Calendar
Calendar calendar = Calendar.getInstance();
calendar.clear();
calendar.set(dateTime.getYear(), dateTime.getMonthValue() - 1, dateTime.getDayOfMonth(),
dateTime.getHour(), dateTime.getMinute(), dateTime.getSecond());
System.out.println("转换后的Calendar: " + calendar.getTime());
// Calendar转换为LocalDateTime
Calendar oldCalendar = Calendar.getInstance();
LocalDateTime fromCalendar = LocalDateTime.ofInstant(oldCalendar.toInstant(), ZoneId.systemDefault());
System.out.println("从Calendar转换的LocalDateTime: " + fromCalendar);
实际应用场景
场景一:计算截止日期
假设我们有一个任务管理系统,需要计算任务的截止日期,即从当前时间开始5个工作日之后。
java
public LocalDateTime calculateDeadline(LocalDateTime startDateTime, int workDays) {
LocalDateTime deadline = startDateTime;
int addedDays = 0;
while (addedDays < workDays) {
deadline = deadline.plusDays(1);
// 跳过周六和周日
if (!(deadline.getDayOfWeek() == DayOfWeek.SATURDAY ||
deadline.getDayOfWeek() == DayOfWeek.SUNDAY)) {
addedDays++;
}
}
return deadline;
}
// 使用示例
LocalDateTime now = LocalDateTime.now();
LocalDateTime deadline = calculateDeadline(now, 5);
System.out.println("任务开始时间: " + now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")));
System.out.println("预计完成时间(5个工作日后): " + deadline.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")));
场景二:预约系统中的时间段验证
在医院或美容院的预约系统中,我们需要检查预约时间是否在营业时间内,以及是否与其他预约有冲突。
java
public class Appointment {
private LocalDateTime start;
private LocalDateTime end;
private String client;
// 构造函数、getter和setter省略
// 检查营业时间(假设营业时间是每天9:00-17:00)
public boolean isWithinBusinessHours() {
int startHour = start.getHour();
int endHour = end.getHour();
int endMinute = end.getMinute();
return startHour >= 9 && (endHour < 17 || (endHour == 17 && endMinute == 0));
}
// 检查是否与其他预约冲突
public boolean conflictsWith(Appointment other) {
return !this.end.isBefore(other.start) && !this.start.isAfter(other.end);
}
}
// 使用示例
Appointment appointment1 = new Appointment();
appointment1.setStart(LocalDateTime.of(2023, 10, 25, 10, 0));
appointment1.setEnd(LocalDateTime.of(2023, 10, 25, 11, 0));
appointment1.setClient("张三");
Appointment appointment2 = new Appointment();
appointment2.setStart(LocalDateTime.of(2023, 10, 25, 10, 30));
appointment2.setEnd(LocalDateTime.of(2023, 10, 25, 11, 30));
appointment2.setClient("李四");
System.out.println("预约1是否在营业时间内: " + appointment1.isWithinBusinessHours());
System.out.println("预约2是否在营业时间内: " + appointment2.isWithinBusinessHours());
System.out.println("预约1和预约2是否冲突: " + appointment1.conflictsWith(appointment2));
场景三:计算年龄
计算一个人的精确年龄:
java
public int calculateAge(LocalDateTime birthDate, LocalDateTime currentDate) {
if (birthDate == null || currentDate == null) {
return 0;
}
return Period.between(birthDate.toLocalDate(), currentDate.toLocalDate()).getYears();
}
// 使用示例
LocalDateTime birthDate = LocalDateTime.of(1990, 5, 15, 8, 30);
LocalDateTime now = LocalDateTime.now();
int age = calculateAge(birthDate, now);
System.out.println("出生日期: " + birthDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
System.out.println("当前年龄: " + age + "岁");
总结
Java 8的LocalDateTime类提供了一个现代化、直观且线程安全的方式来处理日期和时间。主要优点包括:
- 不可变性:对象一旦创建就不能被修改,这使得它们在多线程环境中更安全。
- 易用性:API设计得非常直观,使日期时间操作变得简单。
- 灵活性:丰富的方法支持各种日期时间操作,如加减日期、解析和格式化等。
- 与LocalDate和LocalTime的良好集成:可以轻松地在这些类型之间转换。
相比旧的Date和Calendar类,新的日期时间API更加清晰、一致且强大,因此在新项目中应当优先考虑使用。
练习
- 创建一个表示明天同一时间的LocalDateTime对象。
- 编写代码计算两个LocalDateTime之间相差的天数、小时数、分钟数和秒数。
- 使用LocalDateTime和DateTimeFormatter将当前时间格式化为"年-月-日 星期几 时:分:秒"的形式(例如:2023-10-25 星期三 14:30:45)。
- 创建一个方法,检查给定的LocalDateTime是否在过去、当前还是将来。
- 实现一个简单的倒计时功能,计算从当前时间到指定的LocalDateTime还有多少天、多少小时、多少分钟和多少秒。