Java 日期与时间类
Java 中常见的日期时间 API 可以分为两类:
- 旧版 API:
Date、Calendar、SimpleDateFormat。 - Java 8 新 API:
LocalDate、LocalDateTime、Instant、DateTimeFormatter等。
旧版 API 在维护历史代码时仍会遇到,但新项目通常优先使用 java.time 包。
一、时间戳与时区
Unix 时间戳通常表示从 1970-01-01T00:00:00Z 开始经过的时间。Date 使用毫秒精度保存时间点。
北京时间通常采用 UTC+8。时区会影响时间点如何显示为年月日和时分秒,但不会改变时间点本身。
二、Date 类
java.util.Date 表示时间轴上的一个时间点,内部核心值是从 Unix 纪元开始计算的毫秒数。它并不是简单地分别保存年、月、日、时、分、秒。
Date 中许多直接操作年月日的方法已经过时,日期计算应使用 Calendar 或更推荐的 java.time API。
构造器
Date now = new Date();
无参构造器创建表示当前时间的对象。
Date date = new Date(1_000_000L);
传入的 long 值表示从 Unix 纪元开始经过的毫秒数。
常用方法
Date date = new Date();
long timestamp = date.getTime();
date.setTime(timestamp + 1_000L);
getTime():获取毫秒时间戳。setTime(long time):修改该对象表示的时间点。
Date 是可变对象,调用 setTime() 会改变原对象。
三、Calendar 类
Calendar 是旧版日历计算类,可以根据时区和区域设置处理年月日等字段。
它的构造器通常不直接调用,而是通过 getInstance() 获取实例:
Calendar calendar = Calendar.getInstance();
获取和修改日期字段
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, 3);
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH) + 1;
int day = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(year + "-" + month + "-" + day);
注意:
Calendar.MONTH从0开始,因此显示月份时通常需要加1。add()的参数可以为正数或负数,用于增加或减少时间。Calendar也是可变对象。
与 Date 相互转换
Calendar calendar = Calendar.getInstance();
Date dateFromCalendar = calendar.getTime();
Date date = new Date(100_000_000L);
Calendar anotherCalendar = Calendar.getInstance();
anotherCalendar.setTime(date);
calendar.getTime():将Calendar当前表示的时间转换为Date。calendar.setTime(date):让Calendar使用指定Date的时间点。
四、SimpleDateFormat 类
SimpleDateFormat 用于在 Date 与字符串之间进行格式化和解析。
常见格式符号如下:
| 符号 | 含义 | 示例 |
|---|---|---|
yyyy | 四位年份 | 2026 |
MM | 两位月份 | 01 |
dd | 两位日期 | 25 |
HH | 24 小时制小时 | 16 |
mm | 分钟 | 30 |
ss | 秒 | 45 |
格式符号区分大小写。例如,MM 表示月份,mm 表示分钟。
将 Date 格式化为字符串
import java.text.SimpleDateFormat;
import java.util.Date;
public class FormatDateDemo {
public static void main(String[] args) {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date now = new Date();
String text = formatter.format(now);
System.out.println(text);
}
}将字符串解析为 Date
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ParseDateDemo {
public static void main(String[] args) {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
formatter.setLenient(false);
String text = "2026-01-25";
try {
Date date = formatter.parse(text);
System.out.println(date);
} catch (ParseException e) {
System.out.println("日期格式不正确");
}
}
}setLenient(false) 用于关闭宽松解析。例如,在严格模式下,2026-02-30 会解析失败,而不是自动调整到下一个月。
SimpleDateFormat 不是线程安全的,不应把同一个实例作为共享对象供多个线程同时使用。
五、旧版 API 应用
指定日期增加十五天
将 20260122 增加十五天,并按中文日期格式输出:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class AddDaysLegacyDemo {
public static void main(String[] args) {
String text = "20260122";
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
formatter.setLenient(false);
try {
Date date = formatter.parse(text);
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.add(Calendar.DATE, 15);
formatter.applyPattern("yyyy年MM月dd日");
System.out.println(formatter.format(calendar.getTime()));
} catch (ParseException e) {
System.out.println("日期格式不正确");
}
}
}计算两个时间点的毫秒差
long differenceMillis = endDate.getTime() - startDate.getTime();
直接除以一天的毫秒数只能得到固定时长意义上的天数。如果时间范围跨越夏令时变化,结果可能与日历日期差不同。因此,计算日期之间相差多少天时,更适合使用 LocalDate 和 ChronoUnit.DAYS。
六、Java 8 日期时间 API
java.time API 的类型设计更清晰,并且大多数核心类不可变、线程安全。
LocalDate
LocalDate 表示不带时区的日期,例如生日或课程日期。
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
public class LocalDateDemo {
public static void main(String[] args) {
DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
LocalDate date = LocalDate.parse("20260122", inputFormatter);
LocalDate result = date.plusDays(15);
System.out.println(result.format(outputFormatter));
}
}计算日期差
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
public class DateDifferenceDemo {
public static void main(String[] args) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
LocalDate startDate = LocalDate.parse("20260122", formatter);
LocalDate endDate = LocalDate.parse("20260302", formatter);
long days = ChronoUnit.DAYS.between(startDate, endDate);
System.out.println(days);
}
}常用类型
| 类型 | 适用场景 |
|---|---|
LocalDate | 只需要年月日,不需要时区 |
LocalTime | 只需要时分秒 |
LocalDateTime | 需要日期和时间,但不包含时区 |
Instant | 时间轴上的时间点,适合记录时间戳 |
ZonedDateTime | 需要明确时区规则的日期时间 |
Duration | 表示基于秒和纳秒的时长 |
Period | 表示基于年、月、日的日期差 |
DateTimeFormatter | 日期时间的格式化和解析 |
新代码应根据业务含义选择类型,而不是所有时间都使用 Date 或字符串保存。
喜欢的话,留下你的评论吧~