【问题标题】:-1469913 days left when calculating reoccurring events in Android-1469913 天在 Android 中计算重复事件时剩余
【发布时间】:2012-06-12 12:42:36
【问题描述】:

对于重复发生的事件,我想在我的 Android 日历应用程序中显示下一次发生的剩余天数。

示例:

Today: 2012-06-12
Reoccurring event: 19th June
=> 13 days left

为了实现这一点,我将第一个匹配项保存在数据类型为 Calendar 的对象中:

private Calendar cal;
...
cal = new GregorianCalendar();
cal.set(Calendar.YEAR, USER_INPUT_YEAR);
cal.set(Calendar.MONTH, USER_INPUT_MONTH);
...

为了计算剩余天数,我使用了这个函数:

public int getDaysLeft() {
    Date next = this.getNextOccurrence();
    if (next == null) {
        return -1;
    }
    else {
        long differenceInMilliseconds = next.getTime()-System.currentTimeMillis();
        double differenceInDays = (double) differenceInMilliseconds/DateUtils.DAY_IN_MILLIS;
        return (int) Math.ceil(differenceInDays);
    }
}

哪个使用这个功能:

public Date getNextOccurrence() {
    if (this.cal == null) {
        return null;
    }
    else {
        Calendar today = new GregorianCalendar();
        Calendar next = new GregorianCalendar();
        next.setTime(this.cal.getTime());
        next.set(Calendar.YEAR, today.get(Calendar.YEAR));
        if ((today.get(Calendar.MONTH) > this.cal.get(Calendar.MONTH)) || ((today.get(Calendar.MONTH) == this.cal.get(Calendar.MONTH)) && (today.get(Calendar.DAY_OF_MONTH) > this.cal.get(Calendar.DAY_OF_MONTH)))) {
            next.add(Calendar.YEAR, 1);
        }
        return next.getTime();
    }
}

顺便说一句,要获取初始日期,我希望找到一个 YYYY-MM-DD 值并像这样解析它:

(new SimpleDateFormat("yyyy-MM-dd")).parse(INPUT_DATE_STRING)

这在大多数情况下都可以正常工作,但一些用户报告他们将-1469913 等数字视为“剩余天数”。怎么会这样?

我认为日期 (cal) 可能未设置或无效,但它会显示 -1 或类似的东西,因为所有部分都有空检查,对吧?

-1469913 的意思类似于几年前的-4027!由于这是一个重复发生的事件,我认为“剩余天数”信息应始终在 0 到 366 之间。什么可能导致此代码产生这样的数字?这是否意味着getNextOccurrence() 返回的数据是过去 4027 年?我无法解释这种行为。

我希望你能帮助我。提前非常感谢您!

编辑:因为它可能会有所帮助:使用 DateFormat.getDateInstance().format() 时,错误日期的年份始终输出为 1,例如Jan 3, 1。尽管如此,getDaysLeft() 的结果是 4k 年。

编辑 #2: 我发现像 1--22199-1 这样的日期会产生“还剩 4k 年”的输出。尽管如此,它还是被(new SimpleDateFormat("yyyy-MM-dd")).parse() 成功解析。同样,-1-1-1-91- 被正确解析为Jan 1, 2

编辑#3:事实证明,像“0000-01-03”这样简单的日期造成了所有麻烦。当我以毫秒为单位输出时间时,它显示-62167222800000。然后当我将它输出到 GMT 字符串时,它会显示 0001-01-03 - 很奇怪,不是吗?当我将年份设置为1900 时,以毫秒为单位的时间突然变为-122095040400000。为什么?

【问题讨论】:

  • Joda 附议——但这不是很容易调试吗?
  • 为什么是乔达? Calendar 类实际上非常有用且易于处理,不是吗?但这对我来说并不容易调试,因为我没有要测试的输入数据。但它只能是一个无效的日期,因为重复发生的事件总是在未来 366 天或更短的时间内,还是我违背了基本的数学?
  • @MarcoW。因为这样的事情和更多的事情在乔达是微不足道的。我不明白“没有输入数据”来测试;你有足够的钱在 4k 年前得到一些东西,所以这似乎是一个很好的起点。即使你没有那个,我几乎可以肯定,当正确重构时,日期数学是可测试的。
  • 问题是:我只知道输出(4k 年前),但我不知道哪个输入(日期字符串)产生了这个输出。
  • 我怀疑这是否相关,但是:-1469913 实际上是大约 4024 年前(因为一年大约是 365.25 天)。我注意到这是 2*2012。您是否有可能在某处得到否定的年份数字,使用 -2012 而不是 2012? (查看您的代码,我看不出有什么明显的方法可以实现。)

标签: android date calendar gregorian-calendar


【解决方案1】:

在使用日期之前,很难在这些模糊的错误发生在用户身上之前找出它们。在许多情况下,值得您花时间进行一个小单元测试,在机器中抛出几千万个日期,看看是否会弹出任何极端的答案。

还有this 可能值得一读。在您尝试更好的方法之前,您不会意识到 java 日期类有多糟糕。 :)

编辑:如果用户提供了非常高的输入值,那么当您将结果放入 getDaysLeft() 中的整数时可能会出现数字溢出。只要保持它很长。甚至更好:只接受合理的输入值,如果用户输入了 20120 年或类似的东西,则警告用户:)

EDIT2:我上次编辑时错了,.ceil() 防止数字溢出。老实说,我已经不知道这个错误是如何发生的了。

EDIT3: 响应您的第三次编辑:请记住,DateCalendar 使用 Unix time。这意味着用零表示的时间是 1970 年。1970 年之前的所有时间都将用负值表示。

EDIT4: 请记住,java 的日历类很烂。这段代码 sn-p 表明错误实际上是在日历类中:

Calendar next = new GregorianCalendar();
long date1 = -62167222800000L;
long date2 = -62135600400000L;

next.setTimeInMillis(date1);
next.set(Calendar.YEAR, 2012);
System.out.println(next.getTimeInMillis());

next.setTimeInMillis(date2);
next.set(Calendar.YEAR, 2012);
System.out.println(next.getTimeInMillis());

输出:

-125629491600000
1325545200000

然而,要找出导致此问题的确切错误将非常困难。所有这些错误仍然存​​在的原因是因为修复它们可能会破坏世界各地的遗留系统。我的猜测是,这个错误源于无法给出负数。例如,这将给出输出“2013”​​:

Calendar next = new GregorianCalendar();
next.set(Calendar.YEAR, -2012);
System.out.println(next.get(Calendar.YEAR));

我只是建议您不要在输入中允许这样的极端值。确定一个可接受的跨度,如果值超出这些边界,则给出错误消息。如果您想在其他应用程序中处理所有可能的日期,只需使用joda time。你不会后悔的:)

【讨论】:

  • 谢谢 :) 拥有数千万个日期的测试单元总的来说是个好主意。但是由于DateFormat.parse() 只能产生null 或有效的Date 对象,导致此错误的尴尬输入只能是一个日期字符串,它被错误地解释为4k 年前的日期,对吧?
  • 如果日期是过去,似乎 getNextOccurrence() 应该返回 null,因此仅此一项不足以解释大的负值。如果 cal 设置为 Integer.MAX_VALUE 的年份,这也可以防止发生数字溢出。
  • 对不起!!!这个 if 语句不在原始代码中,我只是在尝试调试应用程序时添加并忘记再次删除它。所以在产生负数的代码中,这不存在。所以getNextOccurrence()的结果早于当前时间肯定是原因。
  • 不,抱歉,对我来说这个问题还没有解决;)请参阅我的最新编辑。
  • 根据您的第三次编辑... :) 当然,我知道 UNIX 时间是相对于 1970 年的。但是如果您仔细查看这些数字,您会发现1900 比 0000 的值更接近负无穷大。这不可能!
【解决方案2】:

您在天数内得到负值,这可能是因为用户输入了下一次发生的日期,即任何以前的日期。

我认为你应该这样计算你的 daysLeft,

            String inputDateString = "19/06/2012";
            Calendar calCurr = Calendar.getInstance();//current date
            Calendar calNext = Calendar.getInstance();// for next date
            calNext.setTime(new Date(inputDateString)); // or do set Day,Month,Year like in your Question

            if(calNext.after(calCurr)) // if the next date is after current
            {
                long timeDiff = calNext.getTimeInMillis() - calCurr.getTimeInMillis(); // time of next year if today is 15 june and some one enter 16 june for next occurance
                int daysLeft = (int) (timeDiff/DateUtils.DAY_IN_MILLIS); // Days Left
            }
            else
            {
                long timeDiff = calCurr.getTimeInMillis() - calNext.getTimeInMillis();
                timeDiff = DateUtils.YEAR_IN_MILLIS - timeDiff; // time of next year if today is 15 june and some one enter 14 june for next occurance
                int daysLeft = (int) (timeDiff/DateUtils.DAY_IN_MILLIS); // Days Left
            }

【讨论】:

  • 谢谢!但是,如果您查看我对该问题的最新编辑,您会发现这似乎不是一个解决方案。
  • @MarcoW。根据我的说法,你得到的 daysleft 的负值只是因为计算错误,所以我 100% 确定这个问题只出在代码的那部分,你真的试过我的答案吗?或者只是忽略了它,因为它只进行计算..无论如何都会尝试更多地分析您的代码/问题,如果我得到,会发布任何内容。
  • 嗯,你看到我对这个问题的修改了吗?如您所见,Calendar 类似乎把事情搞砸了并计算了错误的时间戳。否则,getNextOccurrence() 不能早于今天。
猜你喜欢
  • 2013-06-26
  • 2014-07-30
  • 2014-06-11
  • 2021-06-11
  • 2021-02-18
  • 2010-11-13
  • 2014-03-19
  • 1970-01-01
相关资源
最近更新 更多