2

我已经整理了一种方法,该方法应该检查给定日期是否在某个下限和上限之内。

它不应该考虑时间,只考虑日期。这似乎是一项非常简单的任务,但是在调试它时我遇到了一些奇怪的行为。

我尝试了不同的方法来从输入日期中删除时间值。( inDate.) 但它们似乎都给了我同样的意外行为,即把我的inLeft日期放在前面inLeft,从而使函数结果为假。

调试截图

正如您从屏幕截图中看到的那样inLeftinLeft它们相等(如果我们忽略时间)那么为什么会int leftLimit = DateTimeComparator.getDateOnlyInstance().compare(inLeft, inDate);导致 1 而不是 0?

我的结论是我做错了什么但找不到什么。如果这是完全明显的事情,请原谅我。

这是我的代码:

protected boolean checkDateInRange(Date inDate, Date inLeft, Date inRight) {
    System.out.println("inDate = " + inDate);
    System.out.println("inLeft = " + inLeft);
    System.out.println("inRight = " + inRight);

    int leftLimit = DateTimeComparator.getDateOnlyInstance().compare(inLeft, inDate);
    int rightLimit = DateTimeComparator.getDateOnlyInstance().compare(inDate, inRight);

    System.out.println("leftLimit = " + leftLimit);
    System.out.println("rightLimit = " + rightLimit);

    if (leftLimit > 0 || rightLimit > 0) {
        return false;
    }
    return true;
}

I/System.out: inDate = Mon Sep 26 00:00:00 GMT+02:00 2016
I/System.out: inLeft = Mon Sep 26 20:14:13 GMT+02:00 2016
I/System.out: inRight = Mon Sep 26 00:00:00 GMT+02:00 2016
I/System.out: leftLimit = 1
I/System.out: rightLimit = 0

问题是leftLimit == 1,而不是0,没有时间的inDate和inLeft是一样的。但 leftLimit 仍然结果为 1。

编辑(最终解决方案):根据 sumandas 和 Roberts 的解决方案,我编写了一种新方法,可以给我预期的行为。

新代码:

protected boolean checkDateInRange(Date inDate, Date inLeft, Date inRight) {
    SimpleDateFormat simpleDateFormatter = new SimpleDateFormat("yyyy-MM-dd");
    String dateString = simpleDateFormatter.format(inDate);
    String leftString = simpleDateFormatter.format(inLeft);
    String rightString = simpleDateFormatter.format(inRight);

    if (leftString.compareTo(dateString) > 0 || dateString.compareTo(rightString) > 0)
        return false;

    return true;
}

我仍然不明白为什么我的初始解决方案不起作用。

4

2 回答 2

0

尝试这个:

String date1 = "2014/09/12"
String date2 = "2016/09/12"
SimpleDateFormat simpleDateFormatter = new SimpleDateFormat("yyyy-MM-dd");

Date inDate = simpleDateFormatter.parse(date1);
Date inLeft = simpleDateFormatter.parse(date2);
Date rightDate = simpleDateFormatter.parse(date2);

将这些传递给如下函数并进行比较:

 protected boolean checkDateInRange(Date inDate, Date inLeft, Date inRight) {

    if (inDate.before(inLeft) || inDate.after(inRight)) {
      // Do something
    }
}

我的两分钱,我之前尝试过使用 Joda Time,但由于太多,如果我切换到 simpledateformat,但 joda time 在 ISO 日期格式方面有很大帮助。

为什么建议的解决方案不起作用: 如果您阅读文档,它说 LHS < RHS 返回 -ve 否则返回 +ve

现在以您的示例为例, getDateOnlyInstance 仅返回日期

LHS = Sep 26 2016 = RHS and returns a 1. // Hope this helps.
于 2016-08-18T17:49:43.083 回答
0

根据评论,我假设您使用的是 joda-time API(我在此测试中使用了2.7 版)。

为了解释为什么你的第一次尝试不起作用,我做了一个测试。DateTime首先,我为您的测试输入创建了等效对象:

// equivalent date/times in GMT+02:00
DateTime in = new DateTime(2016, 9, 26, 0, 0, 0, 0, DateTimeZone.forOffsetHours(2));
DateTime left = new DateTime(2016, 9, 26, 20, 14, 13, 0, DateTimeZone.forOffsetHours(2));
DateTime right = new DateTime(2016, 9, 26, 0, 0, 0, 0, DateTimeZone.forOffsetHours(2));

然后我调用了你的方法:

// using toDate to convert org.joda.time.DateTime to java.util.Date
checkDateInRangeWrong(in.toDate(), left.toDate(), right.toDate());

我得到的输出:

inDate = 2016 年 9 月 25 日星期日 19:00:00 BRT
inLeft = 2016 年 9 月 26 日星期一 15:14:13 BRT
inRight = 2016 年 9 月 25 日星期日 19:00:00 BRT

请注意,inDate' 天是25(而不是26),时间是19(而不是00)。另请注意,时区是BRT(而不是GMT+02:00)。

发生这种情况是因为当DateTimeComparator.compare()使用实例调用时java.util.Date,它使用默认时区来转换对象。

由于我的默认时区 ( java.util.TimeZone.getDefault()) 是“America/Sao_Paulo”(或BRT - 巴西标准时间),因此日期/时间26/09 at 00:00 GMT+02:00将转换为25/09 at 19:00 in BRT.

所以代码实际上是比较leftDate(26/09) 和inDate(25/09),这就是为什么leftLimit是 1。

根据您创建变量和的方式inDate,一天中可能会有变化(例如,代码可能只在一天中的某个时间段工作)。并且在夏令时期间也可能存在差异(当时间向前或向后移动 1 小时时),这也可能导致一天中的变化。leftDaterightDate

而且您的新代码(使用SimpleDateFormat)也对我不起作用(它有同样的问题,SimpleDateFormat也使用默认时区) - 如果我使用而不是使用创建Date对象,则会出现同样的错误。java.util.CalendarDateTime.toDate()

所以,为了解决这个问题,我使用org.joda.time.LocalDate了类(没有时间字段的日期),因为我只需要比较日期(日/月/年)并忽略时间(小时/分钟/秒)。我使用了接收时区的构造函数,所以我不依赖系统的默认值,我可以确定我总是在同一个时区上工作。

protected boolean checkDateInRange(Date inDate, Date inLeft, Date inRight) {
    // convert inputs to LocalDate, using the specified timezone
    LocalDate left = new LocalDate(inLeft, DateTimeZone.forOffsetHours(2));
    LocalDate right = new LocalDate(inRight, DateTimeZone.forOffsetHours(2));
    LocalDate date = new LocalDate(inDate, DateTimeZone.forOffsetHours(2));
    System.out.println("inDate = " + date);
    System.out.println("inLeft = " + left);
    System.out.println("inRight = " + right);

    int leftLimit = left.compareTo(date);
    int rightLimit = date.compareTo(right);
    System.out.println("leftLimit = " + leftLimit);
    System.out.println("rightLimit = " + rightLimit);
    if (leftLimit > 0 || rightLimit > 0) {
        return false;
    }

    return true;
}

现在打电话:

checkDateInRange(in.toDate(), left.toDate(), right.toDate());

产生输出:

inDate = 2016-10-26
inLeft = 2016-10-26
inRight = 2016-10-26
leftLimit = 0
rightLimit = 0
true

笔记:

  • 当您使用 joda-time 时,您可以创建一个接收DateTime对象(甚至更好的是 a LocalDate)的方法,而不是使用旧的容易出错的java.util.DateAPI。更改为 aLocalDate更好,因为它明确表明该方法不需要时间字段:

    protected boolean checkDateInRange(LocalDate inDate, LocalDate inLeft, LocalDate inRight) {
        System.out.println("inDate = " + inDate);
        System.out.println("inLeft = " + inLeft);
        System.out.println("inRight = " + inRight);
    
        int leftLimit = inLeft.compareTo(inDate);
        int rightLimit = inDate.compareTo(inRight);
        System.out.println("leftLimit = " + leftLimit);
        System.out.println("rightLimit = " + rightLimit);
        if (leftLimit > 0 || rightLimit > 0) {
            return false;
        }
    
        return true;
    }
    
    // in, left and right are DateTime instances
    checkDateInRange(in.toLocalDate(), left.toLocalDate(), right.toLocalDate());
    
  • 我假设这DateTimeZone.forOffsetHours(2)是您所有输入的时区。如果不是这种情况,我建议在进行任何比较之前相应地处理每种情况并提取LocalDate(使用上述构造函数)。LocalDate(dateObject, DateTimeZone)

于 2017-05-29T17:10:20.543 回答