2014-10-06 48 views
2

datetime對象時,讓我描述我的方案奇怪的行爲。在這臺服務器中,我需要獲取時間服務器,將其轉換爲歐洲/馬德里時區,然後檢查獲取的日期是否在日期間隔內。比較JODA

奇怪的是,我得到的迴應表明,當前的服務器時間一旦轉換爲歐洲/馬德里時區之前的較低日期間隔,這很奇怪。

這裏是我正在做這個,獲取服務器的時間並將其轉換爲歐洲/馬德里時區:

DateTimeZone timeZoneMadrid = DateTimeZone.forID("Europe/Madrid"); 
DateTimeFormatter formatter = DateTimeFormat.forPattern("YYYY-MM-dd HH:mm"); 
DateTime nowServer = new DateTime(); 
log.debug("Current server time is " + nowServer.toString(formatter)); 
DateTime nowServerSpanishTimeZone = nowServer.withZone(timeZoneMadrid); 
log.debug("Current server time converted to Madrid Zone is " + nowServerSpanishTimeZone.toString(formatter)); 

輸出:

Current server is 2014-10-06 06:12 
Current server time converted to Madrid Zone is 2014-10-06 12:12 

現在,我創建的日期時間爲間隔,開始和結束,基於轉換後的日期時間:

int year = serverTimeConverted.getYear(); 
int month = serverTimeConverted.getMonthOfYear(); 
int day = serverTimeConverted.getDayOfMonth(); 
this.setStartDate(new DateTime(year, month, day, 8, 0, 0, 0)); 
this.setEndDate(new DateTime(year, month, day, 21, 0, 0, 0)); 

正如您所看到的,我的間隔fr om 08:00:00至21:00:00

然後我檢查服務器時間轉換是否在日期範圍內,這是非常詳細的,因爲我添加了很多檢查和輸出,因爲奇怪的行爲.. :

private boolean withinTimeRange(DateTime now, DateTime start, DateTime end){ 

    DateTimeFormatter formatter = DateTimeFormat.forPattern("YYYYMMdd-HH:mm"); 
    String currentDate = now.toString(formatter); 
    long nowTimeStamp = now.getMillis()/1000; 
    long startTimeStamp = start.getMillis()/1000; 
    long endTimeStamp = end.getMillis()/1000; 
    log.debug("Checking if date " + currentDate + " is in the interval dates " + start.toString(formatter) + " and " + end.toString(formatter)); 
    log.debug("Checking if UNIX timestamp " + nowTimeStamp + " is in the interval dates " + startTimeStamp + " and " + endTimeStamp); 
    if (!now.isBefore(start)){ 
     log.debug("Current time " + currentDate + " is not before " + start.toString(formatter)); 
     if (!now.isAfter(end)){ 
      log.debug("Current time " + currentDate + " is not after " + end.toString(formatter)); 
      return true; 
     } 
     else{ 
      log.debug("Current time " + currentDate + " is after " + end.toString(formatter)); 
      return false; 
     } 
    } 
    else{ 
     log.debug("Current time " + currentDate + " is before " + start.toString(formatter)); 
     return false; 
    } 
} 

只要打電話與轉換的時間服務器的方法和開始和結束日期,併爲以前的輸出,其中服務器的時間轉換爲2014年10月6日12:12,我從這個輸出以前的方法:

Checking if date 20141006-12:12 is in the interval dates 20141006-08:00 and 20141006-21:00 
Checking if UNIX timestamp 1412590332 is in the interval dates 1412596800 and 1412643600 
Current time 20141006-12:12 is before 20141006-08:00 
Current timeserver converted to Madrid TimeZone is not within time range, skipping iteration 

正如你可以看到服務器時間轉換的時間戳是在開始日期時間之前.....這怎麼可能?

我認爲我在創建DateTime開始和結束時做錯了什麼,我嘗試使用.withTimeZone(「Europe/Madrid」)創建它們,但後來我得到了最奇怪的行爲......任何線索?

謝謝!

UPDATE:根據以往的SO質疑here,我修改了上面的代碼,現在,它的工作原理:

DateTime now = new DateTime(); 
LocalDate today = now.toLocalDate(); 
LocalDate tomorrow = today.plusDays(1); 
DateTimeZone timeZoneMadrid = DateTimeZone.forID("Europe/Madrid"); 
DateTime start = today.toDateTimeAtStartOfDay(timeZoneMadrid); 
DateTime end = tomorrow.toDateTimeAtStartOfDay(timeZoneMadrid); 
start = start.plusHours(8); 
end = end.minusHours(4); 
Interval interval = new Interval(start, end); 
DateTimeFormatter formatter = DateTimeFormat.forPattern("YYYY-MM-dd HH:mm"); 
String currentDate = now.toString(formatter); 
if (interval.contains(now)){ 
    return true; 
} 
else{ 
    return false; 
} 

回答

3

方法DateTime::getMillis返回毫秒的獨立區域。這個值是一個常數。
一旦創建DateTime實例將在任何時區返回相同的毫秒。

final DateTime now = DateTime.now(); 
now.withZone(texas).getMillis() == now.withZone(madrid).getMillis(); // true 

DateTime::isAfterDateTime::isBefore方法比較由DateTime::getMillis方法返回米利斯。
所以這個值也是區域獨立的。

但是打印DateTimeDateTimeFormatter是區域相關的。它將在不同的時區打印不同的小時/分鐘

所以,如果你想單獨比較日期區域,那麼你的結果是正確的。

例子:
- 前提條件:

DateTimeZone usZone = DateTimeZone.forID("US/Eastern"); 
    DateTimeZone spZone = DateTimeZone.forID("Europe/Madrid"); 
    DateTimeZone.setDefault(usZone); 

你的代碼在接下來的方式:

DateTime serverDate = new DateTime(2014, 10, 6, 6, 12);  // US zone 
DateTime dateInMadrid = serverDate.withZone(spZone); // zone is Madrid, but .getMillis() will return the same value 
DateTime startDate = new DateTime(2014, 10, 6, 8, 0); // US zone 
// startDate = startDate.withZone(spZone) - this will not change the result 
DateTime endDate = new DateTime(2014, 10, 6, 21, 0); // US zone 
// endDate = endDate.withZone(spZone) - this will also not change the result 
System.out.println(dateInMadrid .isAfter(startDate) && dateInMadrid .isBefore(endDate)); 
// false - it is correct. because all dates were created in US zone 

正確方法:你應該在馬德里區創建startend日期

DateTime serverDate = new DateTime(2014, 10, 6, 6, 12); 
DateTime startDateMadrid = new DateTime(2014, 10, 6, 8, 0, spZone); // Madrid zone is used in constructor! 
DateTime endDateMadrid = new DateTime(2014, 10, 6, 21, 0, spZone); // Madrid zone is used in constructor! 
System.out.println(serverDate.isAfter(startDateMadrid) && serverDate.isBefore(endDateMadrid)); 
// true 
+0

Hello Ilya,那麼服務器時間的時間戳是在上面的間隔日期之前?根據你的建議,這是做到這一點的正確方法......那麼爲什麼這種奇怪的行爲呢?或者你的建議我不會將服務器時間轉換爲歐洲/馬德里時區? – AlejandroVK 2014-10-07 13:48:32

+0

@AjjandroVK服務器時間是上部間隔日期*區域依賴性*,但較低*區域獨立*。表達式'dt1.isBefore(dt2)'等於'd1.getMillis() Ilya 2014-10-07 18:34:47

+0

那麼?老實說,我仍然不明白你的觀點......你能提供一些實際的例子嗎?我甚至嘗試過使用JODA Interval類,它給出了相同的結果,錯誤 – AlejandroVK 2014-10-08 08:53:43