2014-08-28 68 views
4

什麼是適當和更簡潔的方式來獲取ZonedDateTime(s),它代表在代碼運行的系統上設置的時區中當前日期的開始和結束?野田時間 - 帶區域的開始/結束日期

是不是下面的代碼太複雜了?

ZonedDateTime nowInZone = SystemClock.Instance.Now.InZone(DateTimeZoneProviders.Bcl.GetSystemDefault()); 

ZonedDateTime start = new LocalDateTime(nowInZone.Year, nowInZone.Month, nowInZone.Day, 0, 0, 0).InZoneStrictly(DateTimeZoneProviders.Bcl.GetSystemDefault()); 

ZonedDateTime end = new LocalDateTime(nowInZone.Year, nowInZone.Month, nowInZone.Day, 23, 59, 59).InZoneStrictly(DateTimeZoneProviders.Bcl.GetSystemDefault()); 

給定這些值,我需要測試他們之間是否有另一個ZonedDateTime。

+0

您可能想使用'InZoneLeniently'來代替。例如,巴西在其DST前沿轉型中,從下午11:59到凌晨1點。沒有上午12:00。 'InZoneStrictly'會拋出這種情況。 「InZoneLeniently」將在上午12點轉換LocalDateTime時(如果我正確閱讀文檔),將轉到凌晨1點。同樣在倒退時間從晚上11:59到晚上11點。有兩個11:59 pm。 'InZoneLeniently'選擇後面的一個。 – 2014-08-29 00:02:35

+0

@mikez - 關於巴西的好處。這已經被我演示的方法所涵蓋。謝謝。 – 2014-08-29 04:58:48

回答

6

DateTimeZone對象上的AtStartOfDay值具有您正在尋找的魔力。

// Get the current time 
IClock systemClock = SystemClock.Instance; 
Instant now = systemClock.Now; 

// Get the local time zone, and the current date 
DateTimeZone tz = DateTimeZoneProviders.Tzdb.GetSystemDefault(); 
LocalDate today = now.InZone(tz).Date; 

// Get the start of the day, and the start of the next day as the end date 
ZonedDateTime dayStart = tz.AtStartOfDay(today); 
ZonedDateTime dayEnd = tz.AtStartOfDay(today.PlusDays(1)); 

// Compare instants using inclusive start and exclusive end 
ZonedDateTime other = new ZonedDateTime(); // some other value 
bool between = dayStart.ToInstant() <= other.ToInstant() && 
       dayEnd.ToInstant() > other.ToInstant(); 

幾個要點:

  • 這是更好地從現在的口號分離時鐘實例的習慣來獲得。這使得單元測試後更換時鐘變得更容易。

  • 您只需要獲取當地時區一次。我更喜歡使用Tzdb供應商,但是任何一個供應商都會爲此目的而工作。

  • 對於一天結束,最好使用第二天的開始。這可以防止您不必處理粒度問題,例如您是否應該採取23:59,23:59:59,23:59.999,23:59:59.9999999等。此外,它可以更輕鬆地獲取整數做數學的結果。一般而言,日期+時間範圍(或時間範圍)應視爲半開放時間間隔[start,end) - 而僅限日期範圍應視爲完全關閉時間間隔[start,end]

  • 因此,將開始與<=進行比較,但最後與>進行比較。

  • 如果您確定其他ZonedDateTime值在同一時區並使用相同的日曆,則可以省略對ToInstant的調用並直接比較它們。

更新

如喬恩評論所提到的,Interval類型可以是用於此目的的有用的便利性。它已經設置爲使用Instant值的半開範圍。下面的函數將得到一個當前的「天」的時間間隔在一個特定的時間段:

public Interval GetTodaysInterval(IClock clock, DateTimeZone timeZone) 
{ 
    LocalDate today = clock.Now.InZone(timeZone).Date; 
    ZonedDateTime dayStart = timeZone.AtStartOfDay(today); 
    ZonedDateTime dayEnd = timeZone.AtStartOfDay(today.PlusDays(1)); 
    return new Interval(dayStart.ToInstant(), dayEnd.ToInstant()); 
} 

這樣稱呼它(使用上述相同的值):

Interval day = GetTodaysInterval(systemClock, tz); 

現在比較可以用Contains函數來完成:

bool between = day.Contains(other.ToInstant()); 

請注意,您還是要轉換爲Instant,爲Interval類型不是時區知道。

+0

它很好地隱藏在明顯的景象;) 感謝您的答案和您指出的最佳做法。 – Jhack 2014-08-29 13:12:37

+1

您可能還想提及'Interval' - 例如,您可以輕鬆地編寫一種方法,在給定時鐘和時區(或從2.0開始的ZonedClock)時返回「today」的時間間隔。 – 2014-08-31 16:55:35

相關問題