【问题标题】:Missing something obvious when it comes to comparing dates (Android Date)在比较日期时遗漏了一些明显的东西(Android Date)
【发布时间】:2016-08-18 17:33:29
【问题描述】:

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

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

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

从截图中可以看出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;
}

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

【问题讨论】:

  • 你试过使用 SimpleDateFormatter 吗?
  • 看不到图片中的错误等。
  • 你的意思是SimpleDateFormat?那不是用来在字符串表示和日期之间移动吗?
  • 请在此处复制并粘贴您的代码,而不是发布屏幕截图。

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


【解决方案1】:

试试这个:

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,但由于太多 if else 我切换到 simpledateformat,但 joda time 在 ISO 日期格式方面有很大帮助。

为什么建议的解决方案不起作用: 如果您阅读documentation,它会说 LHS

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

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

【讨论】:

  • 嗨 sumandas,是的,这段代码有效,但是我必须将我的输入 Dates 转换为字符串,然后按照我在编辑问题中发布的方式将这些字符串转换回 Dates,有没有更好的办法?
  • 您可以直接使用字符串比较来代替解析格式化的日期。由于日期以 ISO 样式格式化,因此解析日期和格式化日期字符串的顺序是相同的。
  • 所以当 LHS == LFS 时没有 0?引用文档:'zero if order does not matter, negative value if lhsObj < rhsObj, positive value otherwise.' 我自动期望'order doesn't matter' 等同于equals 但我不是英语所以如果我错了请原谅我
  • 再尝试一件事:存储 getDateInstance() 中的值并检查值,我正在浏览他们的文档,java 类:github.com/JodaOrg/joda-time/blob/master/src/main/java/org/joda/…。让我们知道怎么回事。返回的实例似乎不正确。尝试切换 inDate 和 inLeft 的值并检查会发生什么。
【解决方案2】:

根据 cmets,我假设您使用的是 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)。

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

由于我的默认时区 (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)的方法,而不是使用旧的容易出错的 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))。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-07
    • 1970-01-01
    • 2013-07-12
    • 1970-01-01
    • 2021-04-25
    • 2011-05-14
    相关资源
    最近更新 更多