避免舊日期,時間類
您使用的是已被證明是設計不良的舊日期時間類,混亂和麻煩。避免它們。
java。時間
使用內置於Java 8及更高版本中的java.time框架。見Tutorial。對於Java 6 & 7,請使用ThreeTen-Backport項目。對於Android,該後端端口的改編,ThreeTenABP。
下面是一些示例代碼,讓你去。我只是馬上掀起了這個,所以它可能不健壯。此代碼似乎爲你一個例子使用工作:
(週五18:00)+ 48 =(Tuseday 18:00)(週六,週日被忽略)
我概括這個代碼一點點。而不是幾個小時,在任何時間段(內部表示爲總秒數加上幾分之一秒(納秒))需要Duration
。您會注意到toString
方法的輸出使用標準的ISO 8601表示法,例如PT48H
用於48小時的示例。
假設您想要時間線上的真實時刻,我們需要使用時區來解釋夏令時(DST)等異常情況。爲此,我們需要以ZonedDateTime
開頭,其中結合了UTC(Instant
)時間軸上的時刻與時區(ZoneId
)。
我們還傳遞一個持續時間,如48小時,以及一組週日值,如星期六的星期六&。跳過這個方法來討論下面這些類型。
這種方法的策略是將我們的持續時間例如48小時和芯片一次一天地拿走,以達到第二天開始所需的量。如果第二天恰好是禁止的星期幾,我們就會滑到第二天的那一天,並繼續滑動,直到達到允許的(不禁止的)星期幾日期。我們繼續蠶食我們的剩餘時間,直到達到零。閱讀代碼中的評論以獲得更多討論。
計算第二天的開始時,不要假定時間爲00:00:00.0
。由於夏令時(DST)以及某些時區的其他異常情況,該日可能會在另一時間開始。我們呼叫atStartOfDay
讓java.time確定一天中的第一個時刻。
public ZonedDateTime addDurationSkippingDaysOfWeek (ZonedDateTime zdt , Duration duration , EnumSet<DayOfWeek> daysOfWeek) {
// Purpose: Start at zdt, add duration but skip over entire dates where day-of-week is contained in EnumSet of prohibited days-of-week. For example, skip over United States weekend of Saturday-Sunday.
// Verify inputs.
if ((null == zdt) || (null == zdt) || (null == zdt)) {
throw new IllegalArgumentException ("Passed null argument. Message # bf186439-c4b2-423a-b5c9-76edebd87cf0.");
}
if (daysOfWeek.size() == DayOfWeek.values().length) { // We must receive 6 or less days. If passed all 7 days of the week, no days left to use for calculation.
throw new IllegalArgumentException ("The EnumSet argument specified all days of the week. Count: " + daysOfWeek.size() + ". So, impossible to calculate if we skip over all days. Message # 103a3088-5600-4d4e-a1e0-54410afa14f8.");
}
// Move through time, day-to-day, allocating remaining duration.
ZoneId zoneId = zdt.getZone(); // Passed as argument in code below.
ZonedDateTime moment = zdt; // This var is reassigned in loop below to fresh value, later and later, as we allocate the Duration to determine our target date-time.
Duration toAllocate = duration; // Loop below chips away at this Duration until none left.
while ( ! toAllocate.isZero()) { // Loop while some duration remains to be allocated.
if (toAllocate.isNegative()) { // Bad - Going negative should be impossible. Means our logic is flawed.
throw new RuntimeException ("The duration to allocate ran into a negative amount. Should not be possible. Message # 15a4267d-c16a-417e-a815-3c8f87af0232.");
}
ZonedDateTime nextDayStart = moment.toLocalDate().plusDays (1).atStartOfDay (zoneId);
Duration untilTomorrow = Duration.between (moment , nextDayStart);
// ZonedDateTime oldMoment = moment; // Debugging.
Duration allocation = null;
if (untilTomorrow.compareTo (toAllocate) >= 0) { // If getting to tomorrow exceeds our remaining duration to allocate, we are done.
// Done -- we can allocate the last of the duration. Remaining to allocate is logically zero after this step.
moment = moment.plus (toAllocate);
allocation = toAllocate; // Allocating all of our remaining duration.
// Do not exit here; do not call "return". Code below checks to see if the day-of-week of this date is prohibited.
} else { // Else more duration to allocate, so increment to next day.
moment = nextDayStart;
allocation = untilTomorrow; // Allocating the amount of time to take us to the start of tomorrow.
}
toAllocate = toAllocate.minus (allocation); // Subtract the amount of time allocated to get a fresh amount remaining to be
// Check to see if the moment has a date which happens to be a prohibited day-of-week.
while (daysOfWeek.contains (moment.getDayOfWeek())) { // If 'moment' is a date which is a day-of-week on oun prohibited list, move on to the next date.
moment = moment.toLocalDate().plusDays (1).atStartOfDay (zoneId); // Move to start of the next day after. Continue loop to check this next day.
}
}
return moment;
}
要調用該代碼,我們將使用您的示例數據值。作爲獲取原始數據輸入的一種方式,我們將一個字符串解析爲LocalDateTime
。
String input = "2016-01-01T18:00:00";
LocalDateTime ldt = LocalDateTime.parse (input);
這LocalDateTime
對象不不代表時間軸上的實際的時刻。所以我們申請一個時區來獲得一個實際的時刻,一個ZonedDateTime
。
ZoneId zoneId = ZoneId.of ("America/Montreal");
ZonedDateTime zdt = ldt.atZone (zoneId);
Duration duration = Duration.ofHours (48);
我也是從「the weekend」推廣到任何一組中某一天的周值,而不是硬編碼的週六週日&。 java.time類包括一個方便的enum,DayOfWeek
- 比我們的代碼中使用字符串或數字要好得多。
在大多數語言中,Java中的enum工具比簡單的數字屏蔽常量更靈活有用。其特點是當你想收集由你的枚舉定義的可能項目的一個子集時,一個特殊的Set
實現EnumSet
。對於我們這裏,我們想收集一對物品,爲週六項目和週日項目。
EnumSet<DayOfWeek> daysOfWeek = EnumSet.of (DayOfWeek.SATURDAY , DayOfWeek.SUNDAY);
也許工作日是四天工作制,如週一,週二加週四,週五,然後使用EnumSet.of (DayOfWeek.WEDNESDAY , DayOfWeek.SATURDAY , DayOfWeek.SUNDAY)
。
現在,我們準備調用我們的計算方法。傳遞三個參數:
- 起始
ZonedDateTime
- 一個
Duration
被添加到起始日期 - 時間
- 的
DayOfWeek
項目的EnumSet
。
我們回到我們的計算未來的日期,時間。
ZonedDateTime zdtLater = this.addDurationSkippingDaysOfWeek (zdt , duration , daysOfWeek);
轉儲到控制檯。
System.out.println ("zdt: " + zdt + " plus duration: " + duration + " while skipping daysOfWeek: " + daysOfWeek + " is zdtLater: " + zdtLater);
ZDT:2016-01-01T18:00-05:00 [美國/蒙特利爾]加持續時間:PT48H同時跳過daysOfWeek:[星期六,星期天]是zdtLater:2016-01-05T18:00 -05:00 [美國/蒙特利爾]
您是否必須在週末考慮國家法定節假日?你有沒有做出自己的努力? – skyking
不僅週末(週六和週日)@skyking – MBH
我想你可以檢查一下週末是否重疊(多少次),只需加上n次的X 48' –