[英]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。
實際值取決於您的語言環境:
(參見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
可以用不同的方式定義一周。 例如,在美國,一周的第一天通常被認為是星期日,而在歐洲和許多其他地方,一周的第一天是星期一。
同樣,也可以用不同的方式定義基於周的年份中的星期。
您正在使用的遺留類隱式使用由Locale
指定的定義。 所應用的語言環境也是隱式的,如果您沒有另外指定,則使用 JVM 當前的默認Locale
。
有關定義一周的困難的更多有趣細節,請參閱此問題, JVM 8 和 JVM 10 上 WeekFields 的不同行為
有一個實用的日期時間處理國際標准ISO 8601 。
我建議盡可能使用 ISO 8601 標准定義。 該標准定義簡單且合乎邏輯,並在各行業中得到越來越多的采用。
java.time類在WeekFields
類中為基於一周的一年提供了一些支持。
LocalDate ld = LocalDate.of( 2019 , Month.JANUARY , 1 ) ;
long week = ld.get( WeekFields.ISO.weekOfWeekBasedYear() ) ;
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 年的日期。
java.time框架內置於 Java 8 及更高版本中。 這些類取代了麻煩的舊的遺留日期時間類,例如java.util.Date
、 Calendar
和SimpleDateFormat
。
要了解更多信息,請參閱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.