Java 中的所有時(shí)間操作類(lèi)詳解及使用實(shí)戰(zhàn)
一、早期時(shí)間類(lèi)
1.java.util.Date
- 簡(jiǎn)介:最早的時(shí)間類(lèi),表示某一時(shí)刻。
- 常用方法:
getTime():返回自1970年1月1日00:00:00 GMT以來(lái)的毫秒數(shù)。toString():返回日期字符串。before(Date date)/after(Date date):比較日期先后。
- 缺點(diǎn):大部分方法已廢棄,線程不安全,易出錯(cuò)。
Date date = new Date(); System.out.println(date); // 當(dāng)前時(shí)間
2.java.util.Calendar
- 簡(jiǎn)介:用于更復(fù)雜的日期計(jì)算(如加減天數(shù)),是抽象類(lèi),常用子類(lèi)
GregorianCalendar。 - 常用方法:
add(int field, int amount):增加指定時(shí)間字段的值。get(int field):獲取指定字段的值(如 YEAR, MONTH)。set(int field, int value):設(shè)置指定字段的值。
- 缺點(diǎn):API設(shè)計(jì)復(fù)雜,線程不安全。
Calendar cal = Calendar.getInstance(); cal.add(Calendar.DAY_OF_MONTH, 5); // 當(dāng)前日期加5天 Date newDate = cal.getTime();
3.java.text.SimpleDateFormat
- 簡(jiǎn)介:用于日期格式化和解析。
- 常用方法:
format(Date date):格式化日期為字符串。parse(String source):解析字符串為日期對(duì)象。
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String str = sdf.format(new Date());
Date date = sdf.parse("2024-06-01 12:00:00");二、Java 8 引入的現(xiàn)代時(shí)間API(推薦)
所有類(lèi)都在 java.time 包下,線程安全,易于使用。
1.LocalDate
- 簡(jiǎn)介:只表示日期(不含時(shí)間),如 2024-06-01。
- 常用方法:
now():當(dāng)前日期。of(int year, int month, int dayOfMonth):指定日期。plusDays(long days):加天數(shù)。minusDays(long days):減天數(shù)。getYear(),getMonth(),getDayOfMonth():獲取年月日。
LocalDate today = LocalDate.now(); LocalDate tomorrow = today.plusDays(1);
2.LocalTime
- 簡(jiǎn)介:只表示時(shí)間(不含日期),如 14:30:00。
- 常用方法:
now():當(dāng)前時(shí)間。of(int hour, int minute, int second):指定時(shí)間。plusHours(long hours):加小時(shí)。
LocalTime time = LocalTime.now(); LocalTime later = time.plusHours(2);
3.LocalDateTime
- 簡(jiǎn)介:表示日期和時(shí)間,不含時(shí)區(qū)。
- 常用方法:
now():當(dāng)前日期時(shí)間。of(int year, int month, int day, int hour, int minute, int second):指定日期時(shí)間。plusDays(),plusHours():加天/小時(shí)。
LocalDateTime ldt = LocalDateTime.now(); LocalDateTime future = ldt.plusDays(3).plusHours(5);
4.ZonedDateTime
- 簡(jiǎn)介:日期時(shí)間+時(shí)區(qū)。
- 常用方法:
now(ZoneId zone):當(dāng)前時(shí)區(qū)的日期時(shí)間。of(...):指定日期時(shí)間和時(shí)區(qū)。
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));5.Instant
- 簡(jiǎn)介:時(shí)間戳,精確到納秒,表示自1970年1月1日00:00:00 UTC的瞬間。
- 常用方法:
now():當(dāng)前時(shí)間戳。ofEpochMilli(long epochMilli):通過(guò)毫秒數(shù)創(chuàng)建。
Instant instant = Instant.now(); long epochMilli = instant.toEpochMilli();
6.Duration&Period
Duration:表示兩個(gè)時(shí)間點(diǎn)之間的時(shí)間量(秒、納秒),適用于LocalTime、LocalDateTime、Instant。Period:表示兩個(gè)日期之間的時(shí)間量(年、月、日),適用于LocalDate。
Duration duration = Duration.between(LocalTime.now(), LocalTime.now().plusHours(2)); Period period = Period.between(LocalDate.now(), LocalDate.now().plusDays(10));
7.DateTimeFormatter
- 簡(jiǎn)介:格式化和解析日期時(shí)間,線程安全。
- 常用方法:
format(TemporalAccessor temporal):格式化為字符串。parse(CharSequence text):解析字符串為日期時(shí)間對(duì)象。
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String str = formatter.format(LocalDateTime.now());
LocalDateTime ldt = LocalDateTime.parse("2024-06-01 12:00:00", formatter);8.ZoneId&ZoneOffset
ZoneId:時(shí)區(qū)ID。ZoneOffset:時(shí)區(qū)偏移量。
ZoneId zoneId = ZoneId.of("Asia/Shanghai");
ZoneOffset offset = ZoneOffset.of("+08:00");三、其他相關(guān)類(lèi)
1.java.sql.Date、java.sql.Time、java.sql.Timestamp
- 用于數(shù)據(jù)庫(kù)操作,繼承自
java.util.Date,通常用于 JDBC。
四、時(shí)間類(lèi)關(guān)系圖
java.util.Date
├─ java.sql.Date
├─ java.sql.Time
└─ java.sql.Timestamp
java.util.Calendar
└─ java.util.GregorianCalendar
java.text.SimpleDateFormat
java.time.LocalDate
java.time.LocalTime
java.time.LocalDateTime
java.time.ZonedDateTime
java.time.Instant
java.time.Duration
java.time.Period
java.time.DateTimeFormatter
java.time.ZoneId
java.time.ZoneOffset五、常用時(shí)間操作示例??????
// 當(dāng)前日期時(shí)間
LocalDateTime now = LocalDateTime.now();
// 日期加減
LocalDateTime tomorrow = now.plusDays(1);
LocalDateTime lastWeek = now.minusWeeks(1);
// 日期格式化
String str = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
// 字符串轉(zhuǎn)日期
LocalDateTime ldt = LocalDateTime.parse("2024-06-01 12:00:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
// 時(shí)間差
Duration duration = Duration.between(now, tomorrow);
long hours = duration.toHours();六、建議
- 新項(xiàng)目?jī)?yōu)先使用
java.time包下的類(lèi),線程安全、功能豐富、易用。 - 舊項(xiàng)目如果使用了
Date、Calendar,建議逐步遷移到新的時(shí)間API。 - 格式化和解析請(qǐng)用
DateTimeFormatter,避免SimpleDateFormat的線程安全問(wèn)題。
七. 各時(shí)間類(lèi)詳細(xì)用法與實(shí)戰(zhàn)場(chǎng)景
1.1 LocalDate
場(chǎng)景:只關(guān)心日期(如生日、節(jié)假日、賬單日期)
????????????
LocalDate birthday = LocalDate.of(1995, 6, 1); LocalDate today = LocalDate.now(); // 判斷是否是今天 boolean isToday = birthday.equals(today); // 獲取本月第一天 LocalDate firstDay = today.withDayOfMonth(1); // 獲取本月最后一天 LocalDate lastDay = today.withDayOfMonth(today.lengthOfMonth()); // 日期加減 LocalDate nextWeek = today.plusWeeks(1); LocalDate lastYear = today.minusYears(1);
1.2 LocalTime
場(chǎng)景:只關(guān)心時(shí)間(如打卡時(shí)間、會(huì)議時(shí)間)
LocalTime startTime = LocalTime.of(9, 0, 0); LocalTime endTime = LocalTime.of(18, 0, 0); // 判斷是否在工作時(shí)間內(nèi) LocalTime now = LocalTime.now(); boolean isWorkTime = !now.isBefore(startTime) && !now.isAfter(endTime); // 時(shí)間加減 LocalTime lunchTime = startTime.plusHours(4);
1.3 LocalDateTime
場(chǎng)景:關(guān)心日期和時(shí)間(如訂單創(chuàng)建時(shí)間、日志時(shí)間)
LocalDateTime orderTime = LocalDateTime.now(); LocalDateTime expireTime = orderTime.plusHours(2); // 比較先后 boolean isExpired = LocalDateTime.now().isAfter(expireTime); // 獲取年月日時(shí)分秒 int year = orderTime.getYear(); int month = orderTime.getMonthValue(); int day = orderTime.getDayOfMonth(); int hour = orderTime.getHour(); int minute = orderTime.getMinute();
1.4 ZonedDateTime
場(chǎng)景:全球化應(yīng)用,關(guān)心時(shí)區(qū)(如國(guó)際會(huì)議、航班時(shí)間)
ZonedDateTime shanghaiTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
ZonedDateTime newYorkTime = shanghaiTime.withZoneSameInstant(ZoneId.of("America/New_York"));
// 獲取時(shí)區(qū)
ZoneId zoneId = shanghaiTime.getZone();1.5 Instant
場(chǎng)景:存儲(chǔ)時(shí)間戳(如數(shù)據(jù)庫(kù)、消息隊(duì)列)、高精度時(shí)間計(jì)算
Instant now = Instant.now(); long timestamp = now.toEpochMilli(); // 毫秒時(shí)間戳 // Instant與LocalDateTime互轉(zhuǎn) LocalDateTime ldt = LocalDateTime.ofInstant(now, ZoneId.systemDefault()); Instant instant = ldt.atZone(ZoneId.systemDefault()).toInstant();
1.6 Duration & Period
場(chǎng)景:計(jì)算時(shí)間差
// Duration: 時(shí)間點(diǎn)之間的差(如秒、納秒) Duration duration = Duration.between(ldt1, ldt2); long seconds = duration.getSeconds(); long minutes = duration.toMinutes(); // Period: 日期之間的差(如年、月、日) Period period = Period.between(date1, date2); int days = period.getDays(); int months = period.getMonths(); int years = period.getYears();
2. 時(shí)間比較與排序
LocalDateTime t1 = LocalDateTime.of(2024, 6, 1, 12, 0); LocalDateTime t2 = LocalDateTime.of(2024, 6, 2, 12, 0); boolean isBefore = t1.isBefore(t2); // true boolean isAfter = t1.isAfter(t2); // false boolean isEqual = t1.isEqual(t2); // false // 排序 List<LocalDateTime> list = Arrays.asList(t1, t2); list.sort(Comparator.naturalOrder());
3. 與舊API兼容與轉(zhuǎn)換
Date <-> LocalDateTime
// Date轉(zhuǎn)LocalDateTime Date date = new Date(); Instant instant = date.toInstant(); LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); // LocalDateTime轉(zhuǎn)Date LocalDateTime ldt2 = LocalDateTime.now(); Instant instant2 = ldt2.atZone(ZoneId.systemDefault()).toInstant(); Date date2 = Date.from(instant2);
4. 時(shí)間格式化與解析(自定義格式)
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
// 格式化
String str = LocalDateTime.now().format(fmt);
// 解析
LocalDateTime ldt = LocalDateTime.parse("2024/06/01 13:45:30", fmt);5. 時(shí)區(qū)處理
// 獲取所有可用時(shí)區(qū)
Set<String> zoneIds = ZoneId.getAvailableZoneIds();
// 當(dāng)前時(shí)間在不同地區(qū)
ZonedDateTime utcTime = ZonedDateTime.now(ZoneId.of("UTC"));
ZonedDateTime tokyoTime = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));6. 常見(jiàn)坑與注意事項(xiàng)
- 月份從1開(kāi)始(不是0)
LocalDateTime沒(méi)有時(shí)區(qū)信息,存儲(chǔ)時(shí)要注意轉(zhuǎn)換SimpleDateFormat線程不安全,推薦用DateTimeFormatterDuration不能用于日期(用Period)Date和Calendar已過(guò)時(shí),避免新項(xiàng)目使用- 時(shí)間操作建議統(tǒng)一使用一種API,避免混用造成混亂
7. 進(jìn)階用法
7.1 時(shí)間區(qū)間判斷
LocalDateTime now = LocalDateTime.now(); LocalDateTime start = LocalDateTime.of(2024, 6, 1, 9, 0); LocalDateTime end = LocalDateTime.of(2024, 6, 1, 18, 0); boolean inRange = !now.isBefore(start) && !now.isAfter(end);
7.2 按天、月、年循環(huán)
// 按天循環(huán)
LocalDate start = LocalDate.of(2024, 6, 1);
LocalDate end = LocalDate.of(2024, 6, 10);
for (LocalDate d = start; !d.isAfter(end); d = d.plusDays(1)) {
System.out.println(d);
}7.3 時(shí)間差友好顯示
Duration duration = Duration.between(ldt1, ldt2); long hours = duration.toHours(); long minutes = duration.toMinutes() % 60; System.out.println(hours + "小時(shí)" + minutes + "分鐘");
8. 代碼片段匯總
// 獲取當(dāng)前時(shí)間戳(秒/毫秒)
long epochSecond = Instant.now().getEpochSecond();
long epochMilli = Instant.now().toEpochMilli();
// 時(shí)間加減
LocalDateTime t = LocalDateTime.now().plusDays(3).minusHours(2);
// 時(shí)間格式化
String s = t.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
// 時(shí)間解析
LocalDateTime parsed = LocalDateTime.parse("2024-06-01 12:00:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
// 時(shí)間區(qū)間
LocalDate start = LocalDate.of(2024, 6, 1);
LocalDate end = LocalDate.of(2024, 6, 10);
for (LocalDate d = start; !d.isAfter(end); d = d.plusDays(1)) {
// ...
}9. 時(shí)間操作常見(jiàn)應(yīng)用場(chǎng)景實(shí)戰(zhàn)
9.1 時(shí)間區(qū)間的交集與重疊判斷
在預(yù)訂系統(tǒng)、排班系統(tǒng)等場(chǎng)景,經(jīng)常需要判斷兩個(gè)時(shí)間段是否有重疊。
public static boolean isOverlap(LocalDateTime start1, LocalDateTime end1,
LocalDateTime start2, LocalDateTime end2) {
return !start1.isAfter(end2) && !start2.isAfter(end1);
}
// 示例
LocalDateTime s1 = LocalDateTime.of(2024, 6, 1, 10, 0);
LocalDateTime e1 = LocalDateTime.of(2024, 6, 1, 12, 0);
LocalDateTime s2 = LocalDateTime.of(2024, 6, 1, 11, 0);
LocalDateTime e2 = LocalDateTime.of(2024, 6, 1, 13, 0);
boolean overlap = isOverlap(s1, e1, s2, e2); // true9.2 日期的周期性操作(如每周一的定時(shí)任務(wù))
// 獲取本周的所有日期
LocalDate monday = LocalDate.now().with(DayOfWeek.MONDAY);
for (int i = 0; i < 7; i++) {
LocalDate d = monday.plusDays(i);
System.out.println(d + " " + d.getDayOfWeek());
}9.3 計(jì)算某月的第一天和最后一天
LocalDate now = LocalDate.now(); LocalDate firstDay = now.withDayOfMonth(1); LocalDate lastDay = now.withDayOfMonth(now.lengthOfMonth());
9.4 獲取某天的開(kāi)始和結(jié)束時(shí)間
LocalDate date = LocalDate.of(2024, 6, 1); LocalDateTime startOfDay = date.atStartOfDay(); LocalDateTime endOfDay = date.atTime(LocalTime.MAX); // 23:59:59.999999999
9.5 時(shí)間格式化為友好字符串(如“剛剛”、“1分鐘前”、“1天前”)
public static String friendlyTime(LocalDateTime time) {
Duration duration = Duration.between(time, LocalDateTime.now());
long seconds = duration.getSeconds();
if (seconds < 60) return "剛剛";
if (seconds < 3600) return (seconds / 60) + "分鐘前";
if (seconds < 86400) return (seconds / 3600) + "小時(shí)前";
return (seconds / 86400) + "天前";
}9.6 日期時(shí)間的序列化與反序列化(如 JSON)
- 推薦用 ISO 標(biāo)準(zhǔn)格式,如
"2024-06-01T12:00:00"。 - Jackson、Gson 等主流 JSON 庫(kù)在 Java 8 后已支持直接序列化/反序列化
LocalDateTime、LocalDate等類(lèi)型。 - 若需自定義格式,可用注解:
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime createTime;
10. 時(shí)間操作的性能與線程安全
新 API(java.time)都是不可變對(duì)象,線程安全。
舊 API(Date、Calendar、SimpleDateFormat)都非線程安全。
示例:多線程格式化日期時(shí),推薦如下寫(xiě)法:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
Runnable task = () -> {
String formatted = LocalDateTime.now().format(formatter);
System.out.println(formatted);
};
new Thread(task).start();11. 時(shí)間的國(guó)際化與本地化
11.1 不同區(qū)域的日期格式
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日", Locale.CHINA);
String str = LocalDate.now().format(formatter); // 2024年06月01日11.2 不同國(guó)家的周起始日
LocalDate date = LocalDate.now(); DayOfWeek firstDayOfWeek = WeekFields.of(Locale.US).getFirstDayOfWeek(); // SUNDAY DayOfWeek firstDayOfWeekCN = WeekFields.of(Locale.CHINA).getFirstDayOfWeek(); // MONDAY
12. 時(shí)間工具類(lèi)封裝示例
你可以將常用操作封裝為工具類(lèi),便于項(xiàng)目復(fù)用:
public class DateTimeUtils {
public static String format(LocalDateTime time, String pattern) {
return time.format(DateTimeFormatter.ofPattern(pattern));
}
public static LocalDateTime parse(String str, String pattern) {
return LocalDateTime.parse(str, DateTimeFormatter.ofPattern(pattern));
}
public static long diffMinutes(LocalDateTime start, LocalDateTime end) {
return Duration.between(start, end).toMinutes();
}
// 更多方法...
}13. 時(shí)間操作常見(jiàn)面試題
- 如何獲取某個(gè)月的最后一天?
- 如何判斷兩個(gè)時(shí)間段是否有重疊?
- 如何將 Date 轉(zhuǎn)換為 LocalDateTime?
- 如何計(jì)算兩個(gè)日期之間的天數(shù)?
- 如何格式化為“剛剛”、“幾分鐘前”?
14. 未來(lái)趨勢(shì)和常用第三方庫(kù)
- 推薦只用 java.time 包,避免 Date、Calendar。
- 主流 ORM 框架(如 JPA、MyBatis)已支持 Java 8 時(shí)間類(lèi)。
- 如果需要更高級(jí)的日歷功能,可用 Joda-Time(但現(xiàn)在已被 java.time 替代)。
- 時(shí)間處理相關(guān)的第三方庫(kù)如 Hutool、Apache Commons Lang 也有豐富的工具類(lèi)。
15. 其他常見(jiàn)問(wèn)題與補(bǔ)充
- 時(shí)間戳存儲(chǔ)建議用 UTC,顯示時(shí)再轉(zhuǎn)換為本地時(shí)區(qū)。
- 跨時(shí)區(qū)應(yīng)用注意夏令時(shí)(DST)變化。
- 數(shù)據(jù)庫(kù)時(shí)間字段建議用標(biāo)準(zhǔn)時(shí)間(如 UTC),避免因時(shí)區(qū)導(dǎo)致數(shù)據(jù)混亂。
到此這篇關(guān)于Java 中的所有時(shí)間操作類(lèi)詳解及使用實(shí)戰(zhàn)的文章就介紹到這了,更多相關(guān)java時(shí)間操作類(lèi)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java controller接口出入?yún)r(shí)間序列化轉(zhuǎn)換操作方法(兩種)
- Java中的時(shí)間戳各種操作方法詳解
- Java?LocalTime的常用時(shí)間操作總結(jié)
- Java Date時(shí)間類(lèi)型的操作實(shí)現(xiàn)
- Java后端長(zhǎng)時(shí)間無(wú)操作自動(dòng)退出的實(shí)現(xiàn)方式
- Java8 日期、時(shí)間操作代碼
- 在java中獲取List集合中最大的日期時(shí)間操作
- Java8中的LocalDateTime和Date一些時(shí)間操作方法
- java日期時(shí)間操作工具類(lèi)
相關(guān)文章
如何手寫(xiě)一個(gè)Spring Boot Starter
這篇文章主要介紹了如何手寫(xiě)一個(gè)Spring Boot Starter,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下2021-03-03
Java使用try-with-resources實(shí)現(xiàn)自動(dòng)解鎖
項(xiàng)目中使用Redission分布式鎖,每次使用都需要顯示的解鎖,很麻煩,Java 提供了 try-with-resources 語(yǔ)法糖,它不僅可以用于自動(dòng)關(guān)閉流資源,還可以用于實(shí)現(xiàn)自動(dòng)解鎖,本文將介紹如何利用 try-with-resources 實(shí)現(xiàn)鎖的自動(dòng)釋放,需要的朋友可以參考下2025-01-01
springboot+idea熱啟動(dòng)設(shè)置方法(自動(dòng)加載)
這篇文章主要介紹了springboot+idea熱啟動(dòng)設(shè)置方法(自動(dòng)加載),本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-01-01
spring中使用mybatis實(shí)現(xiàn)批量插入的示例代碼
這篇文章主要介紹了spring中使用mybatis實(shí)現(xiàn)批量插入的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
java實(shí)現(xiàn)內(nèi)存調(diào)試與診斷的示例代碼
隨著現(xiàn)代應(yīng)用程序功能的日益復(fù)雜化以及大數(shù)據(jù)、微服務(wù)、云原生等架構(gòu)模式的廣泛應(yīng)用,Java應(yīng)用的運(yùn)行時(shí)內(nèi)存壓力不斷增大,所以本文就來(lái)和大家講講如何對(duì) Java 應(yīng)用進(jìn)行內(nèi)存調(diào)試與診斷吧2025-05-05
java實(shí)現(xiàn)下載文件到默認(rèn)瀏覽器路徑
這篇文章主要介紹了java實(shí)現(xiàn)下載文件到默認(rèn)瀏覽器路徑,具有很好的參考價(jià)值,希望對(duì)的大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05

