- 被称为“新纪元”的时间线原点 :
- 被设置为穿过伦敦格林尼治皇家天文台的本初子午线所处时区的 1970 年 1 月 1 日的午夜。
- Java 标准库有两套处理日期和时间的 API:
- 一套定义在 java.util 这个包里。
- 主要包括:Date、Calendar 和 TimeZone 这几个类;
- 一套新的 API 是在 Java8 引入的,定义在 java.time 这个包里。
- 主要包括:LocalDateTime、ZonedDateTime、ZoneId 等。
一、时间点 与 时间差
1、java.time.Instant 类(final 修饰)-- 时间点
- 在 Java 中,Instant 表示时间线上的某个点(某个时刻)。
- 这与 UNIX/POSIX 时间中使用的惯例相同。
- 从“新纪元”的时间线原点开始,时间按照每天 86400 秒向前或向回度量,精确到纳秒 。
- Instant 的值往回可追溯 10 亿年(Instant.MIN)前。
- 对于表示宇宙年龄(大约 135 亿年)来说还差得远,但是对于所有实际应用来说,应该足够了。
- 最大的值 Instant.MAX 是公元 1000000000 年的 12 月 31 日。
- 特点:
- 基于时间戳:存储从 1970-01-01T00:00:00Z(Unix 纪元)开始的 秒 和 纳秒 偏移。
- 不可变且线程安全:所有修改操作返回新实例。
- 不包含时区:但可与其他时区类(如: ZonedDateTime)转换。
- 调用静态方法 Instant.now() 会给出当前的时刻 。
public static void main(String[] args) {
Instant now = Instant.now();
System.out.println("当前时间: " + now);
Instant parsed = Instant.parse("2025-05-31T08:00:00Z");
System.out.println("解析时间: " + parsed);
Instant future = now.plus(Duration.ofHours(3));
System.out.println("3小时后: " + future);
ZonedDateTime tokyoTime = now.atZone(ZoneId.of("Asia/Shanghai"));
System.out.println("北京时间: " + tokyoTime);
boolean isLater = future.isAfter(now);
System.out.println("future 是否在 now 之后? " + isLater);
}
2、java.time.Duration 类(final 修饰)-- 基于时间单位(秒、纳秒)的时间差
- Duration 是两个时刻之间的时间量。
- 可以通过调用 toNanos、toMillis、getSeconds、toMinutes、toHours、toDays 来获得 Duration 按照传统单位度量的时间长度。
- 如果想要让计算精确到纳秒级。那么,就需要当心上溢问题。
- long 值可以存储大约 300 年时间对应的纳秒数。
- 如果需要的 Duration 短于这个时间(300 年时间)。
- 如果需要的 Duration 长于这个时间(300 年时间)。
- 让 Duration 对象用一个 long 来存储秒数,用另外一个 int 来存储纳秒数。
- Duration 接口包含了大量用于执行算术运算的方法。
- 特点:
- 不可变且线程安全:所有操作返回新实例。
- 支持正负值:可以表示过去或未来的时间间隔。
- 精确到纳秒:但实际精度取决于输入数据。
- 与时间对象交互:支持对 Instant、LocalDateTime 等时间类进行加减操作。
public static void main(String[] args) {
Duration duration = Duration.ofHours(1).plusMinutes(30).plusSeconds(15);
System.out.println("自定义 Duration: " + duration);
Instant start = Instant.parse("2025-05-31T08:00:00Z");
Instant end = Instant.parse("2025-05-31T09:30:15Z");
Duration between = Duration.between(start, end);
System.out.println("时间间隔: " + between);
System.out.println("总秒数: " + between.toSeconds());
System.out.println("总毫秒数: " + between.toMillis());
Duration newDuration = between.plusMinutes(10).minusSeconds(5);
System.out.println("调整后的 Duration: " + newDuration);
LocalDateTime ldt = LocalDateTime.of(2025, 5, 31, 8, 0);
LocalDateTime updatedLdt = ldt.plus(newDuration);
System.out.println("加 Duration 后的时间: " + updatedLdt);
}
二、本地日期
- 在 Java 中有两种人类时间,本地日期(时间)和时区时间。
- 本地日期(时间):包含当天的日期和当天的时间,但是与时区信息 没有任何关联。
- 1903 年 6 月 14 日就是一个本地日期的示例。
+ lambda 演算的发明者 Alonzo Church 在这一天诞生。
- 因为,这个日期既没有当天的时间,也没有时区信息。因此,它并不对应精确的时刻。
- 时区时间:1969 年 7 月 16 日 09:32:00 EDT(阿波罗 11 号发射的时刻)是一个时区日期/时间。
1、java.time.LocalDate 类(final 修饰)
- LocalDate 带有年、月、日的日期。
- 为了构建 LocalDate 对象,可以使用 now 或 of 静态方法。
- 特点:
- 不可变且线程安全:所有修改操作返回新实例 。
- 无时间和时区:只包含年、月、日 。
- 格式化和解析:支持 ISO-8601 标准(如:yyyy-MM-dd)及其他自定义格式。
- 日期运算:支持加减天数、月数、年数,或调整到特定日期(如月末)。
public static void main(String[] args) {
LocalDate today = LocalDate.now();
System.out.println("当前日期: " + today);
LocalDate parsedDate = LocalDate.parse("2025-12-25");
System.out.println("圣诞节: " + parsedDate);
LocalDate nextWeek = today.plusWeeks(1);
System.out.println("一周后: " + nextWeek);
LocalDate lastDay = today.with(TemporalAdjusters.lastDayOfMonth());
System.out.println("本月最后一天: " + lastDay);
long daysToChristmas = ChronoUnit.DAYS.between(today, parsedDate);
System.out.println("距离圣诞节还有 " + daysToChristmas + " 天");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 EEEE");
String formatted = parsedDate.format(formatter);
System.out.println("格式化后的圣诞节: " + formatted);
}
2、java.time.Period 类(final 修饰)-- 基于日期单位(年、月、日)的时间差
- 对于 Instant 来说,两个 Instant 之间的时长是 Duration。
- 对于本地日期(LocalDate)来说,两个 LocalDate 之间的时长是 Period。
- 特点:
- 不可变且线程安全:所有操作返回新实例。
- 基于日历单位:以年、月、日为单位,而非精确的时间(如:秒、纳秒)。
- 支持格式化和解析:遵循 ISO-8601 标准(如: P1Y2M3D)。
- 与日期类交互:可对 LocalDate 进行加减操作。
public static void main(String[] args) {
LocalDate birthday = LocalDate.of(1990, 5, 20);
LocalDate today = LocalDate.now();
Period age = Period.between(birthday, today);
System.out.println("年龄: " + age.getYears() + "岁 " + age.getMonths() + "个月 " + age.getDays() + "天");
Period projectPeriod = Period.of(1, 6, 10);
Period extendedPeriod = projectPeriod.plusMonths(2);
System.out.println("延长后的项目周期: " + extendedPeriod);
LocalDate projectStart = LocalDate.of(2024, 1, 1);
LocalDate projectEnd = projectStart.plus(extendedPeriod);
System.out.println("项目结束日期: " + projectEnd);
Period parsed = Period.parse("P3Y4M");
System.out.println("解析结果: " + parsed);
LocalDate localDate = LocalDate.of(2024, 01, 31);
System.out.println(localDate.plusMonths(1));
System.out.println(localDate);
localDate = LocalDate.of(2025, 01, 31);
System.out.println(localDate.plusMonths(1));
System.out.println(localDate);
System.out.println(localDate.getDayOfWeek().getValue());
}
三、日期调整器(java.time.temporal.TemporalAdjusters)
- TemporalAdjusters 类提供了大量用于常见调整的静态方法 。
- 用于对日期进行复杂的调整操作。
- 如:找到当月的最后一天、下一个周一、每个月的第一个星期二等)。
public static void main(String[] args) {
LocalDate date = LocalDate.of(2025, 5, 15);
System.out.println("当月第一天: " + date.with(TemporalAdjusters.firstDayOfMonth()));
System.out.println("下一个周一: " + date.with(TemporalAdjusters.next(DayOfWeek.MONDAY)));
LocalDate thirdTuesday = date.with(
TemporalAdjusters.dayOfWeekInMonth(3, DayOfWeek.TUESDAY)
);
System.out.println("当月第三个星期二: " + thirdTuesday);
TemporalAdjuster addBusinessDays = temporal -> {
LocalDate current = LocalDate.from(temporal);
int daysToAdd = 10;
while (daysToAdd > 0) {
current = current.plusDays(1);
if (current.getDayOfWeek() != DayOfWeek.SATURDAY && current.getDayOfWeek() != DayOfWeek.SUNDAY) {
daysToAdd--;
}
}
return temporal.with(current);
};
LocalDate result = date.with(addBusinessDays);
System.out.println("加 10 个工作日: " + result);
}
四、本地时间(java.time.LocalTime 类)
- LocalTime 表示当日时刻(如:15:30:00)。可以用 now 或 of 方法创建其实例:
- plus 和 minus 操作是按照一天 24 小时循环操作的。
- 常量时间:
- LocalTime.MIN(00:00)
- LocalTime.MAX(23:59:59.999999999)
- LocalTime.NOON(12:00)。
- 特性:
- 不可变且线程安全:所有修改操作返回新实例。
- 无日期和时区:仅包含 时、分、秒、纳秒。
- 格式化和解析:支持 ISO-8601 格式(如:HH:mm:ss.SSS)及自定义格式。
- 时间运算:支持加减 小时、分钟、秒、纳秒,或调整到特定时间。
public static void main(String[] args) {
LocalTime now = LocalTime.now();
System.out.println("当前时间: " + now);
LocalTime time1 = LocalTime.of(14, 30);
System.out.println(time1);
LocalTime time2 = LocalTime.of(14, 30, 45);
System.out.println(time2);
LocalTime time3 = LocalTime.of(14, 30, 45, 123_000_000);
System.out.println(time3);
int hour = now.getHour();
int minute = now.getMinute();
int second = now.getSecond();
int nano = now.getNano();
LocalTime parsed = LocalTime.parse("09:15:30");
System.out.println("解析时间: " + parsed);
LocalTime parsedTime = LocalTime.parse("14:30:45.123");
System.out.println("解析时间: " + parsedTime);
LocalTime meetingTime = parsed.plusHours(2).minusMinutes(15).plusNanos(500_000_000);
System.out.println("会议时间: " + meetingTime);
LocalTime nextHour = now.plus(1, ChronoUnit.HOURS);
LocalTime adjusted = now.withHour(15).withMinute(0).withSecond(0);
System.out.println(adjusted);
LocalTime deadline = LocalTime.of(17, 0);
System.out.println("是否超过截止时间? " + now.isAfter(deadline));
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mm a");
String formatted = meetingTime.format(formatter);
System.out.println("格式化会议时间: " + formatted);
formatter = DateTimeFormatter.ofPattern("HH时mm分ss秒");
formatted = now.format(formatter);
System.out.println(formatted);
}
五、本地日期时间(java.time.LocalDateTime 类)
- 表示 不含时区 的日期和时间(如 “2025-05-31T14:30:45.123”),精确到纳秒。
- 它结合了 LocalDate 和 LocalTime。
- 适用于需要同时处理日期和时间的场景(如:日志记录、计划任务等)。
- 特点:
- 不可变且线程安全:所有修改操作返回新实例。
- 无时区信息:仅包含日期和时间,依赖系统默认时区或显式指定的日期时间字段。
- 精确到纳秒:支持高精度时间操作。
- 格式化和解析:兼容 ISO 格式(如:yyyy-MM-ddTHH:mm:ss.SSS)及自定义格式。
- yyyy是年份,MM是月份,dd是日期,EEEE是星期全称。
- HH是 24 小时制小时,hh是 12 小时制小时,mm是分钟,ss是秒,SSS 是毫秒。
- 与日期/时间类互操作:可与 LocalDate、LocalTime、ZonedDateTime 等无缝转换
public static void main(String[] args) {
LocalDateTime now = LocalDateTime.now();
System.out.println("当前时间: " + now);
now = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println(now);
LocalDateTime dateTime = LocalDateTime.of(2025, Month.OCTOBER, 12, 15, 30, 45);
System.out.println(dateTime);
LocalDateTime parsed = LocalDateTime.parse("2025-12-25T08:30");
System.out.println("解析时间: " + parsed);
LocalDateTime nextWeek = now.plusWeeks(1);
System.out.println("一周后: " + nextWeek);
LocalDateTime lastDay = now.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MIDNIGHT);
System.out.println("当月最后一天的午夜: " + lastDay);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm");
String formatted = parsed.format(formatter);
System.out.println("格式化结果: " + formatted);
}
六、时区时间(java.time.ZonedDateTime)
- 互联网编码分配管理机构 (Internet Assigned Numbers Authority,IANA)保存着一个数据库。
- 里面存储着世界上所有已知的时区(www.iana.org/time-zones)。
- 它每年会更新数次,其中许多更新是为了处理夏令时的变更规则。
- Java 使用了 IANA 数据库。
- 每个时区都有一个ID(如:America/New_York、Europe/Berlin、Asia/Shanghai)。
- 要想找出所有可用的时区,可以调用 Zoneld.getAvailableZonelds。
- 给定一个时区 ID,静态方法 Zoneld.of(id) 可以产生一个 Zoneld 对象。
ZoneId.of("Asia/Shanghai")
- 用于表示 不含时区 的完整日期和时间(如:2024-05-31T15:30:45+08:00[Asia/Shanghai])。
- 它结合了 LocalDateTime 和 ZoneId,能够精确处理跨时区的日期时间操作。
- 适用于需要明确时区信息 的场景(如:国际会议、航班时刻表等)。
- 特点:
- 不可变且线程安全:所有操作返回新实例。
- 包含时区和偏移量:基于 ZoneId 和 ZoneOffset,支持夏令时自动调整。
- 精确到纳秒:支持高精度时间操作。
- 格式化和解析:
- 兼容 ISO 标准格式(如:yyyy-MM-ddTHH:mm:ss.SSSXXX[VV])及自定义格式。
- 与 UTC 转换:可转换为 Instant(时间戳),便于跨系统交互。
public static void main(String[] args) {
ZonedDateTime nowShanghai = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println("上海当前时间: " + nowShanghai);
ZonedDateTime nowNewYork = nowShanghai.withZoneSameInstant(ZoneId.of("America/New_York"));
System.out.println("同一时刻的纽约时间: " + nowNewYork);
ZonedDateTime adjusted = nowShanghai.plusDays(2).with(TemporalAdjusters.next(DayOfWeek.MONDAY));
System.out.println("调整后的时间: " + adjusted);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm z");
String formatted = adjusted.format(formatter);
System.out.println("格式化结果: " + formatted);
ZonedDateTime parsed = ZonedDateTime.parse("2025-06-03 10:30 CST",
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm z"));
System.out.println("解析后的时间: " + parsed);
}
七、java.util.Date 类
- Java 早期版本中用于表示日期和时间的类。
- 存储自 1970 年 1 月 1 日 00:00:00 GMT(纪元(epoch)时间) 以来的毫秒数。
public Date() {
this(System.currentTimeMillis());
}
- 特点:
- 时间精度:毫秒级精度。
- 时区问题:Date 内部不存储时区信息,但 toString() 使用 JVM 默认时区格式化。
- 可变性:对象创建后可通过 setTime() 修改时间值,可能导致 线程安全问题。
- 多线程环境下需同步或改用 不可变类(如:java.time.Instant)。
- 过时方法:如 getYear()、setMonth() 已废弃,建议避免使用。
public class DateExample {
public static void main(String[] args) throws InterruptedException {
Date now = new Date();
System.out.println("当前时间: " + now);
long epochMillis = 1696435200000L;
Date specificDate = new Date(System.currentTimeMillis());
System.out.println("指定时间: " + specificDate);
Date date = new Date(125, 9, 5);
System.out.println(date);
Date date1 = new Date();
Date date2 = new Date(date1.getTime() + 1000);
System.out.println("date1 是否在 date2 之前: " + date1.before(date2));
System.out.println("date1 和 date2 的比较结果: " + date1.compareTo(date2));
long start = System.currentTimeMillis();
Thread.sleep(2000);
long end = System.currentTimeMillis();
Date startDate = new Date(start);
Date endDate = new Date(end);
long duration = endDate.getTime() - startDate.getTime();
System.out.println("耗时(毫秒): " + duration);
}
}
八、java.util.Calendar 类
- Java 中用于处理日期和时间的抽象类,提供对日期字段(年、月、日、小时等)的灵活操作。
- Java 8 后推荐使用 java.time API(如:LocalDateTime)。
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimeZone;
public class CalendarExample {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
calendar.set(2025, Calendar.OCTOBER, 5, 15, 30, 0);
Calendar calendarNY = Calendar.getInstance(TimeZone.getTimeZone("America/New_York"));
System.out.println("年: " + calendar.get(Calendar.YEAR));
System.out.println("月: " + (calendar.get(Calendar.MONTH) + 1));
calendar.add(Calendar.DAY_OF_MONTH, 10);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
System.out.println("加 10 天后: " + sdf.format(calendar.getTime()));
Calendar other = Calendar.getInstance();
other.set(2025, Calendar.DECEMBER, 31);
boolean isBefore = calendar.before(other);
System.out.println("是否在 2025-12-31 之前? " + isBefore);
}
}