Java 中的所有時(shí)間操作類詳解及使用實(shí)戰(zhàn)
一、早期時(shí)間類
1.java.util.Date
- 簡介:最早的時(shí)間類,表示某一時(shí)刻。
- 常用方法:
getTime():返回自1970年1月1日00:00:00 GMT以來的毫秒數(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
- 簡介:用于更復(fù)雜的日期計(jì)算(如加減天數(shù)),是抽象類,常用子類
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
- 簡介:用于日期格式化和解析。
- 常用方法:
format(Date date):格式化日期為字符串。parse(String source):解析字符串為日期對象。
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(推薦)
所有類都在 java.time 包下,線程安全,易于使用。
1.LocalDate
- 簡介:只表示日期(不含時(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
- 簡介:只表示時(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
- 簡介:表示日期和時(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
- 簡介:日期時(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
- 簡介:時(shí)間戳,精確到納秒,表示自1970年1月1日00:00:00 UTC的瞬間。
- 常用方法:
now():當(dāng)前時(shí)間戳。ofEpochMilli(long epochMilli):通過毫秒數(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
- 簡介:格式化和解析日期時(shí)間,線程安全。
- 常用方法:
format(TemporalAccessor temporal):格式化為字符串。parse(CharSequence text):解析字符串為日期時(shí)間對象。
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)類
1.java.sql.Date、java.sql.Time、java.sql.Timestamp
- 用于數(shù)據(jù)庫操作,繼承自
java.util.Date,通常用于 JDBC。
四、時(shí)間類關(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)目優(yōu)先使用
java.time包下的類,線程安全、功能豐富、易用。 - 舊項(xiàng)目如果使用了
Date、Calendar,建議逐步遷移到新的時(shí)間API。 - 格式化和解析請用
DateTimeFormatter,避免SimpleDateFormat的線程安全問題。
七. 各時(shí)間類詳細(xì)用法與實(shí)戰(zhàn)場景
1.1 LocalDate
場景:只關(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
場景:只關(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
場景:關(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
場景:全球化應(yīng)用,關(guān)心時(shí)區(qū)(如國際會(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ǔ)時(shí)間戳(如數(shù)據(jù)庫、消息隊(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
場景:計(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. 常見坑與注意事項(xiàng)
- 月份從1開始(不是0)
LocalDateTime沒有時(shí)區(qū)信息,存儲(chǔ)時(shí)要注意轉(zhuǎn)換SimpleDateFormat線程不安全,推薦用DateTimeFormatterDuration不能用于日期(用Period)Date和Calendar已過時(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í)間操作常見應(yīng)用場景實(shí)戰(zhàn)
9.1 時(shí)間區(qū)間的交集與重疊判斷
在預(yù)訂系統(tǒng)、排班系統(tǒ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 獲取某天的開始和結(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 庫在 Java 8 后已支持直接序列化/反序列化
LocalDateTime、LocalDate等類型。 - 若需自定義格式,可用注解:
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime createTime;
10. 時(shí)間操作的性能與線程安全
新 API(java.time)都是不可變對象,線程安全。
舊 API(Date、Calendar、SimpleDateFormat)都非線程安全。
示例:多線程格式化日期時(shí),推薦如下寫法:
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í)間的國際化與本地化
11.1 不同區(qū)域的日期格式
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日", Locale.CHINA);
String str = LocalDate.now().format(formatter); // 2024年06月01日11.2 不同國家的周起始日
LocalDate date = LocalDate.now(); DayOfWeek firstDayOfWeek = WeekFields.of(Locale.US).getFirstDayOfWeek(); // SUNDAY DayOfWeek firstDayOfWeekCN = WeekFields.of(Locale.CHINA).getFirstDayOfWeek(); // MONDAY
12. 時(shí)間工具類封裝示例
你可以將常用操作封裝為工具類,便于項(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í)間操作常見面試題
- 如何獲取某個(gè)月的最后一天?
- 如何判斷兩個(gè)時(shí)間段是否有重疊?
- 如何將 Date 轉(zhuǎn)換為 LocalDateTime?
- 如何計(jì)算兩個(gè)日期之間的天數(shù)?
- 如何格式化為“剛剛”、“幾分鐘前”?
14. 未來趨勢和常用第三方庫
- 推薦只用 java.time 包,避免 Date、Calendar。
- 主流 ORM 框架(如 JPA、MyBatis)已支持 Java 8 時(shí)間類。
- 如果需要更高級(jí)的日歷功能,可用 Joda-Time(但現(xiàn)在已被 java.time 替代)。
- 時(shí)間處理相關(guān)的第三方庫如 Hutool、Apache Commons Lang 也有豐富的工具類。
15. 其他常見問題與補(bǔ)充
- 時(shí)間戳存儲(chǔ)建議用 UTC,顯示時(shí)再轉(zhuǎn)換為本地時(shí)區(qū)。
- 跨時(shí)區(qū)應(yīng)用注意夏令時(shí)(DST)變化。
- 數(shù)據(jù)庫時(shí)間字段建議用標(biāo)準(zhǔn)時(shí)間(如 UTC),避免因時(shí)區(qū)導(dǎo)致數(shù)據(jù)混亂。
到此這篇關(guān)于Java 中的所有時(shí)間操作類詳解及使用實(shí)戰(zhàn)的文章就介紹到這了,更多相關(guān)java時(shí)間操作類內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot2底層注解@ConfigurationProperties配置綁定
這篇文章主要介紹了SpringBoot2底層注解@ConfigurationProperties配置綁定,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05
Intellj Idea中的maven工程Java文件顏色不對,未被識(shí)別的解決
這篇文章主要介紹了Intellj Idea中的maven工程Java文件顏色不對,未被識(shí)別的解決,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08
SpringBoot用@Async注解實(shí)現(xiàn)異步任務(wù)
這篇文章主要介紹了SpringBoot用@Async注解實(shí)現(xiàn)異步任務(wù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
SpringBoot整合Flink CDC實(shí)現(xiàn)實(shí)時(shí)追蹤mysql數(shù)據(jù)變動(dòng)
我們將整合Spring Boot和Apache Flink CDC(Change Data Capture)來實(shí)現(xiàn)實(shí)時(shí)數(shù)據(jù)追蹤,下面是一個(gè)基本的實(shí)踐流程代碼,包括搭建Spring Boot項(xiàng)目、整合Flink CDC以及實(shí)現(xiàn)數(shù)據(jù)變動(dòng)的實(shí)時(shí)追蹤,需要的朋友可以參考下2024-07-07
如何基于ThreadPoolExecutor創(chuàng)建線程池并操作
這篇文章主要介紹了如何基于ThreadPoolExecutor創(chuàng)建線程池并操作,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11

