将DateTime对象与JODA进行比较时出现奇怪的行为

时间:2014-10-06 10:32:11

标签: java datetime jodatime

让我描绘一下我的情景:

我在位于德克萨斯州达拉斯的服务器上有一个正在运行的应用程序(我认为它在内部使用EDT时区)。在这个服务器中,我需要获取时间服务器,将其转换为欧洲/马德里时区,然后检查获得的日期是否在日期间隔内。

奇怪的是,我得到一个响应,表明当前服务器时间一旦转换到欧洲/马德里时区,就在较低的日期间隔之前,这非常奇怪。

以下是我如何做到这一点,获取服务器时间并将其转换为欧洲/马德里时区:

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

现在,我根据转换的DateTime:

为间隔,开始和结束创建DateTime
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));

如您所见,我的间隔时间为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-06 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")创建它们,但后来我得到了最奇怪的行为......任何线索?

谢谢!

更新:基于之前的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;
}

1 个答案:

答案 0 :(得分:3)

方法DateTime::getMillis返回毫秒,与zone无关。该值是常数 创建DateTime实例后,将在任何时区返回相同的毫秒数。

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

DateTime::isAfterDateTime::isBefore方法比较DateTime::getMillis方法返回的毫秒数 所以这个值也是区域独立的。

但使用DateTime打印DateTimeFormatter与区域有关。它将在不同的时区打印不同的小时/分钟

因此,如果您想独立比较日期区域,那么您的结果是正确的。

示例:
- 前提条件:

  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