一:介绍
Java 提供了多套日期时间 API,以下是主要类别的对比和常用方法总结:
|-------|----------------------------------------------------------------|------|-----|--------|-------------|
| 类别 | 主要类 | 线程安全 | 可变性 | java版本 | 特点 |
| 传统日期 | Date, Calendar,GregorianCalendar | 否 | 可变 | 1.0+ | 设计缺陷多,不推荐使用 |
| 新日期时间 | LocalDate, LocalTime, LocalDateTime, ZonedDateTime, ChronoUnit | 是 | 不可变 | 8+ | 设计良好,推荐使用 |
| 时间戳 | Instant | 是 | 不可变 | 8+ | 机器时间,精确到纳秒 |
| 格式化 | DateTimeFormatter | 是 | 不可变 | 8+ | 线程安全的格式化类 |
二:Date类
Java 中的 Date 类位于 java.util 包中,它表示特定的瞬间,精确到毫秒。
Date 类主要用于表示日期和时间信息,是 Java 中处理日期和时间的基础类之一。
Date date = new Date();
// 获取自1970年1月1日00:00:00 GMT以来的毫秒数
long timeInMillis = date.getTime();
// 设置时间 (从1970年1月1日00:00:00 GMT开始的毫秒数)
date.setTime(1640995200000L);
// 比较两个日期
Date anotherDate = new Date();
int result = date.compareTo(anotherDate); // 小于返回-1,等于返回0,大于返回1
// 检查日期是否在指定日期之后
boolean isAfter = date.after(anotherDate);
// 检查日期是否在指定日期之前
boolean isBefore = date.before(anotherDate);
Date类的局限性
- 不是线程安全的:Date 对象是可变的,这在多线程环境中可能会出现问题。
- 设计不佳:月份从0开始(0表示1月),年份从1900年开始计算,这容易导致混淆。
- 时区处理复杂:Date 类本身不包含时区信息,时区处理需要额外的类。
- 功能有限:缺乏许多常用的日期操作功能,如加减天数、周数等。
三:Calendar类
Calendar 类是 Java 中用于处理日期和时间的抽象类,它位于 java.util 包中。这个类提供了一系列方法来操作日期和时间字段(如年、月、日、小时、分钟等),并且可以执行日期计算和比较。
Calendar calendar = Calendar.getInstance();
int year = calendar.get(Calendar.YEAR); // 获取年份
int month = calendar.get(Calendar.MONTH); // 获取月份(0-11)
int day = calendar.get(Calendar.DAY_OF_MONTH);// 获取日
int hour = calendar.get(Calendar.HOUR_OF_DAY);// 获取小时(24小时制)
int minute = calendar.get(Calendar.MINUTE); // 获取分钟
int second = calendar.get(Calendar.SECOND); // 获取秒
// 在当前日期上加10天
calendar.add(Calendar.DAY_OF_MONTH, 10);
// 在当前月份上减3个月
calendar.add(Calendar.MONTH, -3);
//日期比较
Calendar cal1 = Calendar.getInstance();
Calendar cal2 = Calendar.getInstance();
cal2.add(Calendar.DAY_OF_MONTH, 1);
// result < 0 表示 cal1 早于 cal2
// result == 0 表示 cal1 等于 cal2
// result > 0 表示 cal1 晚于 cal2
int result = cal1.compareTo(cal2);
//获取时间戳(毫秒数)
long timestamp = calendar.getTimeInMillis();
//设置时区
TimeZone timeZone = TimeZone.getTimeZone("America/New_York");
calendar.setTimeZone(timeZone);
Calendar 类的注意事项
- 月份从0开始:Calendar 类中月份是从0开始的(0=一月,11=十二月),这容易导致错误。
- 可变性:Calendar 对象是可变的,修改一个 Calendar 对象会影响所有引用它的地方。
- 线程安全:Calendar 类不是线程安全的,多线程环境下需要额外同步。
- 性能:频繁创建 Calendar 实例会影响性能,可以考虑重用实例。
- 时区问题:默认使用系统时区,跨时区应用需要特别注意。
四:GregorianCalendar类
GregorianCalendar 是 Java 中一个表示公历(格里高利历)的日历类,它继承自 Calendar 类。这个类提供了标准的日历系统,也是世界上大多数国家使用的日历系统。
GregorianCalendar 类位于 java.util 包中,用于处理日期和时间相关的操作。它支持从公元 1 年到未来的日期计算,并考虑了闰年等复杂的日历规则。
//创建实例
// 创建表示当前日期和时间的实例
GregorianCalendar calendar1 = new GregorianCalendar();
// 创建指定年份、月份、日期的实例
// 注意:月份从0开始,0表示1月
GregorianCalendar calendar2 = new GregorianCalendar(2023, 10, 15);
// 创建指定年份、月份、日期、小时、分钟的实例
GregorianCalendar calendar3 = new GregorianCalendar(2023, 10, 15, 14, 30);
// 创建指定年份、月份、日期、小时、分钟、秒的实例
GregorianCalendar calendar4 = new GregorianCalendar(2023, 10, 15, 14, 30, 45);
//获取日期和时间信息
GregorianCalendar calendar = new GregorianCalendar();
int year = calendar.get(Calendar.YEAR); // 获取年份
int month = calendar.get(Calendar.MONTH); // 获取月份(0-11)
int day = calendar.get(Calendar.DAY_OF_MONTH); // 获取日期
int hour = calendar.get(Calendar.HOUR_OF_DAY); // 获取小时(24小时制)
int minute = calendar.get(Calendar.MINUTE); // 获取分钟
int second = calendar.get(Calendar.SECOND); // 获取秒
//设置日期和时间
GregorianCalendar calendar = new GregorianCalendar();
// 设置年份
calendar.set(Calendar.YEAR, 2024);
// 设置月份(0-11)
calendar.set(Calendar.MONTH, Calendar.JANUARY);
// 设置日期
calendar.set(Calendar.DAY_OF_MONTH, 1);
// 同时设置年、月、日
calendar.set(2024, Calendar.JANUARY, 1);
//日期计算
GregorianCalendar calendar = new GregorianCalendar(2023, Calendar.NOVEMBER, 15);
// 增加10天
calendar.add(Calendar.DAY_OF_MONTH, 10);
// 减少2个月
calendar.add(Calendar.MONTH, -2);
//比较日期
GregorianCalendar cal1 = new GregorianCalendar(2023, Calendar.NOVEMBER, 15);
GregorianCalendar cal2 = new GregorianCalendar(2023, Calendar.DECEMBER, 25);
// 比较两个日期
int result = cal1.compareTo(cal2); // cal1在cal2之前返回负数,之后返回正数,相等返回0
// 判断是否在某个日期之前
boolean isBefore = cal1.before(cal2);
// 判断是否在某个日期之后
boolean isAfter = cal1.after(cal2);
五:LocalDate类
LocalDate 是 Java 8 中引入的一个不可变日期类,属于 java.time 包的一部分。
LocalDate 表示不带时区的日期(年-月-日),例如 "2023-05-15"。LocalDate 不包含时间和时区信息,非常适合处理生日、节假日等只需要日期的场景。
LocalDate特点:
- 不可变性
LocalDate 实例是不可变的,任何修改操作都会返回一个新的 LocalDate 对象,而不是修改原有对象。
- 线程安全
由于不可变性,LocalDate 是线程安全的,可以在多线程环境中安全使用。
- ISO-8601 标准
LocalDate 遵循 ISO-8601 日历系统,这是现代日期和时间表示的国际标准。
//获取当前日期
LocalDate today = LocalDate.now();
System.out.println("今天的日期: " + today);
//获取日期信息
LocalDate date = LocalDate.of(2023, 5, 15);
int year = date.getYear(); // 2023
Month month = date.getMonth(); // MAY
int day = date.getDayOfMonth(); // 15
DayOfWeek dow = date.getDayOfWeek(); // MONDAY
int len = date.lengthOfMonth(); // 31 (5月的天数)
boolean leap = date.isLeapYear(); // false (2023不是闰年)
//日期加减
LocalDate tomorrow = today.plusDays(1); //+1天
LocalDate nextWeek = today.plusWeeks(1); //+1周
LocalDate nextMonth = today.plusMonths(1); //+1月
LocalDate nextYear = today.plusYears(1); //+1年
LocalDate yesterday = today.minusDays(1); //-1天
//日期比较
LocalDate date1 = LocalDate.of(2023, 5, 15);
LocalDate date2 = LocalDate.of(2023, 6, 20);
boolean isBefore = date1.isBefore(date2); // true
boolean isAfter = date1.isAfter(date2); // false
boolean isEqual = date1.isEqual(date2); // false
六:LocalTime类
LocalTime 是 Java 8 引入的日期时间 API (java.time 包)中的一个重要类,用于表示不带时区信息的时间。它专门处理一天中的时间,精确到纳秒级别。
LocalTime特点:
- 不可变性
LocalTime 是不可变类,所有修改操作都会返回新的实例,原始对象保持不变。
- 无时区信息
LocalTime 不包含时区信息,仅表示本地时间。
- 时间精度
支持从小时到纳秒的时间精度(HH:mm:ss.nnnnnnnnn)。
LocalTime currentTime = LocalTime.now();
System.out.println("当前时间: " + currentTime); //当前时间: 16:07:57.111878700
// 创建 14:30:00
LocalTime time1 = LocalTime.of(14, 30);
// 创建 09:15:30
LocalTime time2 = LocalTime.of(9, 15, 30);
// 创建 10:20:30.500 (500毫秒)
LocalTime time3 = LocalTime.of(10, 20, 30, 500000000);
//时间比较
LocalTime time1 = LocalTime.of(10, 30);
LocalTime time2 = LocalTime.of(11, 15);
boolean isBefore = time1.isBefore(time2); // true
boolean isAfter = time1.isAfter(time2); // false
boolean isEqual = time1.equals(time2); // false
//时间运算
LocalTime time = LocalTime.of(9, 0);
// 加2小时
LocalTime plusHours = time.plusHours(2); // 11:00
// 减30分钟
LocalTime minusMinutes = time.minusMinutes(30); // 08:30
// 加1小时15分钟
LocalTime plusDuration = time.plus(Duration.ofMinutes(75)); // 10:15
七:LocalDateTime类
LocalDateTime 是 Java 8 引入的日期时间 API(java.time 包)中的一个重要类,它表示一个不可变的日期时间对象,不包含时区信息。这个类可以存储年、月、日、时、分、秒和纳秒级别的日期时间信息。
LocalDateTime特点:
-
不可变性:所有 java.time 类都是不可变的,线程安全
-
不包含时区:只表示本地日期和时间
-
精确到纳秒:可以表示精确到纳秒级别的时间
-
丰富的 API:提供了大量方法用于日期时间计算和格式化
LocalDateTime dateTime = LocalDateTime.now();
//时间获取
int year = dateTime.getYear();//年
int monthValue = dateTime.getMonthValue(); //月 返回 1-12
int day = dateTime.getDayOfMonth();//日
int hour = dateTime.getHour();//时
int minute = dateTime.getMinute();//分
int second = dateTime.getSecond();//秒
int nano = dateTime.getNano();//毫秒//时间计算
// 加操作
LocalDateTime plusYears = dateTime.plusYears(1);
LocalDateTime plusMonths = dateTime.plusMonths(1);
LocalDateTime plusDays = dateTime.plusDays(1);
LocalDateTime plusHours = dateTime.plusHours(1);
LocalDateTime plusMinutes = dateTime.plusMinutes(1);
LocalDateTime plusSeconds = dateTime.plusSeconds(1);// 减操作
LocalDateTime minusYears = dateTime.minusYears(1);
LocalDateTime minusDays = dateTime.minusDays(1);//时间比较
LocalDateTime dateTime1 = LocalDateTime.of(2023, 5, 15, 14, 30);
LocalDateTime dateTime2 = LocalDateTime.of(2023, 5, 16, 14, 30);boolean isBefore = dateTime1.isBefore(dateTime2); // true
boolean isAfter = dateTime1.isAfter(dateTime2); // false
boolean isEqual = dateTime1.isEqual(dateTime2); // false
八:ZonedDateTime类
ZonedDateTime 是 Java 8 引入的日期时间 API (java.time 包)中的一个重要类,它表示带有时区的日期和时间。这个类结合了 LocalDateTime 和 ZoneId,能够精确地表示特定时区的时间点。
ZonedDateTime特点:
包含日期(年、月、日)
包含时间(时、分、秒、纳秒)
包含时区信息
是不可变且线程安全的类
支持时区转换和夏令时自动调整
// 获取当前系统默认时区的日期时间
ZonedDateTime now = ZonedDateTime.now();
// 获取指定时区的当前日期时间
ZonedDateTime nowInTokyo = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
//时间获取
ZonedDateTime zdt = ZonedDateTime.now();
int year = zdt.getYear(); // 年
Month month = zdt.getMonth(); // 月
int day = zdt.getDayOfMonth(); // 日
int hour = zdt.getHour(); // 时
int minute = zdt.getMinute(); // 分
int second = zdt.getSecond(); // 秒
ZoneId zone = zdt.getZone(); // 时区
//时间加减操作
ZonedDateTime zdt = ZonedDateTime.now();
// 加1天
ZonedDateTime tomorrow = zdt.plusDays(1);
// 减2小时
ZonedDateTime twoHoursEarlier = zdt.minusHours(2);
// 加3周
ZonedDateTime threeWeeksLater = zdt.plusWeeks(3);
九:ChronoUnit类
ChronoUnit 是 Java 8 引入的一个枚举类,属于 java.time.temporal 包。
ChronoUnit 定义了一组标准的时间单位,用于表示日期和时间的不同粒度。ChronoUnit 主要用于与 Java 8 日期时间 API(如 LocalDate、LocalTime、LocalDateTime 等)一起使用,进行时间的计算和比较。
LocalDate today = LocalDate.now();
LocalDate nextWeek = today.plus(1, ChronoUnit.WEEKS); // 加一周
LocalDate lastMonth = today.minus(1, ChronoUnit.MONTHS); // 减一个月
//时间差计算
LocalDate start = LocalDate.of(2023, 1, 1);
LocalDate end = LocalDate.of(2023, 12, 31);
long days = ChronoUnit.DAYS.between(start, end); // 计算天数差
//计算年龄
LocalDate birthDate = LocalDate.of(1990, 5, 15);
LocalDate now = LocalDate.now();
long age = ChronoUnit.YEARS.between(birthDate, now);
System.out.println("年龄: " + age + " 岁");
十:Instant类
Instant 类是 Java 8 引入的日期时间 API (java.time 包)中的一个重要类,它代表时间线上的一个瞬时点。这个类主要用于记录时间戳,精确到纳秒级别。
Instant特点:
表示从 1970-01-01T00:00:00Z (即 Unix 纪元)开始的时间
不包含时区信息
是不可变且线程安全的
精确到纳秒(而传统的 Date 类只精确到毫秒)
Instant now = Instant.now(); // 获取当前时刻
//时间错获取
long seconds = instant.getEpochSecond(); // 获取从1970-01-01开始的秒数
int nanos = instant.getNano(); // 获取纳秒部分
//时间比较
Instant instant1 = Instant.now();
Instant instant2 = instant1.plusSeconds(10);
boolean isBefore = instant1.isBefore(instant2); // true
boolean isAfter = instant1.isAfter(instant2); // false
//时间运算
Instant instant = Instant.now();
// 加10秒
Instant later = instant.plusSeconds(10);
// 减5分钟
Instant earlier = instant.minus(Duration.ofMinutes(5));
// 加2天4小时30分钟
Instant future = instant.plus(2, ChronoUnit.DAYS)
.plus(4, ChronoUnit.HOURS)
.plus(30, ChronoUnit.MINUTES);
十一:DateTimeFormatter类
DateTimeFormatter 是 Java 8 引入的日期时间 API (java.time 包) 中的一个重要类,它用于格式化和解析日期时间对象。这个类提供了强大的功能来处理日期时间的显示和转换。
LocalDateTime now = LocalDateTime.now();
// 使用预定义的格式化器
System.out.println(now.format(DateTimeFormatter.ISO_LOCAL_DATE)); // 2023-11-15
System.out.println(now.format(DateTimeFormatter.ISO_LOCAL_TIME)); // 14:30:45.123
System.out.println(now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)); // 2023-11-15T14:30:45.123
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formattedDateTime = now.format(formatter); // 格式化
System.out.println(formattedDateTime); // 2023-11-15 14:30:45
// 解析
LocalDateTime parsedDateTime = LocalDateTime.parse("2023-11-15 14:30:45", formatter);
// 常用中文日期格式
DateTimeFormatter chineseFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
String formattedDate = today.format(chineseFormatter);
System.out.println("中文格式日期: " + formattedDate);
// 解析中文日期字符串
String chineseDateStr = "2023年05月20日";
LocalDate parsedDate = LocalDate.parse(chineseDateStr, chineseFormatter);
System.out.println("解析后的日期: " + parsedDate);
// 其他常用格式
DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd");
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("MM/dd/yyyy");
DateTimeFormatter formatter3 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 EEEE", Locale.CHINA);
System.out.println("格式1: " + today.format(formatter1));
System.out.println("格式2: " + today.format(formatter2));
System.out.println("格式3(带星期): " + today.format(formatter3));