【问题标题】:Strange behaviour when comparing DateTime objects with JODA将 DateTime 对象与 JODA 进行比较时的奇怪行为
【发布时间】:2014-10-06 10:32:10
【问题描述】:

让我描述一下我的场景:

我在位于德克萨斯州达拉斯的服务器上有一个正在运行的应用程序(我认为它在内部使用 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;
}

【问题讨论】:

    标签: java datetime jodatime


    【解决方案1】:

    方法DateTime::getMillis 返回与区域无关的毫秒数。这个值是一个常数。
    一旦创建 DateTime 实例将在任何时区返回相同的毫秒。

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

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

    但是使用DateTimeFormatter 打印DateTime 取决于区域。它将在不同的时区打印不同的小时/分钟

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

    示例:
    - 前置条件:

      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
    

    【讨论】:

    • 您好,Ilya,那么为什么服务器时间的时间戳在上间隔日期之前?根据您的建议,这是正确的做法......为什么会有奇怪的行为呢?还是您建议我不要将服务器时间转换为欧洲/马德里时区?
    • @AlejandroVK 服务器时间是上间隔日期区域相关,但下区域独立。表达式dt1.isBefore(dt2) 等于d1.getMillis() < d2.getMillis()
    • 所以?老实说,我仍然不明白你的意思......你能提供一些工作示例吗?我什至用 JODA Interval 类尝试了这个,它给出了相同的结果,错误
    • @AlejandroVK 示例已添加
    • 看起来不错,谢谢 Ilya,尽管我通过查看另一个 SO 问题找到了解决方案。我会给你答案,花费的时间和精力,如果你感到好奇,请查看我更新的问题:)
    猜你喜欢
    • 2018-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多