你知道 Java 日期模式中的 'Y' 和 'y' 字元有什麼差別嗎?在本文中,我們將探討不正確的日期格式如何導致錯誤。我們也將推出新的 Java V6122 診斷規則,讓您免於突然的時間旅行。
撣去大TODO筆記本上的灰塵後,我們偶然發現了一個特別有趣的案例。該文章的評論者強調了潛在的問題。
評論
順便說一句,這裡有一個主意:Java 中的 SimpleDateFormat 可以帶來新年驚喜——用小寫字母書寫的年份與用大寫字母書寫的年份不同。在這一年的最後一周,您可能會突然發現自己身處未來,因為 2021 年 12 月 27 日至 2021 年 12 月 31 日的“YYYY”是 2022 年,而不是某些人預期的 2021 年。
讓我們來一探究竟。
如果您忘記了 SimpleDateFormat 是什麼,您可以在這裡溫習一下知識。
為了儲存和顯示日期,我們經常需要它們遵循特定的模式。 SimpleDateFormat 是一個類,使我們能夠根據給定的模式輕鬆格式化日期。除了格式化之外,SimpleDateFormat還可以解析字串並將其轉換為日期物件。
這就是 Java 所能提供的全部嗎?除了 SimpleDateFormat 之外,DateTimeFormatter 類別還可以格式化日期。
讓我向您展示使用這兩個類別的範例。
如果我們透過 SimpleDateFormat 來格式化日期:
public static void main(String[] args) { Date date = new Date("2024/12/31"); var dateFormatter = new SimpleDateFormat("dd-MM-yyyy"); System.out.println(dateFormatter.format(date)); }
控制台顯示以下內容:
2024年12月31日
這裡發生了什麼事?
我們有日期,我們想以特定格式儲存/顯示它。為了實現它,我們透過將模式字串傳遞給建構子來建立 SimpleDateFormat 物件。日期的格式完全按照此模式進行。 format 方法傳回格式化日期的字串表示形式。這正是我們想要的。
這裡是同樣的事情,但是我們使用DateTimeFormatter:
public static void main(String[] args) { LocalDate date = LocalDate.of(2024, 12, 31); var formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy"); System.out.println(formatter.format(date)); }
控制台顯示以下內容:
2024年12月31日
相同的步驟和結果,但略有不同:DateTimeFormatter 使我們能夠僅格式化由實作 TemporalAccessor 介面的類別表示的日期。例如,其中包括 LocalDate 和 LocalDateTime。 SimpleDateFormat 僅格式化 Date 類別的物件。
讓我們回到評論。在日期模式中使用“Y”代替“y”真的會改變結果嗎?
看一下程式碼:
public static void main(String[] args) { Date date = new Date("2024/12/31"); var dateFormatter = new SimpleDateFormat("dd-MM-yyyy"); System.out.println(dateFormatter.format(date)); }
控制台顯示以下內容:
2025年12月31日
哎呀。 DateTimeFormatter 也是一樣嗎?
程式碼如下:
public static void main(String[] args) { LocalDate date = LocalDate.of(2024, 12, 31); var formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy"); System.out.println(formatter.format(date)); }
控制台顯示以下內容:
2025年12月31日
我們確實旅行到了一年後的未來。是時候看看發生了什麼了。
我的第一步是查看 SimpleDateFormat 類別的文檔。下面是表格的一小部分,描述了日期模式如何解釋我們感興趣的字母字元:
Letter | Date or Time Component | Presentation | Examples |
---|---|---|---|
y | Year | Year | 1996; 96 |
Y | Week year | Year | 2009; 09 |
一年一週?讓我解釋一下。
週年是基於一年中的周數的一年。它是什麼意思以及為什麼它很重要?
在某些任務中,一年中週的序號很重要。為了確定一週的序號,我們需要決定先考慮哪一週。這是因為從一年到另一年的過渡通常會導致兩年之間有幾週的時間。那麼,我們該如何判斷本週屬於哪一年呢? ISO-8601 標準對此進行了規定。
根據這個標準,一年中的第一週必須符合以下條件:
由此,我們可以推導出一個簡單的規則:如果星期四是一月,則一周被認為是一年的第一周。
一個現實生活中的例子可以進一步闡明這一點。
讓我們從範例中取得日期,即 31.12.2024。以下是包含我們日期的一週的日曆片段(2024 年 12 月至 2025 年 1 月):
Mo | Tu | We | Th | Fr | Sa | Su |
---|---|---|---|---|---|---|
30 | 31 | 1 | 2 | 3 | 4 | 5 |
本週被視為 2025 年的第一周,因為它滿足上述條件(一月有 5 天)。因此,如果我們使用「Y」說明符,我們就會得到 2025 年。
你可以猜到,DateTimeFormatter 的情況是完全一樣的。
要注意的是,我們不僅可以穿越到未來,還可以穿越到過去。
我們換個日期吧,2027 年 1 月 1 日。
2027 年的第一週包括 1 月 1 日嗎?再次,讓我們查閱日曆。
以下是包含我們日期的一週的日曆片段(2026 年 12 月 - 2027 年 1 月):
Mo | Tu | We | Th | Fr | Sa | Su |
---|---|---|---|---|---|---|
28 | 29 | 30 | 31 | 1 | 2 | 3 |
由於這一周只有 3 天(根據要求第一周應該是 4 天),因此算作 2026 年的最後一周。所以,日期使用“Y”格式化時角色,將向我們展示2026。
這是證據。看一下程式碼:
public static void main(String[] args) { Date date = new Date("2024/12/31"); var dateFormatter = new SimpleDateFormat("dd-MM-yyyy"); System.out.println(dateFormatter.format(date)); }
控制台顯示以下內容:
2026 年 1 月 1 日
好的,我們已經剖析了這個問題。我們還發現在 PVS-Studio Java 分析器中添加專用診斷規則非常有趣。
診斷規則已經寫好了,是時候測試一下了。
在分析器的開發過程中,測試階段之一涉及執行迴歸測試。我們有一篇關於這個過程的文章。簡而言之,當我們新增新的診斷規則時,我們會分析大量開源項目,並將新報告與參考報告進行比較。
在此診斷規則的情況下,分析器針對多個項目發出了新的警告。讓我們來看看它們。
在這個專案中,診斷規則指向的程式碼片段是相同的,所以我只展示其中一個。
看一下程式碼:
public static void main(String[] args) { LocalDate date = LocalDate.of(2024, 12, 31); var formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy"); System.out.println(formatter.format(date)); }
PVS-Studio 警告:
V6122 偵測到使用「Y」(週年)模式:它可能打算使用「y」(年)。 SkeinParameters.java 246
首先,我瀏覽了GitHub。如果這確實是一個錯誤,開發人員已經發現它並提交了修復怎麼辦?這正是發生的事情。這是提交的鏈接,你可以查看一下。模式中的所有“Y”(週年)字元均替換為“y”(年)。
您可能想知道為什麼提交是在相對較長的時間之前進行的。讓我解釋一下。我們的回歸測試並不是為了持續控制這個或那個開源專案的品質。任務是查看新增診斷規則時報告如何變更:舊警告不應消失,不應出現新錯誤,因為這表示分析儀有問題。所以,檢查的程式碼必須是相同的。
現在讓我們來看看觸發診斷規則的第二個項目。
PVS-Studio 警告:
V6122 偵測到使用「Y」(週年)模式:它可能打算使用「y」(年)。 RepositoryInfo.java 77
代碼:
public static void main(String[] args) { Date date = new Date("2024/12/31"); var dateFormatter = new SimpleDateFormat("dd-MM-YYYY"); System.out.println(dateFormatter.format(date)); }
就像之前的專案一樣,我衝到提交處看看發生了什麼事。首先,值得注意的是,由於重構,該欄位已移至其衍生類別 Repository(這裡是提交的連結)。我進一步搜索並找到了包含修復程序的提交。日期模式中的“Y”字元已替換為“y”:
public static void main(String[] args) { LocalDate date = LocalDate.of(2024, 12, 31); var formatter = DateTimeFormatter.ofPattern("dd-MM-YYYY"); System.out.println(formatter.format(date)); }
所以,這裡也是一個錯誤。
在PVS-Studio,我們歡迎來自社群的任何回饋。在解決 Habr 使用者的評論後,我們透過添加一些有用的診斷規則來增強 Java 分析器。因此,如果您有任何想法想與我們分享,我們很樂意在本文的評論部分與您聊天。
順便說一句,診斷規則已在 10 月 7.33 版本中引入。因此,如果您想嘗試我們的分析器,請使用此連結。
僅此而已。讓我們把事情總結到這裡。希望突然的前進(或倒退)一年不會讓您措手不及。
以上是YYYY? yyyy!的詳細內容。更多資訊請關注PHP中文網其他相關文章!