目录
一、JDK7时间类
1. Date类
● Date类是一个JDK写好的javabean类,用来描述时间,精确到毫秒
● 利用空参构造创建的对象,默认表示系统当前时间
● 利用有参构造创建的对象,表示指定时间
构造方法说明
构造方法 | 说明 |
---|---|
public Date() | 创建Date对象,表示当前时间 |
public Date(long date) | 创建Date对象,表示指定毫秒值对应的时间 |
常用方法说明
方法名 | 说明 |
---|---|
public void setTime(long time) | 设置/修改时间的毫秒值 |
public long getTime() | 获取时间对象的毫秒值 |
使用示例:
// 创建表示当前时间的Date对象
Date now = new Date();
System.out.println(now); // 输出如:Sun May 11 10:23:45 CST 2025
// 创建表示指定时间的Date对象(毫秒值)
Date specifiedDate = new Date(1000); // 1970年1月1日加上1000毫秒
System.out.println(specifiedDate);
// 获取Date对象的毫秒值
long timeInMillis = now.getTime();
System.out.println("毫秒值:" + timeInMillis);
2. Calendar类
● Calendar代表了系统当前时间的日历对象,可以单独修改、获取时间中的年,月,日
● 细节:Calendar是一个抽象类,不能直接创建对象
● 细节:
月份:范围是0~11,0代表1月
星期:在老外眼中,星期日是一个星期的第一天
即:1(星期日)2(星期一)3(星期二)4(星期三)5(星期四)6(星期五)7(星期六)
获取对象方法
方法名 | 说明 |
---|---|
public static Calendar getInstance() | 获取Calendar对象,默认表示当前时间 |
常用方法说明
方法名 | 说明 |
---|---|
public int get(int field) | 获取某个字段的值 |
public void set(int field, int value) | 设置某个字段的值 |
public void add(int field, int amount) | 为某个字段增加/减少指定量 |
public final Date getTime() | 转换成Date对象 |
public long getTimeInMillis() | 获取时间的毫秒值 |
常用字段常量
常量 | 说明 |
---|---|
Calendar.YEAR | 年 |
Calendar.MONTH | 月(0-11) |
Calendar.DAY_OF_MONTH | 日 |
Calendar.HOUR | 12小时制小时 |
Calendar.HOUR_OF_DAY | 24小时制小时 |
Calendar.MINUTE | 分钟 |
Calendar.SECOND | 秒 |
Calendar.DAY_OF_WEEK | 星期几(1-7,1代表星期日) |
使用示例:
// 获取Calendar对象
Calendar calendar = Calendar.getInstance();
// 获取年、月、日
int year = calendar.get(Calendar.YEAR);
// 注意:月份从0开始,0代表1月,11代表12月
int month = calendar.get(Calendar.MONTH) + 1; // +1转换为人类习惯
int day = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(year + "年" + month + "月" + day + "日");
// 设置年、月、日
calendar.set(Calendar.YEAR, 2026);
calendar.set(Calendar.MONTH, 0); // 1月
calendar.set(Calendar.DAY_OF_MONTH, 1);
// 也可以一次性设置年、月、日
calendar.set(2026, 0, 1); // 2026年1月1日
// 日期计算
calendar.add(Calendar.DAY_OF_MONTH, 10); // 增加10天
calendar.add(Calendar.MONTH, -1); // 减少1个月
// 转换成Date对象
Date date = calendar.getTime();
3. SimpleDateFormat类
● 格式化:把时间变成我们喜欢的格式
● 解析:把字符串表示的时间变成Date对象
构造方法说明
构造方法 | 说明 |
---|---|
public SimpleDateFormat() | 构造一个SimpleDateFormat,使用默认格式 |
public SimpleDateFormat(String pattern) | 构造一个SimpleDateFormat,使用指定的格式 |
常用方法说明
方法名 | 说明 |
---|---|
public final String format(Date date) | 格式化(日期对象 -> 字符串) |
public Date parse(String source) | 解析(字符串 -> 日期对象) |
常用的模式字母
字母 | 含义 |
---|---|
y | 年 |
M | 月 |
d | 日 |
H | 小时(24小时制) |
h | 小时(12小时制) |
m | 分钟 |
s | 秒 |
S | 毫秒 |
E | 星期几 |
a | 上午/下午标记 |
使用示例:
// 创建日期对象
Date date = new Date();
// 使用默认格式化器
SimpleDateFormat defaultFormat = new SimpleDateFormat();
String defaultStr = defaultFormat.format(date);
System.out.println("默认格式:" + defaultStr); // 默认格式如:5/11/25 10:23 AM
// 自定义格式化器
SimpleDateFormat customFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
String customStr = customFormat.format(date);
System.out.println("自定义格式:" + customStr); // 如:2025年05月11日 10:23:45
// 解析字符串为日期对象
try {
String dateStr = "2025-05-11 10:23:45";
SimpleDateFormat parseFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date parsedDate = parseFormat.parse(dateStr);
System.out.println("解析结果:" + parsedDate);
} catch (Exception e) {
e.printStackTrace();
}
注意事项:
- SimpleDateFormat不是线程安全的,在多线程环境下应该为每个线程创建独立的实例
- 解析时,字符串的格式必须与SimpleDateFormat的模式匹配,否则会抛出ParseException
-
默认格式说明
无参构造函数SimpleDateFormat()
使用的默认格式取决于当前 Java 虚拟机的区域设置(Locale)。常见 Locale 的默认格式示例如下:Locale 日期默认格式 时间默认格式 日期 + 时间默认格式 中文(中国) yyyy-M-d
H:mm:ss
yyyy-M-d H:mm:ss
英文(美国) M/d/yyyy
h:mm:ss a
M/d/yyyy h:mm:ss a
日文(日本) yyyy/MM/dd
H:mm:ss
yyyy/MM/dd H:mm:ss
二、JDK8时间类
1. LocalDate类
● LocalDate表示不带时区的日期(年月日)
● 该类是不可变的,所有的修改操作都会返回新的实例
创建对象的方法
方法名 | 说明 |
---|---|
public static LocalDate now() | 获取当前日期 |
public static LocalDate of(int year, int month, int dayOfMonth) | 获取指定年、月、日的日期 |
public static LocalDate parse(CharSequence text) | 解析字符串获得LocalDate对象 |
常用方法说明
方法名 | 说明 |
---|---|
public int getYear() | 获取年份 |
public int getMonthValue() | 获取月份(1-12) |
public int getDayOfMonth() | 获取月份中的日期 |
public DayOfWeek getDayOfWeek() | 获取星期几 |
public LocalDate plusDays(long days) | 增加天数 |
public LocalDate plusMonths(long months) | 增加月数 |
public LocalDate plusYears(long years) | 增加年数 |
public LocalDate minusDays(long days) | 减少天数 |
public boolean isLeapYear() | 判断是否是闰年 |
public boolean isBefore(LocalDate other) | 判断是否在指定日期之前 |
public boolean isAfter(LocalDate other) | 判断是否在指定日期之后 |
public LocalDate withYear(int year) | 修改年份 |
public LocalDate withMonth(int month) | 修改月份 |
public LocalDate withDayOfMonth(int dayOfMonth) | 修改日期 |
使用示例:
// 获取当前日期
LocalDate today = LocalDate.now();
System.out.println("今天的日期:" + today); // 输出如:2025-05-11
// 创建指定日期
LocalDate birthday = LocalDate.of(1990, 5, 15);
System.out.println("生日:" + birthday); // 输出:1990-05-15
// 解析日期字符串
LocalDate parsedDate = LocalDate.parse("2025-05-11");
System.out.println("解析的日期:" + parsedDate);
// 获取年、月、日信息
System.out.println("年:" + today.getYear());
System.out.println("月:" + today.getMonthValue()); // 注意:这里返回1-12
System.out.println("日:" + today.getDayOfMonth());
System.out.println("星期:" + today.getDayOfWeek()); // 返回如:SUNDAY
// 日期计算
LocalDate tomorrow = today.plusDays(1);
LocalDate nextMonth = today.plusMonths(1);
LocalDate lastYear = today.minusYears(1);
// 日期比较
boolean isBefore = today.isBefore(tomorrow); // true
boolean isAfter = today.isAfter(lastYear); // true
// 修改日期
LocalDate changedDate = today.withYear(2030).withMonth(12).withDayOfMonth(25);
System.out.println("修改后的日期:" + changedDate); // 2030-12-25
2. LocalTime类
● LocalTime表示不带时区的时间(时分秒纳秒)
● 该类是不可变的,所有的修改操作都会返回新的实例
创建对象的方法
方法名 | 说明 |
---|---|
public static LocalTime now() | 获取当前时间 |
public static LocalTime of(int hour, int minute, int second) | 获取指定时、分、秒的时间 |
public static LocalTime parse(CharSequence text) | 解析字符串获得LocalTime对象 |
常用方法说明
方法名 | 说明 |
---|---|
public int getHour() | 获取小时 |
public int getMinute() | 获取分钟 |
public int getSecond() | 获取秒 |
public int getNano() | 获取纳秒 |
public LocalTime plusHours(long hours) | 增加小时 |
public LocalTime plusMinutes(long minutes) | 增加分钟 |
public LocalTime minusSeconds(long seconds) | 减少秒 |
public LocalTime withHour(int hour) | 修改小时 |
public LocalTime withMinute(int minute) | 修改分钟 |
public LocalTime withSecond(int second) | 修改秒 |
使用示例:
// 获取当前时间
LocalTime now = LocalTime.now();
System.out.println("当前时间:" + now); // 输出如:10:23:45.123
// 创建指定时间
LocalTime specificTime = LocalTime.of(20, 30, 0);
System.out.println("指定时间:" + specificTime); // 输出:20:30:00
// 解析时间字符串
LocalTime parsedTime = LocalTime.parse("10:23:45");
System.out.println("解析的时间:" + parsedTime);
// 获取时间信息
System.out.println("小时:" + now.getHour());
System.out.println("分钟:" + now.getMinute());
System.out.println("秒:" + now.getSecond());
// 时间计算
LocalTime inOneHour = now.plusHours(1);
LocalTime halfHourAgo = now.minusMinutes(30);
// 修改时间
LocalTime changedTime = now.withHour(6).withMinute(30).withSecond(0);
System.out.println("修改后的时间:" + changedTime); // 06:30:00
3. LocalDateTime类
● LocalDateTime表示不带时区的日期和时间(年月日时分秒)
● 该类是不可变的,包含了LocalDate和LocalTime的所有信息
创建对象的方法
方法名 | 说明 |
---|---|
public static LocalDateTime now() | 获取当前日期和时间 |
public static LocalDateTime of(int year, int month, int dayOfMonth, int hour, int minute, int second) | 获取指定日期和时间 |
public static LocalDateTime of(LocalDate date, LocalTime time) | 从LocalDate和LocalTime创建 |
常用方法说明
方法名 | 说明 |
---|---|
public LocalDate toLocalDate() | 转换为LocalDate |
public LocalTime toLocalTime() | 转换为LocalTime |
public int getYear() | 获取年份 |
public int getMonthValue() | 获取月份 |
public int getDayOfMonth() | 获取日 |
public int getHour() | 获取小时 |
public int getMinute() | 获取分钟 |
public LocalDateTime plusDays(long days) | 增加天数 |
public LocalDateTime minusMonths(long months) | 减少月数 |
public LocalDateTime withYear(int year) | 修改年份 |
public LocalDateTime withHour(int hour) | 修改小时 |
使用示例:
// 获取当前日期和时间
LocalDateTime now = LocalDateTime.now();
System.out.println("当前日期和时间:" + now); // 输出如:2025-05-11T10:23:45.123
// 创建指定日期和时间
LocalDateTime dateTime = LocalDateTime.of(2025, 5, 11, 10, 23, 45);
System.out.println("指定日期和时间:" + dateTime);
// 从LocalDate和LocalTime创建
LocalDateTime combined = LocalDateTime.of(LocalDate.now(), LocalTime.now());
// 获取部分信息
System.out.println("年:" + now.getYear());
System.out.println("月:" + now.getMonthValue());
System.out.println("小时:" + now.getHour());
// 日期时间计算
LocalDateTime nextWeek = now.plusWeeks(1);
LocalDateTime threeHoursLater = now.plusHours(3);
// 转换为LocalDate或LocalTime
LocalDate justDate = now.toLocalDate();
LocalTime justTime = now.toLocalTime();
4. ZoneId和时区处理
● ZoneId表示时区ID,用于识别不同的时区
● Java 8支持多种时区表示方式
创建ZoneId的方法
方法名 | 说明 |
---|---|
static Set<String> getAvailableZoneIds() | 获取Java中支持的所有时区 |
static ZoneId systemDefault() | 获取系统默认时区 |
static ZoneId of(String zoneId) | 获取一个指定时区 |
常用时区ID示例
时区ID | 说明 |
---|---|
"Asia/Shanghai" | 上海时间(北京时间) |
"America/New_York" | 纽约时间 |
"Europe/London" | 伦敦时间 |
"UTC" | 协调世界时 |
"GMT+8" | GMT偏移时间(北京时间) |
使用示例:
// 获取所有可用的时区ID
Set<String> zoneIds = ZoneId.getAvailableZoneIds();
System.out.println("可用时区数量:" + zoneIds.size());
// 获取系统默认时区
ZoneId defaultZone = ZoneId.systemDefault();
System.out.println("系统默认时区:" + defaultZone);
// 创建特定时区
ZoneId shanghaiZone = ZoneId.of("Asia/Shanghai");
ZoneId newYorkZone = ZoneId.of("America/New_York");
// 将LocalDateTime转换为特定时区的ZonedDateTime
LocalDateTime dateTime = LocalDateTime.now();
ZonedDateTime shanghaiTime = dateTime.atZone(shanghaiZone);
ZonedDateTime newYorkTime = dateTime.atZone(newYorkZone);
System.out.println("上海时间:" + shanghaiTime);
System.out.println("纽约时间:" + newYorkTime);
5. ZonedDateTime类
● ZonedDateTime表示带时区的日期和时间
● 用于处理不同时区之间的日期时间转换
创建对象的方法
方法名 | 说明 |
---|---|
static ZonedDateTime now() | 获取当前时间的ZonedDateTime对象 |
static ZonedDateTime of(LocalDateTime localDateTime, ZoneId zone) | 获取指定时间和时区的ZonedDateTime对象 |
static ZonedDateTime ofXxxx(...) | 获取指定时间的ZonedDateTime对象 |
常用方法说明
方法名 | 说明 |
---|---|
ZonedDateTime withXxx(时间) | 修改时间系列的方法 |
ZonedDateTime minusXxx(时间) | 减少时间系列的方法 |
ZonedDateTime plusXxx(时间) | 增加时间系列的方法 |
ZonedDateTime withZoneSameInstant(ZoneId zone) | 转换为另一个时区的相同时刻 |
ZonedDateTime withZoneSameLocal(ZoneId zone) | 转换为另一个时区的相同本地时间 |
使用示例:
// 获取当前带时区的日期时间
ZonedDateTime now = ZonedDateTime.now();
System.out.println("当前日期时间:" + now);
// 创建特定时区的日期时间
ZonedDateTime tokyoTime = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
System.out.println("东京时间:" + tokyoTime);
// 从LocalDateTime创建ZonedDateTime
LocalDateTime localDateTime = LocalDateTime.now();
ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of("Europe/Paris"));
System.out.println("巴黎时间:" + zonedDateTime);
// 时区转换(保持时刻相同)
ZonedDateTime newYorkTime = zonedDateTime.withZoneSameInstant(ZoneId.of("America/New_York"));
System.out.println("纽约时间(同一时刻):" + newYorkTime);
// 时区转换(保持本地时间相同)
ZonedDateTime sydneyTime = zonedDateTime.withZoneSameLocal(ZoneId.of("Australia/Sydney"));
System.out.println("悉尼时间(同一本地时间):" + sydneyTime);
6. Instant类
● Instant表示时间线上的一个点(时间戳),精确到纳秒
● 可以用于记录应用程序中的事件时间戳
创建对象的方法
方法名 | 说明 |
---|---|
static Instant now() | 获取当前时间的Instant对象(标准时间) |
static Instant ofEpochMilli(long epochMilli) | 根据(秒/毫秒/纳秒)获取Instant对象 |
ZonedDateTime atZone(ZoneId zone) | 指定时区 |
常用方法说明
方法名 | 说明 |
---|---|
boolean isXxx(Instant otherInstant) | 判断系列的方法 |
Instant minusXxx(long millisToSubtract) | 减少时间系列的方法 |
Instant plusXxx(long millisToSubtract) | 增加时间系列的方法 |
public long toEpochMilli() | 获取从1970-01-01T00:00:00Z开始的毫秒数 |
public long getEpochSecond() | 获取从1970-01-01T00:00:00Z开始的秒数 |
使用示例:
// 获取当前时刻
Instant now = Instant.now();
System.out.println("当前时刻:" + now); // 输出如:2025-05-11T02:23:45.123456Z
// 从毫秒数创建Instant
Instant fromMillis = Instant.ofEpochMilli(System.currentTimeMillis());
System.out.println("从毫秒数创建:" + fromMillis);
// 获取时间戳
long epochSecond = now.getEpochSecond();
System.out.println("秒数:" + epochSecond);
long epochMilli = now.toEpochMilli();
System.out.println("毫秒数:" + epochMilli);
// 时间计算
Instant future = now.plusMillis(10000); // 增加10秒
Instant past = now.minusSeconds(60); // 减少1分钟
// 时间比较
boolean isBefore = past.isBefore(now); // true
boolean isAfter = future.isAfter(now); // true
// 转换为ZonedDateTime
ZonedDateTime zonedDateTime = now.atZone(ZoneId.of("Asia/Shanghai"));
System.out.println("转换为上海时区:" + zonedDateTime);
7. DateTimeFormatter类
● DateTimeFormatter用于格式化和解析日期时间对象
● 该类是线程安全的,不像SimpleDateFormat
创建对象的方法
方法名 | 说明 |
---|---|
static DateTimeFormatter ofPattern(String pattern) | 通过格式模式字符串创建 |
常用方法说明
方法名 | 说明 |
---|---|
String format(TemporalAccessor temporal) | 按照指定方式格式化 |
TemporalAccessor parse(CharSequence text) | 解析字符串为时间对象 |
常用的预定义格式器
常量 | 说明 |
---|---|
ISO_LOCAL_DATE | 标准日期格式,如:2025-05-11 |
ISO_LOCAL_TIME | 标准时间格式,如:10:23:45 |
ISO_LOCAL_DATE_TIME | 标准日期时间格式,如:2025-05-11T10:23:45 |
ISO_OFFSET_DATE_TIME | 带时区偏移的日期时间,如:2025-05-11T10:23:45+08:00 |
ISO_ZONED_DATE_TIME | 带时区ID的日期时间,如:2025-05-11T10:23:45+08:00[Asia/Shanghai] |
使用示例:
// 创建日期时间对象
LocalDateTime dateTime = LocalDateTime.now();
// 使用预定义格式
String isoDate = dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE);
System.out.println("ISO日期:" + isoDate); // 如:2025-05-11
// 自定义格式
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒");
String formatted = dateTime.format(formatter);
System.out.println("自定义格式:" + formatted); // 如:2025年05月11日 10时23分45秒
// 带时区的格式化
ZonedDateTime zonedDateTime = ZonedDateTime.now();
DateTimeFormatter zoneFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z");
System.out.println(zonedDateTime.format(zoneFormatter)); // 如:2025-05-11 10:23:45 CST
// 解析字符串
String dateStr = "2025年05月11日";
DateTimeFormatter parseFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
LocalDate parsedDate = LocalDate.parse(dateStr, parseFormatter);
System.out.println("解析结果:" + parsedDate);
8. ChronoUnit枚举
● ChronoUnit是TemporalUnit接口的实现,提供了各种时间单位
● 用于在时间点之间进行计算和测量时间差
常用的ChronoUnit常量
常量 | 说明 |
---|---|
YEARS | 年 |
MONTHS | 月 |
WEEKS | 周 |
DAYS | 天 |
HOURS | 小时 |
MINUTES | 分钟 |
SECONDS | 秒 |
MILLIS | 毫秒 |
MICROS | 微秒 |
NANOS | 纳秒 |
HALF_DAYS | 半天 |
DECADES | 十年 |
CENTURIES | 世纪 |
MILLENNIA | 千年 |
ERAS | 纪元 |
使用示例:
// 创建两个时间点
LocalDateTime today = LocalDateTime.now();
LocalDateTime birthDate = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
// 计算时间差
long years = ChronoUnit.YEARS.between(birthDate, today);
long months = ChronoUnit.MONTHS.between(birthDate, today);
long days = ChronoUnit.DAYS.between(birthDate, today);
long hours = ChronoUnit.HOURS.between(birthDate, today);
long minutes = ChronoUnit.MINUTES.between(birthDate, today);
long seconds = ChronoUnit.SECONDS.between(birthDate, today);
long millis = ChronoUnit.MILLIS.between(birthDate, today);
long micros = ChronoUnit.MICROS.between(birthDate, today);
long nanos = ChronoUnit.NANOS.between(birthDate, today);
System.out.println("相差的年数: " + years);
System.out.println("相差的月数: " + months);
System.out.println("相差的周数: " + ChronoUnit.WEEKS.between(birthDate, today));
System.out.println("相差的天数: " + days);
System.out.println("相差的小时数: " + hours);
System.out.println("相差的分钟数: " + minutes);
System.out.println("相差的秒数: " + seconds);
System.out.println("相差的毫秒数: " + millis);
System.out.println("相差的微秒数: " + micros);
System.out.println("相差的纳秒数: " + nanos);
9. Duration和Period类
● Duration表示两个时间之间的间隔(秒、纳秒)
● Period表示两个日期之间的间隔(年、月、日)
● ChronoUnit用于计算两个"日期"间隔
创建Duration对象的方法
方法名 | 说明 |
---|---|
static Duration between(Temporal startInclusive, Temporal endExclusive) | 计算两个时间之间的差 |
static Duration ofDays(long days) | 创建指定天数的Duration |
static Duration ofHours(long hours) | 创建指定小时数的Duration |
Duration常用方法
方法名 | 说明 |
---|---|
long toDays() | 将时间间隔转换为天数 |
long toHours() | 将时间间隔转换为小时数 |
long toMinutes() | 将时间间隔转换为分钟数 |
long toMillis() | 将时间间隔转换为毫秒数 |
long toNanos() | 将时间间隔转换为纳秒数 |
创建Period对象的方法
方法名 | 说明 |
---|---|
static Period between(LocalDate startDateInclusive, LocalDate endDateExclusive) | 计算两个日期之间的差 |
static Period ofDays(int days) | 创建指定天数的Period |
static Period ofMonths(int months) | 创建指定月数的Period |
static Period ofYears(int years) | 创建指定年数的Period |
Period常用方法
方法名 | 说明 |
---|---|
int getYears() | 获取年数 |
int getMonths() | 获取月数 |
int getDays() | 获取天数 |
long toTotalMonths() | 获取总月数 |
使用示例:
// Duration示例 - 计算时间差
LocalDateTime today = LocalDateTime.now();
LocalDateTime birthDate = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
Duration duration = Duration.between(birthDate, today);
System.out.println("相差的时间间隔对象: " + duration); // 如:PT219637H27M42.639166S
System.out.println("===============================");
System.out.println("相差的天数: " + duration.toDays());
System.out.println("相差的小时数: " + duration.toHours());
System.out.println("相差的分钟数: " + duration.toMinutes());
System.out.println("相差的毫秒数: " + duration.toMillis());
System.out.println("相差的纳秒数: " + duration.toNanos());
// Period示例 - 计算日期差
LocalDate todayDate = LocalDate.now();
LocalDate birthDay = LocalDate.of(2000, 1, 1);
Period period = Period.between(birthDay, todayDate);
System.out.println("相差的时间间隔对象: " + period); // 如:P22Y6M17D
System.out.println("相差的年数: " + period.getYears());
System.out.println("相差的月数: " + period.getMonths());
System.out.println("相差的天数: " + period.getDays());
System.out.println("相差的总月数: " + period.toTotalMonths());
10. Local类之间的关系
JDK8的时间类中,Local系列(LocalDate、LocalTime、LocalDateTime)之间有明确的关系:
- LocalDateTime 可以看作是 LocalDate 和 LocalTime 的组合
- 可以通过相互转换方法在它们之间进行转换
转换关系
方法名 | 说明 |
---|---|
public LocalDate toLocalDate() | LocalDateTime转换成一个LocalDate对象 |
public LocalTime toLocalTime() | LocalDateTime转换成一个LocalTime对象 |
使用示例:
// LocalDateTime与LocalDate、LocalTime的转换
LocalDateTime now = LocalDateTime.now();
System.out.println("当前日期时间:" + now);
// 转换为LocalDate
LocalDate date = now.toLocalDate();
System.out.println("提取的日期:" + date);
// 转换为LocalTime
LocalTime time = now.toLocalTime();
System.out.println("提取的时间:" + time);
// 从LocalDate和LocalTime创建LocalDateTime
LocalDateTime combined = LocalDateTime.of(date, time);
System.out.println("组合后的日期时间:" + combined);
三、JDK7和JDK8时间类对比
比较项 | JDK7 | JDK8 |
---|---|---|
可变性 | 可变(线程不安全) | 不可变(线程安全) |
月份从几开始 | 0(0代表1月) | 1(1代表1月) |
日期格式化 | SimpleDateFormat(线程不安全) | DateTimeFormatter(线程安全) |
时区支持 | 有但不完善 | 完善(ZonedDateTime) |
API设计 | 不直观 | 直观、流畅 |
时间计算 | 复杂 | 简单 |
代码可读性 | 较差 | 较好 |
判断方法 | 麻烦 | 简单 |
计算时间间隔 | 复杂 | 简单(Duration/Period/ChronoUnit) |
JDK7的问题与JDK8的改进:
- 代码层面:JDK7代码麻烦,JDK8简单直观
- 安全层面:JDK7在多线程环境下容易出现数据安全问题,JDK8时间日期对象都是不可变的,解决了这个问题
四、从JDK7到JDK8的迁移
替换对应关系
Date
→Instant
或LocalDateTime
Calendar
→LocalDate
、LocalTime
、LocalDateTime
或ZonedDateTime
SimpleDateFormat
→DateTimeFormatter
转换代码示例
// JDK7的Date转换为JDK8的Instant
Date date = new Date();
Instant instant = date.toInstant();
// JDK7的Date转换为LocalDateTime
LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
// JDK8的Instant转换回JDK7的Date
Date newDate = Date.from(instant);
// JDK7的Calendar转换为ZonedDateTime
Calendar calendar = Calendar.getInstance();
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(calendar.toInstant(), calendar.getTimeZone().toZoneId());
// JDK8的ZonedDateTime转换回Calendar
Calendar newCalendar = GregorianCalendar.from(zonedDateTime);
五、面试常见问题
1. JDK8中为什么要引入新的日期时间API?
答:
旧的日期时间API存在诸多问题:
- 线程不安全(Date和SimpleDateFormat都是可变的)
- API设计不良(月份从0开始,年份从1900开始)
- 时区支持不足
- 操作复杂且容易出错
JDK8的新API修复了这些问题,提供了更好的设计和更丰富的功能。
2. SimpleDateFormat为什么不是线程安全的?
答:
SimpleDateFormat在格式化或解析过程中会修改内部状态,多线程同时使用同一个实例会导致数据错乱或异常。它内部使用的Calendar对象也是可变的,这进一步加剧了线程安全问题。
3. JDK8中如何实现日期和时间的加减运算?
答:
可以使用LocalDate、LocalTime或LocalDateTime的plus和minus方法,如plusDays()、minusMonths()等。还可以使用with*方法直接设置日期时间的某个字段,如withYear()、withMonth()等。
4. 如何在JDK7和JDK8的日期时间API之间进行转换?
答:
// Date转Instant
Date date = new Date();
Instant instant = date.toInstant();
// Instant转Date
Date newDate = Date.from(instant);
// LocalDateTime转Date
LocalDateTime localDateTime = LocalDateTime.now();
Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
Date date = Date.from(instant);
// Date转LocalDateTime
Date date = new Date();
LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
5. 如何处理不同时区的时间?
答:
JDK8中使用ZonedDateTime类,它可以表示带时区的完整日期时间信息,并能够正确处理时区转换和夏令时调整。
// 创建不同时区的时间
ZonedDateTime tokyoTime = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
ZonedDateTime newYorkTime = ZonedDateTime.now(ZoneId.of("America/New_York"));
// 时区转换
ZonedDateTime converted = tokyoTime.withZoneSameInstant(ZoneId.of("America/New_York"));
总结
-
JDK7的时间API(Date、Calendar、SimpleDateFormat)存在设计缺陷,使用不便且线程不安全。
-
JDK8引入的新时间API(LocalDate、LocalTime、LocalDateTime、Instant等)解决了旧API的问题:
- 不可变对象,保证线程安全
- API设计更合理,月份从1开始
- 功能更丰富,支持更多日期时间操作
- 支持链式调用,代码更简洁
-
在新项目中应优先使用JDK8的时间API,旧项目可以考虑逐步迁移。
-
选择合适的类:
- 只有日期:LocalDate
- 只有时间:LocalTime
- 日期和时间:LocalDateTime
- 时间戳:Instant
- 带时区:ZonedDateTime
- 时间间隔:Duration(秒、纳秒)或Period(年、月、日)
- 时间单位和计算:ChronoUnit
参考资料:部分JDK7时间类的说明和格式参考了黑马程序员的Java教程内容。API表格和示例代码也有部分来自黑马程序员的教程资料。