簡體   English   中英

Java“周年”到底是如何工作的?

[英]How does Java "week year" really work?

這開始是一個簡單的錯誤:我在SimpleDateFormat對象的格式字符串中使用YYYY而不是yyyy 但是我對格式字符串不正確的測試結果感到非常困惑。

這段代碼:

@Test
public void whatTheHell() {
        try {
                SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/YYYY");

                Date d1 = sdf.parse("01/07/2016");
                Date d2 = sdf.parse("02/08/2016");
                Date d3 = sdf.parse("11/29/2027");

                System.out.println(d1.toString());
                System.out.println(d2.toString());
                System.out.println(d3.toString());
        } catch (ParseException pe) {
                fail("ParseException: " + pe.getMessage());
        }
}

產生這個輸出:

Sun Dec 27 00:00:00 PST 2015
Sun Dec 27 00:00:00 PST 2015
Sun Dec 27 00:00:00 PST 2026

我在這里閱讀了關於“Y”參數的文檔: https ://docs.oracle.com/javase/7/docs/api/java/util/GregorianCalendar.html,但我仍然看不到邏輯在這里工作。 尤其是最后一個例子:我有點理解 1 月(也許是 2 月)的日期如何可以轉換為上一年的 12 月,但是將 11 月 29 日的日期向后移動 11 個月讓我感到困惑。 12 月 27 日有什么特別之處?

誰能解釋一下?

更多信息

@Jan 建議依賴 toString() 方法可能會出現問題,因此我定義了一種日期格式以在與上面相同的代碼中打印YYYY MM dd '-' yyyy MM dd 這是額外的輸出:

2016 12 27 - 2015 12 27
2016 12 27 - 2015 12 27
2027 12 27 - 2026 12 27

很簡單:2015 年 12 月 27 日是 2016 年第 1 周的第 1 天(2026 年 12 月 27 日是 2027 年第 1 周的第 1 天)。 這可以通過添加以下行來驗證:

SimpleDateFormat odf = new SimpleDateFormat("YYYY-ww-u");
System.out.println(odf.format(d1));
System.out.println(odf.format(d2));
System.out.println(odf.format(d3));

如果SimpleDateFormat輸出一個日期,它可以使用所有字段:年、月、日、星期幾、月中的周、年中的周、年中的周等。

在解析時, SimpleDateFormat需要一組匹配的值:日、月、年星期幾、一年中的一周、一年中的一周。 由於您提供了一周年但沒有提供一年中的星期幾和星期幾,因此這些值已假定為 1。


實際值取決於您的語言環境:

  • 一年中的哪一周是第 1 周
  • 哪一天是一周的第一天

(參見https://docs.oracle.com/javase/7/docs/api/java/util/GregorianCalendar.html#week_and_year

在我的系統上(使用 de-ch 區域設置,格式為“EEE MMM dd HH:mm:ss zzz yyyy - YYYY-ww-u”)

Mo Jan 04 00:00:00 MEZ 2016 - 2016-01-1
Mo Jan 04 00:00:00 MEZ 2016 - 2016-01-1
Mo Jan 04 00:00:00 MEZ 2027 - 2027-01-1

定義各不相同

可以用不同的方式定義一周。 例如,在美國,一周的第一天通常被認為是星期日,而在歐洲和許多其他地方,一周的第一天是星期一。

同樣,也可以用不同的方式定義基於周的年份中的星期。

  • 第 1 周包含 1 月 1 日。
  • 第 1 周是包含特定星期幾(例如星期日)的第一周。
  • 第 1 周是第一周,只包含新年的幾天,沒有前一年的日期。
  • … 和更多

您正在使用的遺留類隱式使用由Locale指定的定義。 所應用的語言環境也是隱式的,如果您沒有另外指定,則使用 JVM 當前的默認Locale

有關定義一周的困難的更多有趣細節,請參閱此問題, JVM 8 和 JVM 10 上 WeekFields 的不同行為

國際標准化組織 8601

有一個實用的日期時間處理國際標准ISO 8601

ISO 8601 對一周的定義是:

  • 每周從星期一開始
  • 第 1 周是日歷年的第一個星期四
  • 一年由 52 或 53 個整周組成。
  • 一年可能有前一個日歷年的一天或多天,也可能有下一個日歷年的一天。

我建議盡可能使用 ISO 8601 標准定義。 該標准定義簡單且合乎邏輯,並在各行業中得到越來越多的采用。

java.time

java.time類在WeekFields類中為基於一周的一年提供了一些支持。

LocalDate ld = LocalDate.of( 2019 , Month.JANUARY , 1 ) ;
long week = ld.get( WeekFields.ISO.weekOfWeekBasedYear() ) ;

在 IdeOne.com 上查看此代碼的實時運行

ld.toString(): 2019-01-01

周:1

org.threeten.extra.YearWeek

但是,如果要完成大量此類工作,我建議將ThreeTen-Extra庫添加到您的項目中。 您會發現YearWeek類很有幫助。 此類提供了幾種方便的方法,例如為該周內的任何一天生成LocalDate

LocalDate ld = LocalDate.of ( 2019 , Month.JANUARY , 1 );
YearWeek yw = YearWeek.from ( ld );
LocalDate startOfWeek = yw.atDay ( DayOfWeek.MONDAY );

ld.toString(): 2019-01-01

yw.toString(): 2019-W01

startOfWeek.toString(): 2018-12-31

請注意,在基於周的 2019 年中,一年中的第一天是上一個日歷年 2018 年而不是 2019 年的日期。

2019 年 1 月的日歷,帶有周數,顯示第一周從上一個日歷年的 2018-12-31 開始


關於java.time

java.time框架內置於 Java 8 及更高版本中。 這些類取代了麻煩的舊的遺留日期時間類,例如java.util.DateCalendarSimpleDateFormat

要了解更多信息,請參閱Oracle 教程 並在 Stack Overflow 中搜索許多示例和解釋。 規范是JSR 310

現在處於維護模式Joda-Time項目建議遷移到java.time類。

您可以直接與數據庫交換java.time對象。 使用符合JDBC 4.2或更高版本的JDBC 驅動程序 不需要字符串,不需要java.sql.*類。

在哪里獲取 java.time 類?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM