天天看點

實戰:求年月日時間前後遇到的坑和解決方式

作者:java易

前言

這周接到一個時間轉換任務需要處理,本來沒什麼問題,後來完成後發現時間有偏差,又重寫了一遍代碼,感覺很有記錄必要性,希望看過的小夥伴可以避坑。照例,先說結論。

正确執行個體:

ini複制代碼DateTimeFormatter formatter = DateTimeFormatter.ofPattern(CommonConstants.DATE_FORMAT);
LocalDateTime now = LocalDateTime.now();
           

錯誤執行個體:

ini複制代碼Calendar calendar = Calendar.getInstance(); 
           

結果:如果我們使用了錯誤的工具類Calendar,求出的時間在時分秒中會有偏差,打到幾小時,小到十幾秒。這個時間的偏差是我們無法忍受的。

需求

這個工作是要求一個接口傳輸給我們一部分參數,然後根據參數求出對應的資料,比如昨天今天,三小時、天、月、年後等等。下面是代碼執行個體,照例先放正确的,錯誤的留給大家參考。

scss複制代碼//求出年月日小時對應要求的時間
public Intent dateForParams2(Intent intent, String time) {
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern(CommonConstants.DATE_FORMAT);
    LocalDateTime now = LocalDateTime.now();
    String endTime = now.format(formatter);

    // 将時間向前推移1小時
    if (CommonConstants.HeHaiTimeKey.HOUR_1.equals(time)) {
        now = now.minusHours(1);

    }
    if (CommonConstants.HeHaiTimeKey.HOUR_3.equals(time)) {
        now = now.minusHours(3);

    }
    if (CommonConstants.HeHaiTimeKey.HOUR_24.equals(time)) {
        now = now.minusDays(1);
    }
    if (CommonConstants.HeHaiTimeKey.HOUR_48.equals(time)) {
        now = now.minusDays(2);
    }
    if (CommonConstants.HeHaiTimeKey.HOUR_72.equals(time)) {
        now = now.minusDays(3);
    }

    // 将日期向前推移3天
    if (CommonConstants.HeHaiTimeKey.DAY_3.equals(time)) {
        now = LocalDateTime.of(
                now.minusDays(3).toLocalDate(),
                LocalTime.of(8, 0, 0)            // 設定為8點整
        );
    }
    if (CommonConstants.HeHaiTimeKey.DAY_7.equals(time)) {
        now = LocalDateTime.of(
                now.minusDays(7).toLocalDate(),
                LocalTime.of(8, 0, 0)
        );
    }
    if (CommonConstants.HeHaiTimeKey.DAY_15.equals(time)) {
        now = LocalDateTime.of(
                now.minusDays(15).toLocalDate(),
                LocalTime.of(8, 0, 0)
        );
    }
    if (CommonConstants.HeHaiTimeKey.DAY_30.equals(time)) {
        now = LocalDateTime.of(
                now.minusDays(30).toLocalDate(),
                LocalTime.of(8, 0, 0)
        );
    }
    // 将月份向前推移3個月
    if (CommonConstants.HeHaiTimeKey.THREE_MONTH.equals(time)) {
        now = LocalDateTime.of(
                now.minusMonths(3).toLocalDate(),
                LocalTime.of(8, 0, 0)
        );
    }
    if (CommonConstants.HeHaiTimeKey.HALF_YEAR.equals(time)) {
        // 将月份向前推移6個月
        now = LocalDateTime.of(
                now.minusMonths(6).toLocalDate(),
                LocalTime.of(8, 0, 0)
        );
    }

    // 将年份向前推移1年
    if (CommonConstants.HeHaiTimeKey.ONE_YEAR.equals(time)) {
        now = LocalDateTime.of(
                now.minusYears(1).toLocalDate(),
                LocalTime.of(8, 0, 0)
        );
    }
    if (CommonConstants.HeHaiTimeKey.THREE_YEAR.equals(time)) {
        now = LocalDateTime.of(
                now.minusYears(3).toLocalDate(),
                LocalTime.of(8, 0, 0)
        );
    }
    if (CommonConstants.HeHaiTimeKey.FIVE_YEAR.equals(time)) {
        now = LocalDateTime.of(
                now.minusYears(5).toLocalDate(),
                LocalTime.of(8, 0, 0)
        );
    }
    if (CommonConstants.HeHaiTimeKey.TEN_YEAR.equals(time)) {
        now = LocalDateTime.of(
                now.minusYears(10).toLocalDate(),
                LocalTime.of(8, 0, 0)
        );
    }


    //設定早上8點
    if (CommonConstants.HeHaiTimeKey.TODAY.equals(time)) {
        now = LocalDateTime.of(
                now.toLocalDate(),          // 使用目前日期
                LocalTime.of(8, 0, 0)      // 設定為8點整
        );
    }
    if (CommonConstants.HeHaiTimeKey.YESTERDAY.equals(time)) {
        //減去1天
        now = LocalDateTime.of(
                now.minusDays(1).toLocalDate(),
                LocalTime.of(8, 0, 0)
        );
        // 結束日期為今日8點整
        endTime = eightForNow2(0);


    }
    if (CommonConstants.HeHaiTimeKey.LAST_DAY.equals(time)) {
        //減去2天
        now = LocalDateTime.of(
                now.minusDays(2).toLocalDate(),
                LocalTime.of(8, 0, 0)
        );
        // 結束日期為昨日8點整
        endTime = eightForNow2(1);

    }


    String startTime = now.format(formatter);
    log.info("輸出開始時間:{}", startTime);
    intent.setStartTime(startTime);
    intent.setEndTime(endTime);
    return intent;
}
           
ini複制代碼/**
 * 求出早8點
 */
public String eightForNow2(Integer day) {
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern(CommonConstants.DATE_FORMAT);
    LocalDateTime now = LocalDateTime.now();
    if (day != null) {
        if (day == 0) {
            now = LocalDateTime.of(
                    now.toLocalDate(),          // 使用目前日期
                    LocalTime.of(8, 0, 0)      // 設定為8點整
            );
        } else {
            now = LocalDateTime.of(
                    now.minusDays(day).toLocalDate(),
                    LocalTime.of(8, 0, 0)
            );
        }
    }

    return now.format(formatter);
}
           

這個沒有內建一個工具類是因為需求開發時間小,而且要求時間短,再加上是使用頻率低。有常用的可以內建到你們的時間工具類中。

-------------------------------錯誤-------------------------------

scss複制代碼/**
 * 根據Params求出不同的時間
 */
public Intent dateForParams(Intent intent, String time) {
    SimpleDateFormat format = new SimpleDateFormat(CommonConstants.DATE_FORMAT);

    Calendar calendar = Calendar.getInstance(); //擷取月曆執行個體
    String endTime = format.format(calendar.getTime());//後去結束時間
    calendar.setTime(date); //将目前時間設定給月曆執行個體
    int hour = calendar.get(Calendar.HOUR_OF_DAY); // 擷取目前的小時數

    // 将時間向前推移1小時
    if (CommonConstants.HeHaiTimeKey.HOUR_1.equals(time)) {
        if (hour < 1) {
            calendar.add(Calendar.DAY_OF_MONTH, -1); // 将日期向前推移1天
            hour += 24; // 計算出對應的小時數
            calendar.add(Calendar.HOUR_OF_DAY, hour - 1);
        } else {
            calendar.add(Calendar.HOUR_OF_DAY, -1);
        }

    }
    if (CommonConstants.HeHaiTimeKey.HOUR_3.equals(time)) {
        if (hour < 1) {
            calendar.add(Calendar.DAY_OF_MONTH, -1); // 将日期向前推移1天
            hour += 24; // 計算出對應的小時數
            calendar.add(Calendar.HOUR_OF_DAY, hour - 3);
        } else {
            calendar.add(Calendar.HOUR_OF_DAY, -3);
        }

    }
    if (CommonConstants.HeHaiTimeKey.HOUR_24.equals(time)) {
        calendar.add(Calendar.DAY_OF_MONTH, -1);
    }
    if (CommonConstants.HeHaiTimeKey.HOUR_48.equals(time)) {
        calendar.add(Calendar.DAY_OF_MONTH, -2);
    }
    if (CommonConstants.HeHaiTimeKey.HOUR_72.equals(time)) {
        calendar.add(Calendar.DAY_OF_MONTH, -3);
    }

    // 将日期向前推移3天
    if (CommonConstants.HeHaiTimeKey.DAY_3.equals(time)) {
        calendar.add(Calendar.DAY_OF_MONTH, -3);
        calendar.set(Calendar.HOUR_OF_DAY, 8);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
    }
    if (CommonConstants.HeHaiTimeKey.DAY_7.equals(time)) {
        calendar.add(Calendar.DAY_OF_MONTH, -7);
        calendar.set(Calendar.HOUR_OF_DAY, 8);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
    }
    if (CommonConstants.HeHaiTimeKey.DAY_15.equals(time)) {
        calendar.add(Calendar.DAY_OF_MONTH, -15);
        calendar.set(Calendar.HOUR_OF_DAY, 8);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
    }
    if (CommonConstants.HeHaiTimeKey.DAY_30.equals(time)) {
        calendar.add(Calendar.DAY_OF_MONTH, -30);
        calendar.set(Calendar.HOUR_OF_DAY, 8);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
    }
    // 将月份向前推移3個月
    if (CommonConstants.HeHaiTimeKey.THREE_MONTH.equals(time)) {
        calendar.add(Calendar.MONTH, -3);
    }
    if (CommonConstants.HeHaiTimeKey.HALF_YEAR.equals(time)) {
        // 将月份向前推移6個月
        calendar.add(Calendar.MONTH, -6);
    }

    // 将年份向前推移1年
    if (CommonConstants.HeHaiTimeKey.ONE_YEAR.equals(time)) {
        calendar.add(Calendar.YEAR, -1);
    }
    if (CommonConstants.HeHaiTimeKey.THREE_YEAR.equals(time)) {
        calendar.add(Calendar.YEAR, -3);
    }
    if (CommonConstants.HeHaiTimeKey.FIVE_YEAR.equals(time)) {
        calendar.add(Calendar.YEAR, -5);
    }
    if (CommonConstants.HeHaiTimeKey.TEN_YEAR.equals(time)) {
        calendar.add(Calendar.YEAR, -10);
    }


    //設定早上8點
    if (CommonConstants.HeHaiTimeKey.TODAY.equals(time)) {
        calendar.set(Calendar.HOUR_OF_DAY, 8);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
    }
    if (CommonConstants.HeHaiTimeKey.YESTERDAY.equals(time)) {
        //減去1天
        calendar.add(Calendar.DAY_OF_MONTH, -1);
        calendar.set(Calendar.HOUR_OF_DAY, 8);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        // 結束日期為今日8點整
        endTime = eightForNow(null);
        intent.setEndTime(endTime);

    }
    if (CommonConstants.HeHaiTimeKey.LAST_DAY.equals(time)) {
        //減去2天
        calendar.add(Calendar.DAY_OF_MONTH, -2);
        calendar.set(Calendar.HOUR_OF_DAY, 8);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        // 結束日期為昨日8點整
        endTime = eightForNow(1);
        intent.setEndTime(endTime);
    }

    String startTime;
    Date dayTime = calendar.getTime();
    if (CommonConstants.HeHaiTimeKey.REAL_TIME.equals(time)) {
        startTime = endTime;
    } else {
        startTime = format.format(dayTime); //将時間格式化為字元串
    }
    log.info("輸出開始時間:{}", startTime);
    intent.setStartTime(startTime);
    intent.setEndTime(endTime);
    return intent;
}


/**
 * 求出早8點
 */
public String eightForNow(Integer day) {

    Calendar calendar = Calendar.getInstance(); //擷取月曆執行個體
    calendar.setTime(date); //将目前時間設定給月曆執行個體

    if (day != null) {
        calendar.add(Calendar.DAY_OF_MONTH, -day); //減去對應天數
    }

    calendar.set(Calendar.HOUR_OF_DAY, 8); //設定早上8點
    calendar.set(Calendar.MINUTE, 0);
    calendar.set(Calendar.SECOND, 0);
    Date yesterday8am = calendar.getTime(); //擷取昨天早上8點的時間

    SimpleDateFormat format = new SimpleDateFormat(CommonConstants.DATE_FORMAT);
    String formattedDateTime = format.format(yesterday8am);
    log.info("輸出開始時間:{}", formattedDateTime);
    return formattedDateTime;
}
           

Calendar是Java中處理日期和時間的類,它提供了各種實用方法來操作月曆、時區和時間等資訊。以前,Java的日期時間類主要是使用Date和SimpleDateFormat,但是這兩個類存在一些問題,如線程不安全,可變性等,是以Java 1.1中引入了Calendar類來替代它們。

Calendar類是一個抽象類,不能直接建立對象,可以通過getInstance()方法擷取它的執行個體。通過set()方法設定日期和時間資訊,getTime()方法擷取Date類型的時間,get()方法擷取年月日等資訊。

Calendar類提供了對時間進行加減的方法,add()方法用于加減某個時間機關,例如,可以使用add(Calendar.DATE, 1)增加一天,也可以使用add(Calendar.MONTH, -1)減少一個月。roll()方法則是這樣做的一種變體,它隻對指定的字段進行修改,而不會對較高的字段進行更改。

Calendar類也提供了很多有用的方法來操縱和顯示日期和時間,如getFirstDayOfWeek()擷取目前的星期起始日,getMinimum()擷取給定月曆字段的最小值等等。

盡管Calendar類有很多優點,它仍然沒有解決Date類的基本問題。在Java 8中,LocalDateTime類被引入作為替代Calendar和Date的更簡單、更靈活的實作。本地日期時間的類提供了Java程式員可以更自然地處理日期和時間的方法,避免了時間區的混淆和線程安全問題。

LocalDateTime是Java 8新增加的一個日期時間類,代表的是一個不帶時區的日期和時間,具有不可變性,用于處理本地日期和時間。它的執行個體可以存儲到精确到納秒的時間。

LocalDateTime的初始化可以通過now()調用擷取目前的日期和時間,也可以通過of()方法建立指定的日期和時間。其中,now()方法的具體實作是通過系統時鐘擷取目前的時間,of()方法則接受七個參數,分别是年、月、日、時、分、秒、納秒順序。

LocalDateTime還提供了很多方法來操縱日期和時間,比如加減日期時間、計算日期之間的間隔、格式化日期時間等。這樣可以很友善地進行日常的開發工作。對于需要考慮時區的應用來說,Java 8還提供了ZonedDateTime類。

LocalDateTime是一種非常實用的日期和時間處理類,它可以滿足大多數應用對日期和時間的需求。

上面都是一些官網給出的答案,由此我們也可以看出,使用LocalDateTime是更好的一種選擇,還在用calender的可以嘗試替換了,不要停留在舊時代裡。

代碼就不具體講解了也不是很難,注釋也挺多,有什麼不懂得可以評論下方提出,看到後會抓緊回複的。

ps:有的小夥伴可能的确是剛接觸,這裡提供一個calender的示例,自己試着修改成LocalDateTime,前者你可以不會,後者不行哦。

ini複制代碼public static void main(String[] args) throws ParseException {
    String dateTimeString = "2023-06-13 02:00:00";
    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Date date1 = format.parse(dateTimeString); // 解析為 Date 對象

    Calendar calendar = Calendar.getInstance(); //擷取月曆執行個體
    calendar.setTime(date1); //将目前時間設定給月曆執行個體

	//這裡零時轉換可以忽略,LocalDateTime沒有這方面的煩惱
    int hour = calendar.get(Calendar.HOUR_OF_DAY); // 擷取目前的小時數
    if (hour < 1) { // 如果小時數小于1,則需要特殊處理
        calendar.add(Calendar.DAY_OF_MONTH, -1); // 将日期向前推移1天
        hour += 24; // 計算出對應的小時數
    }
    //calendar.set(Calendar.HOUR_OF_DAY, hour - 72); // 将時間向前推移1小時
    calendar.add(Calendar.YEAR, -10); // 将年份向前推移10年


    Date yesterday8am = calendar.getTime();
    String formattedDateTime = format.format(yesterday8am); //将時間格式化為字元串
    System.out.println("輸出結果:"+formattedDateTime); //輸出結果

}
           

今天就到這裡吧,感覺有用的小夥伴可以點個贊,你的支援就是我更新的最大動力!

繼續閱讀