當前位置: 妍妍網 > 碼農

空降 CTO:禁止在計畫中使用 Date 類!!!

2024-05-15碼農

來源: www.cnblogs.com/wlovet/p/18058514

為什麽現在連Date類都不建議使用了?

一、有什麽問題嗎 java.util.Date

java.util.Date Date 從現在開始)是一個糟糕的型別,這解釋了為什麽它的大部份內容在 Java 1.1 中被棄用(但不幸的是仍在使用)。

設計缺陷包括:

  • 它的名稱具有誤導性:它並不代表一個日期,而是代表時間的一個瞬間。所以它應該被稱為 Instant ——正如它的 java.time 等價物一樣。

  • 它是非最終的:這鼓勵了對繼承的不良使用,例如 java.sql.Date (這 意味著 代表一個日期,並且由於具有相同的短名稱而也令人困惑)

  • 它是可變的:日期/時間型別是自然值,可以透過不可變型別有效地建模。可變的事實 Date (例如透過 setTime 方法)意味著勤奮的開發人員最終會在各處建立防禦性副本。

  • 它在許多地方(包括)隱式使用系統本地時區, toString() 這讓許多開發人員感到困惑。有關此內容的更多資訊,請參閱「什麽是即時」部份

  • 它的月份編號是從 0 開始的,是從 C 語言復制的。這導致了很多很多相差一的錯誤。

  • 它的年份編號是基於 1900 年的,也是從 C 語言復制的。當然,當 Java 出現時,我們已經意識到這不利於可讀性?

  • 它的方法命名不明確: getDate() 返回月份中的某一天,並 getDay() 返回星期幾。給這些更具描述性的名字有多難?

  • 對於是否支持閏秒含糊其辭:「秒由 0 到 61 之間的整數表示;值 60 和 61 僅在閏秒時出現,即使如此,也僅在實際正確跟蹤閏秒的 Java 實作中出現。」 我強烈懷疑大多數開發人員(包括我自己)都做了很多假設,認為 for 的範圍 getSeconds() 實際上在 0-59 範圍內(含)。

  • 它的寬容沒有明顯的理由:「在所有情況下,為這些目的而對方法給出的論據不必落在指定的範圍內; 例如,日期可以指定為 1 月 32 日,並被解釋為 2 月 1 日。」 多久有用一次?

  • 關鍵原因如下:

    原文如下:https://codeblog.jonskeet.uk/2017/04/23/all-about-java-util-date/

    二、為啥要改?

    我們要改的原因很簡單,我們的程式碼缺陷掃描規則認為這是一個必須修改的缺陷,否則不給釋出,不改不行,服了。

    圖片

    解決思路:避免使用 java.util.Date java.sql.Date 類和其提供的API,考慮使用 java.time.Instant 類或 java.time.LocalDateTime 類及其提供的API替代。

    三、怎麽改?

    只能說這種基礎的類改起來牽一發動全身,需要從DO實體類看起,然後就是各種Converter,最後是DTO。由於我們還是微服務架構,業務服務依賴於基礎服務的API,所以必須要一起改否則就會報錯。

    這裏就不細說修改流程了,主要說一下我們在改造的時候遇到的一些問題。

    1. 耐心比對資料庫日期欄位和DO的對映

    (1)確定欄位型別

    首先你需要確定數據物件中的 Date 欄位代表的是日期、時間還是時間戳。

  • 如果欄位代表日期和時間,則可能需要使用 LocalDateTime

  • 如果欄位僅代表日期,則可能需要使用 LocalDate

  • 如果欄位僅代表時間,則可能需要使用 LocalTime

  • 如果欄位需要保存時間戳(帶時區的),則可能需要使用 Instant ZonedDateTime

  • (2)更新數據物件類

    更新數據物件類中的欄位,把 Date 型別改為適當的 java.time 型別。

    2. 將DateUtil中的方法改造

    (1)替換原來的new Date()和Calendar.getInstance().getTime()

    原來的方式:

    Date nowDate = new Date();
    Date nowCalendarDate = Calendar.getInstance().getTime();

    使用 java.time 改造後:

    // 使用Instant代表一個時間點,這與Date類似
    Instant nowInstant = Instant.now();
    // 如果需要用到具體的日期和時間(例如年、月、日、時、分、秒)
    LocalDateTime nowLocalDateTime = LocalDateTime.now();
    // 如果你需要和特定的時區互動,可以使用ZonedDateTime
    ZonedDateTime nowZonedDateTime = ZonedDateTime.now();
    // 如果你需要轉換回java.util.Date,你可以這樣做(假設你的程式碼其他部份還需要使用Date)
    Date nowFromDateInstant = Date.from(nowInstant);
    // 如果需要與java.sql.Timestamp互動
    java.sql.Timestamp nowFromInstant = java.sql.Timestamp.from(nowInstant);


    一些註意點:

    1. Instant 表示的是一個時間點,它是時區無關的,相當於舊的 Date 類。它通常用於表示時間戳。

    2. LocalDateTime 表示沒有時區資訊的日期和時間,它不能直接轉換為時間戳,除非你將其與時區結合使用(例如透過 ZonedDateTime )。

    3. ZonedDateTime 包含時區資訊的日期和時間,它更類似於 Calendar ,因為 Calendar 也包含時區資訊。

    4. 當你需要將 java.time 物件轉換回 java.util.Date 物件時,可以使用 Date.from(Instant) 方法。這在你的程式碼需要與舊的API或庫互動時非常有用。

    (2)一些基礎的方法改造

    a. dateFormat

    原來的方式

    public static String dateFormat(Date date, String dateFormat) {
    SimpleDateFormat formatter = new SimpleDateFormat(dateFormat);
    return formatter.format(date);
    }

    使用 java.time 改造後

    public static String dateFormat(LocalDateTime date, String dateFormat) {
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat);
    return date.format(formatter);
    }

    b. addSecond、addMinute、addHour、addDay、addMonth、addYear

    原來的方式

    public static Date addSecond(Date date, int second) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);
    calendar.add(13, second);
    return calendar.getTime();
    }
    public static Date addMinute(Date date, int minute) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);
    calendar.add(12, minute);
    return calendar.getTime();
    }
    public static Date addHour(Date date, int hour) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);
    calendar.add(10, hour);
    return calendar.getTime();
    }
    public static Date addDay(Date date, int day) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);
    calendar.add(5, day);
    return calendar.getTime();
    }
    public static Date addMonth(Date date, int month) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);
    calendar.add(2, month);
    return calendar.getTime();
    }
    public static Date addYear(Date date, int year) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);
    calendar.add(1, year);
    return calendar.getTime();
    }



    使用 java.time 改造後

    public static LocalDateTime addSecond(LocalDateTime date, int second) {
    return date.plusSeconds(second);
    }
    public static LocalDateTime addMinute(LocalDateTime date, int minute) {
    return date.plusMinutes(minute);
    }
    public static LocalDateTime addHour(LocalDateTime date, int hour) {
    return date.plusHours(hour);
    }
    public static LocalDateTime addDay(LocalDateTime date, int day) {
    return date.plusDays(day);
    }
    public static LocalDateTime addMonth(LocalDateTime date, int month) {
    return date.plusMonths(month);
    }
    public static LocalDateTime addYear(LocalDateTime date, int year) {
    return date.plusYears(year);
    }



    c. dateToWeek

    原來的方式

    public static final String[] WEEK_DAY_OF_CHINESE = new String[]{"周日""周一""周二""周三""周四""周五""周六"};
    public static String dateToWeek(Date date) {
    Calendar cal = Calendar.getInstance();
    cal.setTime(date);
    return WEEK_DAY_OF_CHINESE[cal.get(7) - 1];
    }

    使用 java.time 改造後

    public static final String[] WEEK_DAY_OF_CHINESE = new String[]{"周日""周一""周二""周三""周四""周五""周六"};
    public static String dateToWeek(LocalDate date) {
    DayOfWeek dayOfWeek = date.getDayOfWeek();
    return WEEK_DAY_OF_CHINESE[dayOfWeek.getValue() % 7];
    }

    d. getStartOfDay和getEndOfDay

    原來的方式

    public static Date getStartTimeOfDay(Date date) {
    if (date == null) {
    return null;
    else {
    LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()), ZoneId.systemDefault());
    LocalDateTime startOfDay = localDateTime.with(LocalTime.MIN);
    return Date.from(startOfDay.atZone(ZoneId.systemDefault()).toInstant());
    }
    }
    public static Date getEndTimeOfDay(Date date) {
    if (date == null) {
    return null;
    else {
    LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()), ZoneId.systemDefault());
    LocalDateTime endOfDay = localDateTime.with(LocalTime.MAX);
    return Date.from(endOfDay.atZone(ZoneId.systemDefault()).toInstant());
    }
    }

    使用 java.time 改造後

    public static LocalDateTime getStartTimeOfDay(LocalDateTime date) {
    if (date == null) {
    return null;
    else {
    // 獲取一天的開始時間,即00:00
    return date.toLocalDate().atStartOfDay();
    }
    }
    public static LocalDateTime getEndTimeOfDay(LocalDateTime date) {
    if (date == null) {
    return null;
    else {
    // 獲取一天的結束時間,即23:59:59.999999999
    return date.toLocalDate().atTime(LocalTime.MAX);
    }
    }

    e. betweenStartAndEnd

    原來的方式

    public static Boolean betweenStartAndEnd(Date nowTime, Date beginTime, Date endTime) {
    Calendar date = Calendar.getInstance();
    date.setTime(nowTime);
    Calendar begin = Calendar.getInstance();
    begin.setTime(beginTime);
    Calendar end = Calendar.getInstance();
    end.setTime(endTime);
    return date.after(begin) && date.before(end);
    }

    使用 java.time 改造後

    public static Boolean betweenStartAndEnd(Instant nowTime, Instant beginTime, Instant endTime) {
    return nowTime.isAfter(beginTime) && nowTime.isBefore(endTime);
    }

    我這裏就只列了一些,如果有缺失的可以自己補充,不會寫的話直接問問ChatGPT,它最會幹這事了。最後把這些修改後的方法替換一下就行了。

    四、小結一下

    這個改造難度不高,但是復雜度非常高,一個地方沒改好,輕則介面報錯,重則啟動失敗,非常耗費精力,真不想改。

    >>

    END

    精品資料,超贊福利,免費領

    微信掃碼/長按辨識 添加【技術交流群

    群內每天分享精品學習資料

    最近開發整理了一個用於速刷面試題的小程式;其中收錄了上千道常見面試題及答案(包含基礎並行JVMMySQLRedisSpringSpringMVCSpringBootSpringCloud訊息佇列等多個型別),歡迎您的使用。

    👇👇

    👇點選"閱讀原文",獲取更多資料(持續更新中