Java日期時間詳解(LocalDate、LocalTime、LocalDateTime)
前言
Java 中 1.8 之前有 date 類,date 類到了 1.8 大部分的方法被棄而且 date 類如果不格式化可讀性十分差,而 simpledateformat 方法中 format 和 parse 方法都是線程不安全的。
1.8 之后出現(xiàn)了 localdate、localdatetime、localtime 這些類,而這些類使用了 final 來修飾,使得這些類是不可變的,一旦實(shí)例化,值就固定了,有點(diǎn)類似于 String 類,所以這些類都是線程安全的。
localdate 年月日、localtime 時分秒、localdatetime 年月日時分秒
SimpleDateFormat 為什么是線程不安全
SimpleDateFormat 類主要負(fù)責(zé)日期的轉(zhuǎn)換與格式化等操作,在多線程的環(huán)境中,使用此類容易造成數(shù)據(jù)轉(zhuǎn)換及處理的不正確,因為 SimpleDateFormat 類并不是線程安全的,但在單線程環(huán)境下是沒有問題的。
SimpleDateFormat 在類注釋中也提醒大家不適用于多線程場景:

說的很清楚,SimpleDateFormat 不是線程安全的,多線程下需要為每個線程創(chuàng)建不同的實(shí)例。不安全的原因是因為使用了 Calendar 這個全局變量

format 方法在執(zhí)行中,會操作成員變量 calendar 來保存時間 calendar.setTime(date) 。
如果 SimpleDateFormat 是一個共享變量,SimpleDateFormat 中的 calendar 也就可以被多個線程訪問到,所以問題就出現(xiàn)了。
除了 format 方法以外,SimpleDateFormat 的 parse 方法也有同樣的問題。
至此,我們發(fā)現(xiàn)了 SimpleDateFormat 的弊端,所以為了解決這個問題就是不要把 SimpleDateFormat 當(dāng)做一個共享變量來使用。
解決 SimpleDateFormat 線程不安全的方法
- 每次使用就創(chuàng)建一個新的 SimpleDateFormat
class DateUtils {
public static Date parse(String formatPattern, String datestring) throws ParseException {
return new SimpleDateFormat(formatPattern).parse(datestring);
}
public static String format(String formatPattern, Date date) {
return new SimpleDateFormat(formatPattern).format(date);
}
}
- 加鎖
class DateUtils {
private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("mm:ss");
public static Date parse(String formatPattern, String datestring) throws ParseException {
synchronized (simpleDateFormat) {
return simpleDateFormat.parse(datestring);
}
}
public static String format(String formatPattern, Date date) {
synchronized (simpleDateFormat) {
return simpleDateFormat.format(date);
}
}
}
- 使用 ThreadLocal
class DateUtils {
private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("mm:ss");
}
};
public static Date parse(String formatPattern, String datestring) throws ParseException {
return threadLocal.get().parse(datestring);
}
public static String format(String formatPattern, Date date) {
return threadLocal.get().format(date);
}
}
- Java 8 引入了新的日期時間 API,并引入了線程安全的日期類
Instant:瞬時實(shí)例。LocalDate:本地日期,不包含具體時間 例如:2014-01-14 可以用來記錄生日、紀(jì)念日、加盟日等。LocalTime:本地時間,不包含日期。LocalDateTime:組合了日期和時間,但不包含時差和時區(qū)信息。ZonedDateTime:最完整的日期時間,包含時區(qū)和相對UTC或格林威治的時差。
新 API 還引入了 ZoneOffSet 和 ZoneId 類,使得解決時區(qū)問題更為簡便。文章下面為常用 API 使用
1、LocalDate API
LocalDate類的實(shí)例是一個不可變對象,它只提供了簡單的日期,并不含當(dāng)天的時間信息,這個類是不可變的和線程安全的。
| 方法 | 描述 |
|---|---|
| now | 根據(jù)當(dāng)前時間創(chuàng)建LocalDate對象 |
| of | 根據(jù)指定年月日創(chuàng)建LocalDate對象 |
| getYear | 獲得年份 |
| getMonthValue | 獲得月份 |
| getMonth | 獲得月份枚舉值 |
| getDayOfMonth | 獲得月份天數(shù)(1-31) |
| getDayOfWeek | 獲得星期幾 |
| getDayOfYear | 獲得年份中的第幾天(1-366) |
| lengthOfYear | 獲得當(dāng)年總天數(shù) |
| lengthOfMonth | 獲得當(dāng)月總天數(shù) |
| toEpochDay | 與時間紀(jì)元(1970年1月1日)相差的天數(shù) |
| plusDays | 加天 |
| plusWeeks | 加周 |
| plusMonths | 加月 |
| plusYears | 加年 |
| minusDays | 減年 |
| minusWeeks | 減周 |
| minusMonths | 減月 |
| minusYears | 減年 |
| withYear | 替換年份 |
| withYear | 替換年份 |
| withDayOfMonth | 替換日期 |
| withDayOfYear | 替換日期 |
| isBefore | 是否日期在之前 |
| isAfter | 是否日期在之后 |
| isEqual | 是否是當(dāng)前日期 |
| isleapYear | 是否是閏年 |
代碼如下(示例):
// A.獲取
//(1)獲取當(dāng)前日期 2022-04-20
System.out.println(LocalDate.now());
//(2)獲取指定日期 2014-03-18
System.out.println(LocalDate.of(2014, 3, 18));
//(3)獲取日期的年份 2022
System.out.println(LocalDate.now().getYear());
//(4)獲取日期的月份 4
System.out.println(LocalDate.now().getMonthValue());
//(5)獲取日期的日子 20
System.out.println(LocalDate.now().getDayOfMonth());
//(6)獲取日期的星期 WEDNESDAY
System.out.println(LocalDate.now().getDayOfWeek());
//(7)當(dāng)天所在這一年的第幾天 110
System.out.println(LocalDate.now().getDayOfYear());
//(8)獲取當(dāng)年天數(shù) 365
System.out.println(LocalDate.now().lengthOfYear());
//(9)獲取當(dāng)月天數(shù) 30
System.out.println(LocalDate.now().lengthOfMonth());
//(10)與時間紀(jì)元(1970年1月1日)相差的天數(shù),負(fù)數(shù)表示在時間紀(jì)元之前多少天 19102
System.out.println(LocalDate.now().toEpochDay());
// B.運(yùn)算
//(1)加一天
System.out.println("加1天:" + LocalDate.now().plusDays(1));
//(2)加一周
System.out.println("加1周:" + LocalDate.now().plusWeeks(1));
//(3)加一月
System.out.println("加1月:" + LocalDate.now().plusMonths(1));
//(4)加一年
System.out.println("加1年:" + LocalDate.now().plusYears(1));
//(5)減一天
System.out.println("減1天:" + LocalDate.now().minusDays(1));
//(6)減一周
System.out.println("減1周:" + LocalDate.now().minusWeeks(1));
//(7)減一月
System.out.println("減1月:" + LocalDate.now().minusMonths(1));
//(8)減一年
System.out.println("減1年:" + LocalDate.now().minusYears(1));
// C.替換
//(1)替換年份
System.out.println("替換年份為1:" + LocalDate.now().withYear(1));
//(2)替換月份
System.out.println("替換月份為1:" + LocalDate.now().withMonth(1));
//(3)替換日子
System.out.println("替換日期為1:" + LocalDate.now().withDayOfMonth(1));
//(4)替換天數(shù)
System.out.println("替換天數(shù)為1:" + LocalDate.now().withDayOfYear(1));
// D.比較
//(1)是否在當(dāng)天之前
System.out.println("是否在當(dāng)天之前:" + LocalDate.now().minusDays(1).isBefore(LocalDate.now()));
//(2)是否在當(dāng)天之后
System.out.println("是否在當(dāng)天之后:" + LocalDate.now().plusDays(1).isAfter(LocalDate.now()));
//(3)是否在當(dāng)天
System.out.println("是否在當(dāng)天:" + LocalDate.now().isEqual(LocalDate.now()));
//(4)是否是閏年
System.out.println("今年是否是閏年:" + LocalDate.now().isLeapYear());
2、LocalTime API
LocalTime是一個不可變的時間對象,代表一個時間,格為 時 - 分 - 秒,時間表示為納秒精度,這個類是不可變的和線程安全的。
| 方法 | 描述 |
|---|---|
| static LocalTime now() | 獲取默認(rèn)時區(qū)的當(dāng)前時間 |
| static LocalTime now(ZoneId zone) | 獲取指定時區(qū)的當(dāng)前時間 |
| static LocalTime now(Clock clock) | 從指定時鐘獲取當(dāng)前時間 |
| of | 根據(jù)指定的時、分、秒獲取LocalTime 實(shí)例 |
| getHour | 獲取小時字段 |
| getMinute | 獲取分鐘字段 |
| getSecond | 獲取秒字段 |
| getNano | 獲取納秒字段 |
| plusHours | 增加小時數(shù) |
| plusMinutes | 增加分鐘數(shù) |
| plusSeconds | 增加秒數(shù) |
| plusNanos | 增加納秒數(shù) |
| minusHours | 減少小時數(shù) |
| minusMinutes | 減少分鐘數(shù) |
| minusSeconds | 減少秒數(shù) |
| minusNanos | 減少納秒數(shù) |
| compareTo | 時間與另一個時間比較 |
| isAfter | 檢查時間是否在指定時間之后 |
| isBefore | 檢查時間是否在指定時間之前 |
代碼如下(示例):
// A.獲取
//(1)獲取默認(rèn)時區(qū)的當(dāng)前時間 14:11:31.294
System.out.println(LocalTime.now());
//(2)獲取指定時區(qū)的當(dāng)前時間 14:11:31.392
System.out.println(LocalTime.now(ZoneId.of("Asia/Shanghai")));
//(3)從指定時鐘獲取當(dāng)前時間 14:11:31.392
System.out.println(LocalTime.now(Clock.systemDefaultZone()));
//(4)指定獲取時分秒
System.out.println(LocalTime.of(12, 30, 30));
//(5)指定獲取時分
System.out.println(LocalTime.of(12, 30));
//(6)指定獲取時分秒納秒
System.out.println(LocalTime.of(12, 30, 30, 123));
//(7)獲取小時字段
System.out.println("時: " + LocalTime.now().getHour());
//(8)獲取分鐘字段
System.out.println("時: " + LocalTime.now().getMinute());
//(9)獲取秒字段
System.out.println("時: " + LocalTime.now().getSecond());
//(10)獲取納秒字段
System.out.println("時: " + LocalTime.now().getNano());
// B.計算
//(1)增加一小時
System.out.println("增加1小時: " + LocalTime.now().plusHours(1));
//(2)增加三十分鐘
System.out.println("增加30分鐘: " + LocalTime.now().plusMinutes(30));
//(3)增加三十秒
System.out.println("增加30秒: " + LocalTime.now().plusSeconds(30));
//(4)增加一萬納秒
System.out.println("增加10000納秒:" + LocalTime.now().plusNanos(10000));
//(5)減少一小時
System.out.println("減少1小時: " + LocalTime.now().minusHours(1));
//(6)減少三十分鐘
System.out.println("減少30分鐘: " + LocalTime.now().minusMinutes(30));
//(7)減少三十秒
System.out.println("減少30秒: " + LocalTime.now().minusSeconds(30));
//(8)減少一萬納秒
System.out.println("減少10000納秒:" + LocalTime.now().minusNanos(10000));
// C.比較
//(1)時間與另一個時間比較 0(相等)正數(shù)(大)負(fù)數(shù)(?。?
System.out.println(LocalTime.now().compareTo(LocalTime.now()));
//(2)檢查時間是否在指定時間之后
System.out.println(LocalTime.now().isAfter(LocalTime.now()));
//(3)檢查時間是否在指定時間之前
System.out.println(LocalTime.now().isBefore(LocalTime.now()));
3、LocalDateTime API
LocalDateTime是一個不可變的日期時間對象,代表日期時間,格式為 年 - 月 - 日 - 時 - 分 - 秒,這個類是不可變的和線程安全的。
| 方法 | 描述 |
|---|---|
| static LocalDateTime now() | 獲取默認(rèn)時區(qū)的當(dāng)前日期時間 |
| static LocalDateTime now(Clock clock) | 從指定時鐘獲取當(dāng)前日期時間 |
| static LocalDateTime now(ZoneId zone) | 獲取指定時區(qū)的當(dāng)前日期時間 |
| static LocalDateTime of(LocalDate date, LocalTime time) | 根據(jù)日期和時間對象獲取LocalDateTime 實(shí)例 |
| static LocalDateTime of(int year, Month month, int dayOfMonth, int hour, int minute, int second) | second) 根據(jù)指定的年、月、日、時、分、秒獲取LocalDateTime 實(shí)例 |
| getYear | 獲取年份 |
| getMonth | 使用月份枚舉類獲取月份 |
| getDayOfMonth | 獲取日期在該月是第幾天 |
| getDayOfWeek | 獲取日期是星期幾 |
| getDayOfYear | 獲取日期在該年是第幾天 |
| getHour | 獲取小時 |
| getMinute | 獲取分鐘 |
| getSecond | 獲取秒 |
| getNano | 獲取納秒 |
| plusYears | 增加年 |
| plusMonths | 增加月 |
| plusWeeks | 增加周 |
| plusDays | 增加天 |
| plusHours | 增加小時 |
| plusMinutes | 增加分 |
| plusSeconds | 增加秒 |
| plusNanos | 增加納秒 |
| minusYears | 減少年 |
| minusMonths | 減少月 |
| meminusWeeks | 減少周 |
| minusDays | 減少天 |
| minusHours | 減少小時 |
| minusMinutes | 減少分 |
| minusSeconds | 減少秒 |
| minusNanos | 減少納秒 |
| isEqual | 判斷日期時間是否相等 |
| isBefore | 檢查是否在指定日期時間之前 |
| isAfter | 檢查是否在指定日期時間之后 |
| toLocalDate | 獲取日期部分 |
| toLocalTime | 獲取時間部分 |
代碼如下(示例):
// A.獲取
//(1)獲取默認(rèn)時區(qū)的當(dāng)前日期時間
System.out.println(LocalDateTime.now());
//(2)獲取指定時區(qū)的當(dāng)前日期時間
System.out.println(LocalDateTime.now(ZoneId.of("Asia/Shanghai")));
//(3)從指定時鐘獲取當(dāng)前日期時間
System.out.println(LocalDateTime.now(Clock.systemDefaultZone()));
//(4)根據(jù)日期和時間對象獲取LocalDateTime實(shí)例
System.out.println(LocalDateTime.of(LocalDate.now(), LocalTime.now()));
//(5)根據(jù)指定的年、月、日、時、分、秒獲取LocalDateTime實(shí)例
System.out.println(LocalDateTime.of(2019, 12, 7, 21, 48, 50));
//(6)獲取年份
System.out.println("年 : " + LocalDateTime.now().getYear());
//(7)使用月份枚舉類獲取月份
System.out.println("月(英文) : " + LocalDateTime.now().getMonth());
//(8) 使用月份數(shù)字類獲取月份
System.out.println(" 月(數(shù)字英文): " + LocalDateTime.now().getMonth().getValue());
//(9)獲取日期在該月是第幾天
System.out.println("天 : " + LocalDateTime.now().getDayOfMonth());
//(10)獲取日期是星期幾(英文)
System.out.println("星期幾(英文) : " + LocalDateTime.now().getDayOfWeek());
//(11)獲取日期是星期幾(數(shù)字英文)
System.out.println("星期幾(數(shù)字英文) : " + LocalDateTime.now().getDayOfWeek().getValue());
//(12)獲取日期在該年是第幾天
System.out.println("本年的第幾天 : " + LocalDateTime.now().getDayOfYear());
//(13)獲取小時
System.out.println("時: " + LocalDateTime.now().getHour());
//(14)獲取分鐘
System.out.println("分: " + LocalDateTime.now().getMinute());
//(15)獲取秒
System.out.println("秒: " + LocalDateTime.now().getSecond());
//(16)獲取納秒
System.out.println("納秒: " + LocalDateTime.now().getNano());
//(17)獲取日期部分
System.out.println(LocalDateTime.now().toLocalDate());
//(18)獲取時間部分
System.out.println(LocalDateTime.now().toLocalTime());
// B.計算
//(1)增加天數(shù)
System.out.println("增加天數(shù) : " + LocalDateTime.now().plusDays(1));
//(2)增加周數(shù)
System.out.println("增加周數(shù) : " + LocalDateTime.now().plusWeeks(1));
//(3)增加月數(shù)
System.out.println("增加月數(shù) : " + LocalDateTime.now().plusMonths(1));
//(4)增加年數(shù)
System.out.println("增加年數(shù) : " + LocalDateTime.now().plusYears(1));
//(5)減少天數(shù)
System.out.println("減少天數(shù) : " + LocalDateTime.now().minusDays(1));
//(6)減少月數(shù)
System.out.println("減少月數(shù) : " + LocalDateTime.now().minusMonths(1));
//(7)減少周數(shù)
System.out.println("減少周數(shù) : " + LocalDateTime.now().minusWeeks(1));
//(8)減少年數(shù)
System.out.println("減少年數(shù) : " + LocalDateTime.now().minusYears(1));
//(9)增加小時
System.out.println("增加1小時: " + LocalDateTime.now().plusHours(1));
//(10)增加分鐘
System.out.println("增加30分鐘: " + LocalDateTime.now().plusMinutes(30));
//(11)增加秒數(shù)
System.out.println("增加30秒: " + LocalDateTime.now().plusSeconds(30));
//(12)增加納秒
System.out.println("增加10000納秒:" + LocalDateTime.now().plusNanos(10000));
//(13)減少小時
System.out.println("減少1小時:" + LocalDateTime.now().minusHours(1));
//(14)減少分鐘
System.out.println("減少30分鐘:" + LocalDateTime.now().minusMinutes(30));
//(15)減少秒數(shù)
System.out.println("減少30秒: " + LocalDateTime.now().minusSeconds(30));
//(16)減少納秒
System.out.println("減少10000納秒:" + LocalDateTime.now().minusNanos(10000));
// C.比較
//(1)判斷日期時間是否相等
System.out.println(LocalDateTime.now().isEqual(LocalDateTime.now()));
//(2)檢查是否在指定日期時間之前
System.out.println(LocalDateTime.now().isBefore(LocalDateTime.now()));
//(3)檢查是否在指定日期時間之后
System.out.println(LocalDateTime.now().isAfter(LocalDateTime.now()));
4、轉(zhuǎn)換關(guān)系
4.1、LocalDateTime 與 LocalDate 之間的轉(zhuǎn)換
//(1)LocalDateTime轉(zhuǎn)化為LocalDate LocalDateTime localDateTime = LocalDateTime.now(); LocalDate localDate = localDateTime.toLocalDate(); System.out.println(localDate); //(2)LocalDate轉(zhuǎn)化為LocalDateTime LocalDate localDate = LocalDate.now(); LocalDateTime localDateTime1 = localDate.atStartOfDay(); LocalDateTime localDateTime2 = localDate.atTime(8,20,33); LocalDateTime localDateTime3 = localDate.atTime(LocalTime.now()); System.out.println(localDateTime1); System.out.println(localDateTime2); System.out.println(localDateTime3);
4.2、LocalDateTime 與 Date 之間的轉(zhuǎn)換
//(1)LocalDateTime轉(zhuǎn)化為Date LocalDateTime localDateTime = LocalDateTime.now(); Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()); System.out.println(date); //(2)Date轉(zhuǎn)化為LocalDateTime Date todayDate = new Date(); LocalDateTime ldt = todayDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); System.out.println(ldt);
4.3、LocalDate 與 Date 之間的轉(zhuǎn)換
//(1)localDate轉(zhuǎn)化為Date LocalDate localDate = LocalDate.now(); ZoneId zoneId = ZoneId.systemDefault(); Date date = Date.from(localDate.atStartOfDay().atZone(zoneId).toInstant()); System.out.println(date); //(2)Date轉(zhuǎn)化為localDate Date date1 = new Date(); ZoneId zoneId1 = ZoneId.systemDefault(); LocalDate localDate1 = date1.toInstant().atZone(zoneId1).toLocalDate(); System.out.println(localDate1);
4.4、LocalDate 與 String 之間的轉(zhuǎn)換
//(1)從文本字符串獲取LocalDate實(shí)例
LocalDate localdate = LocalDate.parse("2022-04-21");
System.out.println(localdate);
//(2)使用特定格式化形式從文本字符串獲取LocalDate實(shí)例
String str = "2019-03-03";
DateTimeFormatter fmt1 = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate date = LocalDate.parse(str, fmt1);
System.out.println(date);
//(3)使用特定格式化形式將LocalDate轉(zhuǎn)為字符串
LocalDate today = LocalDate.now();
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy/MM/dd");
String dateStr = today.format(fmt);
System.out.println(dateStr);
4.5、LocalTime 與 String 之間的轉(zhuǎn)換
// (1)字符串轉(zhuǎn)為LocalTime
LocalTime localdate = LocalTime.parse("12:01:02");
System.out.println(localdate);
// (2)使用特定格式化將字符串轉(zhuǎn)為LocalTime
DateTimeFormatter fmt1 = DateTimeFormatter.ofPattern("HH:mm:ss");
LocalTime date = LocalTime.parse("12:01:02", fmt1);
System.out.println(date);
// (3)LocalTime轉(zhuǎn)為字符串
LocalTime toTime = LocalTime.now();
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("HH:mm:ss");
String dateStr = toTime.format(fmt);
System.out.println(dateStr);
4.6、LocalDateTime 與 String 之間的轉(zhuǎn)換
//(1)字符串轉(zhuǎn)為LocalDateTime
LocalDateTime ldt2 = LocalDateTime.parse("2019-12-07T21:20:06.303995200");
System.out.println(ldt2);
//(2)使用特定格式化將字符串轉(zhuǎn)為LocalDateTime
DateTimeFormatter df1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime ldt3 = LocalDateTime.parse("2019-12-07 21:20:06", df1);
System.out.println(ldt3);
//(3)LocalDateTime轉(zhuǎn)為字符串
LocalDateTime today = LocalDateTime.now();
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy/MM/dd");
String dateStr = today.format(fmt);
System.out.println(dateStr);
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
springboot+redis自定義注解實(shí)現(xiàn)發(fā)布訂閱的實(shí)現(xiàn)代碼
在Redis中客戶端可以通過訂閱特定的頻道來接收發(fā)送至該頻道的消息,本文主要介紹了springboot+redis自定義注解實(shí)現(xiàn)發(fā)布訂閱,具有一定的參考價值,感興趣的可以了解一下2023-08-08
Java實(shí)現(xiàn)調(diào)用ElasticSearch?API的示例詳解
這篇文章主要為大家詳細(xì)介紹了Java調(diào)用ElasticSearch?API的效果資料,文中的示例代碼講解詳細(xì),具有一定的參考價值,感興趣的可以了解一下2023-03-03
Java后臺接口開發(fā)初步實(shí)戰(zhàn)教程
下面小編就為大家分享一篇 Java后臺接口開發(fā)初步實(shí)戰(zhàn)教程,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-01-01
idea使用spring Initializr 快速搭建springboot項目遇到的坑
這篇文章主要介紹了idea使用spring Initializr 快速搭建springboot項目遇到的坑,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-11-11

