在比较日期时遗漏了一些明显的东西(Android日期)

时间:2016-08-18 17:33:29

标签: java android jodatime datetime-conversion datetime-comparison

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

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

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

debugging screenshot

正如您从屏幕截图中看到的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;
}

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

2 个答案:

答案 0 :(得分: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时间在ISO日期格式中有很多帮助。

为什么建议的解决方案无法运作: 如果您阅读documentation,则说LHS< RHS返回-ve 否则返回+ ve

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

的日期
LHS = Sep 26 2016 = RHS and returns a 1. // Hope this helps.

答案 1 :(得分: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 = Sun Sep 25 19:00:00 BRT 2016
  inLeft = Mon Sep 26 15:14:13 BRT 2016
  inRight = Sun Sep 25 19:00:00 BRT 2016

请注意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的原因。

根据您创建变量inDateleftDaterightDate的方式,白天可能会有变化(因此代码可能仅在某些部分有效)这一天,例如)。在夏令时期间(当小时向前或向后移动1小时)也可能存在差异,这也可能导致当天的转变。

您的新代码(使用SimpleDateFormat)对我来说也不起作用(它有同样的问题,因为SimpleDateFormat也使用默认时区) - 如果出现同样的错误我使用Date创建了java.util.Calendar个对象,而不是使用DateTime.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对象(甚至更好,LocalDate)的方法,而不是使用旧错误-prone java.util.Date API。更改为LocalDate更好,因为它明确表示该方法不需要时间字段:

    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)