簡體   English   中英

添加月時的Java日歷/日期問題

[英]Java Calendar/Date Issue while adding Month

如果我們在當前日期(IST 2013年5月31日星期五18:33:00)加上1個月,則會得出:

星期日2013年6月30日18:33:00

如果我們減去1個月,將得出:

IST 2013年5月30日18:33:00

是一個錯誤還是任何人都可以提供推理?

請找到相同的代碼:

Calendar c1 = Calendar.getInstance()
System.out.println(c1.getTime());
c1.add(Calendar.MONTH, 1);
System.out.println(c1.getTime());
c1.add(Calendar.MONTH, -1);
System.out.println(c1.getTime());

輸出量

Fri May 31 18:33:00 IST 2013
Sun Jun 30 18:33:00 IST 2013
Thu May 30 18:33:00 IST 2013

每月的更改是預期的行為,在此處記錄為“添加規則2”: http : //docs.oracle.com/javase/6/docs/api/java/util/Calendar.html

add(f,delta)將delta添加到字段f。 這等效於通過兩個調整來調用set(f,get(f)+ delta):

添加規則1.調用后字段f的值減去調用之前字段f的值是delta,對字段f中發生的任何溢出取模。 當字段值超出其范圍時會發生溢出,結果,下一個較大的字段將遞增或遞減,然后將字段值調整回其范圍。

添加規則2 如果預期較小的字段是不變的,但由於字段f更改后其最小值或最大值或其他約束(例如時區偏移量更改)而發生變化,則不可能使其等於先前值,則其值是不可能的調整為盡可能接近其期望值。 較小的字段表示較小的時間單位。 HOUR是比DAY_OF_MONTH小的字段。 不會對預計不會不變的較小字段進行調整。 日歷系統確定期望哪些字段是不變的。

有了這些規則,如果再添​​加1個月,然后再添加-1個月,就無法保留月份中的某天。

不,這是一個功能,並已記錄在案(很抱歉,這里很挑剔,但您在詢問之前是否確實閱讀過文檔?)。 閱讀有關add()方法的文檔中的“ Field Manipulation部分。 相關部分:

如果預期較小的字段是不變的,但由於字段f更改后其最小值或最大值或其他約束(例如時區偏移量更改)而發生變化,則不可能使其等於先前值,則其值是不可能的調整為盡可能接近其期望值。

示例:考慮一個最初設置為1999年8月31日的GregorianCalendar。調用add(Calendar.MONTH,13)將日歷設置為2000年9月30日。添加規則1將MONTH字段設置為9月,因為將8個月加上13個月將得出9月下一年。 由於在GregorianCalendar中,DAY_OF_MONTH在9月不能為31,因此添加規則2會將DAY_OF_MONTH設置為30(最接近的可能值)。 盡管它是一個較小的字段,但是DAY_OF_WEEK不受規則2的調整,因為當GregorianCalendar中的月份更改時,它可能會更改。

這是設計正確的。

從5月31日起增加一個月后,您將獲得6月30日(該月的最后一天)。

如果您從6月30日減去一個月,則會得到5月31日(一個月前)。

當您使用getTime()獲得時間時,便完成了計算。 因此,我在這里看不到任何錯誤。

使用取代了麻煩的舊舊日期時間類(包括Calendar的java.time類來進行現代更新。

時區

定義您的時區。 continent/region的格式指定正確的時區名稱 ,例如America/MontrealAfrica/CasablancaPacific/Auckland 切勿使用ESTIST等3-4個字母的縮寫,因為它們不是真實的時區,不是標准化的,甚至不是唯一的(!)。 我猜您的意思是IST是印度時間,但也許是愛爾蘭標准時間或其他時間?

final ZoneId z = ZoneId.of ( "Asia/Kolkata" );

ZonedDateTime類表示調整到特定時區的時間軸上的時刻。

final ZonedDateTime zdt = ZonedDateTime.of ( 2013, 5, 31, 18, 33, 0, 0, z );

plusMonths | minusMonths

final ZonedDateTime zdtMonthPlus = zdt.plusMonths ( 1 );
final ZonedDateTime zdtMonthMinus = zdtMonthPlus.minusMonths ( 1 );

轉儲到控制台。

System.out.println ( "zdt.toString(): " + zdt );
System.out.println ( "zdtMonthPlus.toString(): " + zdtMonthPlus );
System.out.println ( "zdtMonthMinus.toString(): " + zdtMonthMinus );

我們看到的行為與舊版Calendar類相同。 如果從5月31日開始添加一個月,則沒有6月31日,因此它會退回到該月的最后一天,即6月30日。從6月30日減去時,該類將嘗試匹配同一天,因此它使用May 30歲

zdt.toString():2013-05-31T18:33 + 05:30 [亞洲/加爾各答]

zdtMonthPlus.toString():2013-06-30T18:33 + 05:30 [亞洲/加爾各答]

zdtMonthMinus.toString():2013-05-30T18:33 + 05:30 [亞洲/加爾各答]

plus( Duration) | minus( Duration )

如果您想要其他行為,請對您的代碼使用另一種方法。 例如,如果用“月”來表示“ 30天的普通24小時天(忽略諸如夏時制之類的異常)”,則應添加/減去30天的持續時間。

Duration thirtyDays = Duration.ofDays( 30 );  // 30 days of generic 24-hour length. Ignoring anomalies such as DST. Ignoring calendar months.
final ZonedDateTime zdtMonthPlusDuration = zdt.plus ( thirtyDays );
final ZonedDateTime zdtMonthMinusDuration = zdtMonthPlusDuration.minus ( thirtyDays );

fiftyDays.toString():PT720H

zdtMonthPlusDuration.toString():2013-06-30T18:33 + 05:30 [亞洲/加爾各答]

zdtMonthMinusDuration.toString():2013-05-31T18:33 + 05:30 [亞洲/加爾各答]


關於java.time

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

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

要了解更多信息,請參見Oracle教程 並在Stack Overflow中搜索許多示例和說明。 規格為JSR 310

在哪里獲取java.time類?

ThreeTen-Extra項目使用其他類擴展了java.time。 該項目為將來可能在java.time中添加內容提供了一個試驗場。 您可以在這里找到一些有用的類,比如IntervalYearWeekYearQuarter ,和更多

暫無
暫無

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

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