在Java开发中,获取当前时间是常见的需求,无论是记录日志、处理业务逻辑还是数据存储,时间信息都扮演着重要角色,Java提供了多种时间API,从早期的java.util.Date和java.util.Calendar到Java 8引入的java.time包,每种方式都有其特点和适用场景,本文将详细介绍这些方法,帮助开发者根据实际需求选择合适的时间获取方式。

传统时间API:Date与Calendar
在Java 8之前,Date和Calendar是处理时间的主要工具,但它们存在设计缺陷,逐渐被新API取代。
java.util.Date
Date类是最基础的时间表示类,它封装了自1970年1月1日00:00:00 GMT以来的毫秒数,获取当前时间非常简单:
Date now = new Date(); System.out.println(now); // 输出类似:Wed Oct 23 10:30:45 CST 2026
需要注意的是,Date的构造方法会自动获取当前系统时间,且其toString()方法已格式化为易读形式,但Date存在明显问题:
- 线程不安全:
Date的所有方法都是可变的,多线程环境下直接修改会导致数据混乱。 - 时区处理混乱:默认使用系统时区,且无法灵活调整时区。
- 设计缺陷:部分方法(如
getYear()、getMonth())已标记为过时,且月份从0开始计数(0代表1月),使用不便。
java.util.Calendar
为弥补Date的不足,Java引入了Calendar类,它提供了更丰富的时间操作功能,如日期加减、时区设置等,获取当前时间需通过Calendar.getInstance():
Calendar calendar = Calendar.getInstance(); int year = calendar.get(Calendar.YEAR); // 年份(如2026) int month = calendar.get(Calendar.MONTH); // 月份(0-11,0代表1月) int day = calendar.get(Calendar.DAY_OF_MONTH); // 日期(1-31) System.out.println(year + "-" + (month + 1) + "-" + day); // 输出:2026-10-23
Calendar的优势在于支持日期计算(如add(Calendar.DAY_OF_MONTH, 1)表示加1天)和时区设置(calendar.setTimeZone(TimeZone.getTimeZone("UTC"))),但它仍有缺陷:
- 线程不安全:与
Date类似,Calendar是可变类,多线程环境下需同步处理。 - 月份计数混乱:月份从0开始,使用时需手动+1,容易出错。
- API冗余:方法命名不够直观(如
get(Calendar.DAY_OF_WEEK)获取的是星期几,1代表星期日)。
Java 8新时间API:java.time包
为彻底解决传统API的问题,Java 8引入了全新的java.time包,提供了不可变、线程安全且功能丰富的时间类,包括LocalDate、LocalTime、LocalDateTime、Instant等,成为目前推荐的时间处理方案。
LocalDate:仅处理日期
LocalDate表示不带时区的日期(如2026-10-23),适用于生日、纪念日等纯日期场景,获取当前日期:
LocalDate today = LocalDate.now(); System.out.println(today); // 输出:2026-10-23 int year = today.getYear(); // 2026 int month = today.getMonthValue(); // 10(1-12) int day = today.getDayOfMonth(); // 23
LocalDate的now()方法可接受时区参数,如LocalDate.now(ZoneId.of("America/New_York"))获取纽约时区的当前日期。

LocalTime:仅处理时间
LocalTime表示不带时区的时间(如10:30:45),适用于课程表、作息时间等纯时间场景,获取当前时间:
LocalTime now = LocalTime.now(); System.out.println(now); // 输出:10:30:45.123(含毫秒) int hour = now.getHour(); // 10 int minute = now.getMinute(); // 30 int second = now.getSecond(); // 45
可通过LocalTime.of(12, 0)创建指定时间(12:00),或LocalTime.parse("13:45:30")解析字符串为时间对象。
LocalDateTime:处理日期+时间
LocalDateTime是最常用的类,它结合了LocalDate和LocalTime,表示不带时区的日期时间(如2026-10-23T10:30:45),获取当前日期时间:
LocalDateTime now = LocalDateTime.now(); System.out.println(now); // 输出:2026-10-23T10:30:45.123
LocalDateTime支持灵活的时间计算,
- 加1天:
now.plusDays(1) - 减2小时:
now.minusHours(2) - 获取月份:
now.getMonth()(返回枚举类型,如OCTOBER)
Instant:时间戳
Instant表示自1970-01-01T00:00:00Z(UTC)以来的秒数和纳秒数,适用于系统时间戳、日志记录等场景,获取当前时间戳:
Instant timestamp = Instant.now(); System.out.println(timestamp); // 输出:2026-10-23T02:30:45.123Z(UTC时区) long epochSecond = timestamp.getEpochSecond(); // 秒数 long nano = timestamp.getNano(); // 纳秒数
Instant是UTC时间,与时区无关,适合跨系统的时间传递。
时间格式化与解析
无论是传统API还是新API,都涉及时间与字符串的转换,Java 8之前使用SimpleDateFormat,线程不安全且功能有限;Java 8引入DateTimeFormatter,线程安全且支持自定义格式。
使用DateTimeFormatter
DateTimeFormatter是java.time.format包的核心类,可通过ofPattern()自定义格式:

// 定义格式:yyyy-MM-dd HH:mm:ss
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 格式化当前时间为字符串
LocalDateTime now = LocalDateTime.now();
String formattedTime = now.format(formatter);
System.out.println(formattedTime); // 输出:2026-10-23 10:30:45
// 解析字符串为LocalDateTime
String timeStr = "2026-10-24 15:00:00";
LocalDateTime parsedTime = LocalDateTime.parse(timeStr, formatter);
System.out.println(parsedTime); // 输出:2026-10-24T15:00
常用格式符号:
yyyy:4位年份MM:2位月份(01-12)dd:2位日期(01-31)HH:24小时制小时(00-23)mm:分钟(00-59)ss:秒数(00-59)
时区处理
跨时区应用中,时区转换是关键,Java 8的ZoneId和ZonedDateTime解决了传统API时区处理混乱的问题。
ZoneId:时区标识
ZoneId表示时区ID(如”Asia/Shanghai”、”America/New_York”),通过ZoneId.of()获取:
ZoneId shanghaiZone = ZoneId.of("Asia/Shanghai");
ZoneId newYorkZone = ZoneId.of("America/New_York");
ZonedDateTime:带时区的日期时间
ZonedDateTime是LocalDateTime的时区版本,适用于需要精确到时区的时间场景(如航班时刻、全球会议时间):
// 获取上海时区的当前时间
ZonedDateTime shanghaiTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println(shanghaiTime); // 输出:2026-10-23T10:30:45+08:00[Asia/Shanghai]
// 转换为纽约时区时间
ZonedDateTime newYorkTime = shanghaiTime.withZoneSameInstant(ZoneId.of("America/New_York"));
System.out.println(newYorkTime); // 输出:2026-10-22T22:30:45-04:00[America/New_York]
性能对比与最佳实践
性能对比
- 传统API:
Date和Calendar因线程安全问题,在高并发场景下需同步处理,性能较差。 - 新API:
java.time包的类均为不可变对象,无需同步,且优化了底层实现,性能更优。LocalDateTime.now()的创建速度比Calendar.getInstance()快约3倍。
最佳实践
- 优先使用Java 8+新API:除非兼容旧版本(Java 8以下),否则推荐使用
LocalDate、LocalDateTime等类,避免线程安全问题。 - 明确需求选择类:
- 纯日期(生日):
LocalDate - 纯时间(课程表):
LocalTime - 日期+时间(订单时间):
LocalDateTime - 时间戳(日志记录):
Instant - 跨时区(全球会议):
ZonedDateTime
- 纯日期(生日):
- 格式化线程安全:使用
DateTimeFormatter替代SimpleDateFormat,避免多线程环境下格式化异常。 - 时区显式处理:涉及跨时区场景时,始终显式指定
ZoneId,避免依赖系统默认时区。
Java获取当前时间的方法经历了从Date/Calendar到java.time包的演进,新API凭借线程安全、功能丰富、设计直观的优势,已成为现代Java开发的首选,开发者应根据具体场景选择合适的时间类,并结合格式化和时区处理,确保时间数据的准确性和一致性,掌握这些方法,不仅能提升代码质量,还能避免因时间处理不当导致的潜在问题。
















